예제 #1
0
 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.");
     }
 }
예제 #2
0
        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);
        }
예제 #3
0
 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);
            }
        }
예제 #5
0
        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);
        }
예제 #6
0
        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);
            }
        }