private static ApplicationType GetApplicationType(string applicationPath) { using (var archive = new ZipBundle(applicationPath)) { if (archive.Entries.Any(x => x.FullName == "BundleConfig.pb")) { return(ApplicationType.Aab); } if (archive.Entries.Any(x => x.FullName == "AndroidManifest.xml")) { return(ApplicationType.Apk); } throw new Exception("Couldn't determine Android build type."); } }
public MobileArchInfo[] GetArchitectureInfo(string applicationPath) { MobileArchInfo[] architectures; using (var archive = new ZipBundle(applicationPath)) { var archList = new List <MobileArchInfo>(); foreach (var file in archive.Entries) { if (file.Name != "libunity.so") { continue; } var parent = file.FullName.Replace("/libunity.so", string.Empty); var architecture = parent.Substring(parent.LastIndexOf('/') + 1); archList.Add(new MobileArchInfo(architecture)); } if (archList.Count < 1) { throw new Exception($"Couldn't extract architecture info from application {applicationPath}"); } architectures = archList.ToArray(); } var applicationType = GetApplicationType(applicationPath); switch (applicationType) { case ApplicationType.Aab: GetAabDownloadSizes(applicationPath, ref architectures); break; case ApplicationType.Apk: var downloadSize = GetApkDownloadSize(applicationPath); foreach (var archInfo in architectures) { archInfo.DownloadSize = downloadSize; } break; default: throw new Exception("Unknown application type to collect architecture data from."); } return(architectures); }
private static bool IsBuildValid(string buildPath) { try { using (var archive = new ZipBundle(buildPath)) { return(archive.Entries.Any(x => x.FullName == "AndroidManifest.xml" || x.FullName == "BundleConfig.pb" || x.Name == "Info.plist" && x.FullName.StartsWith("Payload"))); } } catch { return(false); } }
internal static void GenerateAppleAppendix(string applicationPath, string guid) { var temporaryFolder = Utilities.GetTemporaryFolder(); try { MobileHelper.RegisterPlatformUtilities(new AppleUtilities()); using (var archive = new ZipBundle(applicationPath)) { var guidFile = archive.Entries.FirstOrDefault(x => x.Name == "UnityBuildGuid.txt"); if (guidFile == null) { Debug.LogError("The signature of the opened build report doesn't match the provided application."); return; } var guidUnzipped = Path.Combine(temporaryFolder, "UnityBuildGuid.txt"); AppleUtilities.UnzipFile(applicationPath, guidFile.FullName, guidUnzipped); using (var reader = new StreamReader(guidUnzipped)) { var applicationGuid = reader.ReadToEnd(); if (applicationGuid != guid) { Debug.LogError($"The GUID of the selected report does not match the GUID of the provided application.\nExpected: {guid} but got: {applicationGuid}."); return; } } } GenerateMobileAppendix(applicationPath, guid); } catch { Debug.LogError("Could not open the application archive. Please provide a valid .ipa bundle."); } finally { Directory.Delete(temporaryFolder, true); } }
internal MobileAppendix(string applicationPath) { if (!IsBuildValid(applicationPath)) { Debug.LogError("Couldn't collect report data from application bundle: build invalid."); return; } // Get the actual size of the app bundle on disk BuildSize = new FileInfo(applicationPath).Length; // Get the list of files inside of the app bundle from the zip header using (var archive = new ZipBundle(applicationPath)) { var files = new List <MobileFile>(); foreach (var entry in archive.Entries) { // Skip iOS/tvOS directory meta files if (entry.CompressedSize == 0) { continue; } files.Add(new MobileFile( entry.FullName, entry.CompressedSize, entry.UncompressedSize)); } Files = files.ToArray(); } if (MobileHelper.s_PlatformUtilities == null) { return; } // Extract the data about the different architectures comprising the build Architectures = MobileHelper.s_PlatformUtilities.GetArchitectureInfo(applicationPath); }
public MobileArchInfo[] GetArchitectureInfo(string applicationPath) { var temporaryFolder = Utilities.GetTemporaryFolder(); try { var frameworkFile = Path.Combine(temporaryFolder, "UnityFramework"); long appSizeNoFramework; using (var archive = new ZipBundle(applicationPath)) { var unityFramework = archive.Entries.FirstOrDefault(x => x.FullName.EndsWith(k_UnityFrameworkRelativePath, StringComparison.InvariantCulture)); if (unityFramework == null) { throw new Exception("Failed to locate UnityFramework file in the build."); } UnzipFile(applicationPath, unityFramework.FullName, frameworkFile); appSizeNoFramework = new FileInfo(applicationPath).Length - unityFramework.CompressedSize; } var foundArchitectures = new List <string>(); var archOutput = Utilities.RunProcessAndGetOutput(k_File, $"-b {frameworkFile}", out var archExitCode); if (archExitCode != 0) { throw new Exception($"Failed to collect UnityFramework data with command: {k_Size} -m {frameworkFile}. Output:\n{archOutput}"); } using (var reader = new StringReader(archOutput)) { string line; while ((line = reader.ReadLine()) != null) { var archString = line.Substring(line.LastIndexOf(' ') + 1); if (archString.StartsWith("arm", StringComparison.InvariantCulture)) { foundArchitectures.Add(archString.Replace("_", string.Empty)); } } } var appleArchInfos = new List <MobileArchInfo>(); foreach (var arch in foundArchitectures) { var sizeArgs = $"-m -arch {arch} {frameworkFile}"; var sizeOutput = Utilities.RunProcessAndGetOutput(k_Size, sizeArgs, out var sizeExitCode); if (sizeExitCode != 0) { throw new Exception($"Failed to collect UnityFramework data with command: {k_Size} {sizeArgs}. Output:\n{sizeOutput}"); } var segments = new MobileArchInfo.ExecutableSegments(); using (var reader = new StringReader(sizeOutput)) { string line; while ((line = reader.ReadLine()) != null) { if (!line.StartsWith("Segment __", StringComparison.InvariantCulture)) { continue; } var segmentSize = long.Parse(line.Substring(line.LastIndexOf(' ') + 1)); if (line.Contains("__TEXT")) { segments.TextSize = segmentSize; } else if (line.Contains("__DATA")) { segments.DataSize = segmentSize; } else if (line.Contains("__LLVM")) { segments.LlvmSize = segmentSize; } else if (line.Contains("__LINKEDIT")) { segments.LinkeditSize = segmentSize; } } } // Calculate the estimated App Store download size with the formula: // DownloadSize = Whole App - Framework Size + Text Segment + (Data Segment / 5) var downloadSize = appSizeNoFramework + segments.TextSize + segments.DataSize / 5; appleArchInfos.Add(new MobileArchInfo(arch) { DownloadSize = downloadSize, Segments = segments }); } if (appleArchInfos.Count < 1) { throw new Exception($"Couldn't extract architecture info from application {applicationPath}"); } return(appleArchInfos.ToArray()); } finally { Directory.Delete(temporaryFolder, true); } }