Пример #1
0
        public static string ToNativeArchitecture(this TargetArchitecture self)
        {
            switch (self)
            {
            case TargetArchitecture.i386:
            case TargetArchitecture.x86_64:
                return(self.ToString());

            case TargetArchitecture.ARMv6:
                return("armv6");

            case TargetArchitecture.ARMv7:
                return("armv7");

            case TargetArchitecture.ARMv7s:
                return("armv7s");

            case TargetArchitecture.ARMv7k:
                return("armv7k");

            case TargetArchitecture.ARM64:
                return("arm64");

            case TargetArchitecture.ARM64_32:
                return("arm64_32");

            default:
                throw new ArgumentOutOfRangeException(nameof(self), $"The value '{self}' does not represent a single architecture.");
            }
        }
Пример #2
0
        private void BuildMsvc(BuildOptions options, TargetPlatform platform, TargetArchitecture architecture)
        {
            var    configuration = "Release";
            string buildPlatform;

            switch (architecture)
            {
            case TargetArchitecture.x86:
                buildPlatform = "Win32";
                break;

            default:
                buildPlatform = architecture.ToString();
                break;
            }

            // Build mono
            Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "libmono-static.vcxproj"), configuration, buildPlatform);
            Deploy.VCEnvironment.BuildSolution(Path.Combine(root, "msvc", "monoposixhelper.vcxproj"), configuration, buildPlatform);

            // Deploy binaries
            var binaries = new[]
            {
                Path.Combine("lib", configuration, "libmono-static.lib"),
                Path.Combine("bin", configuration, "MonoPosixHelper.dll"),
            };
            var srcBinaries = Path.Combine(root, "msvc", "build", "sgen", buildPlatform);
            var depsFolder  = GetThirdPartyFolder(options, platform, architecture);

            Log.Verbose("Copy mono binaries from " + srcBinaries);
            foreach (var binary in binaries)
            {
                var src = Path.Combine(srcBinaries, binary);
                var dst = Path.Combine(depsFolder, Path.GetFileName(src));
                Utilities.FileCopy(src, dst);
            }

            // Deploy debug symbols
            var debugSymbolsLibs = new[]
            {
                "libmonoruntime",
                "libmonoutils",
                "libgcmonosgen",
                "libmini",
                "eglib",
            };

            foreach (var debugSymbol in debugSymbolsLibs)
            {
                var src = Path.Combine(srcBinaries, "obj", debugSymbol, configuration, debugSymbol + ".pdb");
                var dst = Path.Combine(depsFolder, Path.GetFileName(src));
                Utilities.FileCopy(src, dst);
            }
        }
Пример #3
0
        public static string GetFriendlyModuleArchitecture(this TargetArchitecture targetArchitecture)
        {
            switch (targetArchitecture)
            {
            case TargetArchitecture.I386:
                return("x86");

            case TargetArchitecture.AMD64:
                return("x64");

            case TargetArchitecture.IA64:
                return("Itanium");

            case TargetArchitecture.AnyCPU:
                return("Any CPU");

            default: return(targetArchitecture.ToString());
            }
        }
Пример #4
0
        public static void Write(string perfMapFileName, int perfMapFormatVersion, IEnumerable <MethodInfo> methods, IEnumerable <AssemblyInfo> inputAssemblies, TargetOS targetOS, TargetArchitecture targetArch)
        {
            if (perfMapFormatVersion > CurrentFormatVersion)
            {
                throw new NotSupportedException(perfMapFormatVersion.ToString());
            }

            using (TextWriter writer = new StreamWriter(perfMapFileName))
            {
                IEnumerable <AssemblyInfo> orderedInputs = inputAssemblies.OrderBy(asm => asm.Name, StringComparer.OrdinalIgnoreCase);

                PerfMapWriter perfMapWriter = new PerfMapWriter(writer);

                List <byte> inputHash = new List <byte>();
                foreach (AssemblyInfo inputAssembly in orderedInputs)
                {
                    inputHash.AddRange(inputAssembly.Mvid.ToByteArray());
                }
                inputHash.Add((byte)targetOS);
                inputHash.Add((byte)targetArch);
                Guid outputGuid = new Guid(MD5.HashData(inputHash.ToArray()));
                perfMapWriter.WriteLine(outputGuid.ToString(), (uint)PseudoRVA.OutputGuid, 0);
                perfMapWriter.WriteLine(targetOS.ToString(), (uint)PseudoRVA.TargetOS, 0);
                perfMapWriter.WriteLine(targetArch.ToString(), (uint)PseudoRVA.TargetArchitecture, 0);
                perfMapWriter.WriteLine(CurrentFormatVersion.ToString(), (uint)PseudoRVA.FormatVersion, 0);

                foreach (MethodInfo methodInfo in methods)
                {
                    if (methodInfo.HotRVA != 0 && methodInfo.HotLength != 0)
                    {
                        perfMapWriter.WriteLine(methodInfo.Name, methodInfo.HotRVA, methodInfo.HotLength);
                    }
                    if (methodInfo.ColdRVA != 0 && methodInfo.ColdLength != 0)
                    {
                        perfMapWriter.WriteLine(methodInfo.Name, methodInfo.ColdRVA, methodInfo.ColdLength);
                    }
                }
            }
        }
Пример #5
0
        /// <summary>
        /// Gets the build options for the given target and the configuration.
        /// </summary>
        /// <param name="target">The target.</param>
        /// <param name="platform">The platform.</param>
        /// <param name="toolchain">The toolchain.</param>
        /// <param name="architecture">The build architecture.</param>
        /// <param name="configuration">The build configuration.</param>
        /// <param name="workingDirectory">The build workspace root folder path.</param>
        /// <param name="hotReloadPostfix">The output binaries postfix added for hot-reload builds in Editor to prevent file names collisions.</param>
        /// <returns>The build options.</returns>
        public static BuildOptions GetBuildOptions(Target target, Platform platform, Toolchain toolchain, TargetArchitecture architecture, TargetConfiguration configuration, string workingDirectory, string hotReloadPostfix = "")
        {
            var platformName      = platform.Target.ToString();
            var architectureName  = architecture.ToString();
            var configurationName = configuration.ToString();
            var options           = new BuildOptions
            {
                Target             = target,
                Platform           = platform,
                Toolchain          = toolchain,
                Architecture       = architecture,
                Configuration      = configuration,
                CompileEnv         = new CompileEnvironment(),
                LinkEnv            = new LinkEnvironment(),
                IntermediateFolder = Path.Combine(workingDirectory, Configuration.IntermediateFolder, target.Name, platformName, architectureName, configurationName),
                OutputFolder       = Path.Combine(workingDirectory, Configuration.BinariesFolder, target.Name, platformName, architectureName, configurationName),
                WorkingDirectory   = workingDirectory,
                HotReloadPostfix   = hotReloadPostfix,
            };

            toolchain?.SetupEnvironment(options);
            target.SetupTargetEnvironment(options);
            return(options);
        }
Пример #6
0
 /// <summary>
 /// Gets the dependency third-party packages binaries folder.
 /// </summary>
 /// <param name="options">The options.</param>
 /// <param name="platform">The target platform.</param>
 /// <param name="architecture">The target architecture.</param>
 /// <returns>The absolute path to the deps folder for the given platform and architecture configuration.</returns>
 public static string GetThirdPartyFolder(BuildOptions options, TargetPlatform platform, TargetArchitecture architecture)
 {
     return(Path.Combine(options.PlatformsFolder, platform.ToString(), "Binaries", "ThirdParty", architecture.ToString()));
 }
Пример #7
0
        private void Build(BuildOptions options, string preset, TargetPlatform targetPlatform, TargetArchitecture architecture)
        {
            // Load preset configuration
            var presetPath = Path.Combine(projectGenDir, "buildtools", "presets", "public", preset + ".xml");

            if (!File.Exists(presetPath))
            {
                throw new Exception(string.Format("Missing PhysX preset {0} (file: {1})", preset, presetPath));
            }
            var presetXml = new XmlDocument();

            presetXml.Load(presetPath);

            // Configure preset
            var cmakeSwitches = presetXml["preset"]["CMakeSwitches"];

            ConfigureCmakeSwitch(cmakeSwitches, "PX_BUILDSNIPPETS", "False");
            ConfigureCmakeSwitch(cmakeSwitches, "PX_BUILDSAMPLES", "False");
            ConfigureCmakeSwitch(cmakeSwitches, "PX_BUILDPUBLICSAMPLES", "False");
            ConfigureCmakeSwitch(cmakeSwitches, "PX_GENERATE_STATIC_LIBRARIES", "True");
            ConfigureCmakeSwitch(cmakeSwitches, "NV_USE_STATIC_WINCRT", "False");
            ConfigureCmakeSwitch(cmakeSwitches, "NV_USE_DEBUG_WINCRT", "False");
            ConfigureCmakeSwitch(cmakeSwitches, "PX_FLOAT_POINT_PRECISE_MATH", "False");
            var cmakeParams = presetXml["preset"]["CMakeParams"];

            switch (targetPlatform)
            {
            case TargetPlatform.Android:
                ConfigureCmakeSwitch(cmakeParams, "CMAKE_INSTALL_PREFIX", $"install/android-{Configuration.AndroidPlatformApi}/PhysX");
                ConfigureCmakeSwitch(cmakeParams, "ANDROID_NATIVE_API_LEVEL", $"android-{Configuration.AndroidPlatformApi}");
                ConfigureCmakeSwitch(cmakeParams, "ANDROID_ABI", AndroidToolchain.GetAbiName(architecture));
                break;
            }

            // Save preset
            presetXml.Save(presetPath);

            // Peek options
            var    platform      = Platform.GetPlatform(targetPlatform);
            var    configuration = "release";
            string bits;
            string arch;
            string binariesSubDir;
            string buildPlatform;
            bool   suppressBitsPostfix = false;
            string binariesPrefix      = string.Empty;

            switch (architecture)
            {
            case TargetArchitecture.x86:
                arch = "x86";
                bits = "32";
                break;

            case TargetArchitecture.x64:
                arch = "x86";
                bits = "64";
                break;

            case TargetArchitecture.ARM:
                arch = "arm";
                bits = "32";
                break;

            case TargetArchitecture.ARM64:
                arch = "arm";
                bits = "64";
                break;

            default: throw new InvalidArchitectureException(architecture);
            }

            switch (architecture)
            {
            case TargetArchitecture.x86:
                buildPlatform = "Win32";
                break;

            default:
                buildPlatform = architecture.ToString();
                break;
            }

            switch (targetPlatform)
            {
            case TargetPlatform.Windows:
                binariesSubDir = string.Format("win.{0}_{1}.vc140.md", arch, bits);
                break;

            case TargetPlatform.UWP:
                binariesSubDir = string.Format("uwp.{0}_{1}.vc141", arch, bits);
                break;

            case TargetPlatform.Linux:
                binariesSubDir = "linux.clang";
                binariesPrefix = "lib";
                break;

            case TargetPlatform.PS4:
                binariesSubDir      = "ps4";
                buildPlatform       = "ORBIS";
                suppressBitsPostfix = true;
                binariesPrefix      = "lib";
                break;

            case TargetPlatform.XboxOne:
            case TargetPlatform.XboxScarlett:
                binariesSubDir = "win.x86_64.vc142.md";
                break;

            case TargetPlatform.Android:
                switch (architecture)
                {
                case TargetArchitecture.ARM64:
                    binariesSubDir = "android.arm64-v8a.fp-soft";
                    break;

                default: throw new InvalidArchitectureException(architecture);
                }
                binariesPrefix      = "lib";
                suppressBitsPostfix = true;
                break;

            case TargetPlatform.Switch:
                binariesSubDir      = "switch64";
                buildPlatform       = "NX64";
                suppressBitsPostfix = true;
                binariesPrefix      = "lib";
                break;

            default: throw new InvalidPlatformException(targetPlatform);
            }

            // Setup build environment variables for PhysX build system
            var envVars = new Dictionary <string, string>();

            switch (BuildPlatform)
            {
            case TargetPlatform.Windows:
            {
                var msBuild = VCEnvironment.MSBuildPath;
                if (File.Exists(msBuild))
                {
                    envVars.Add("PATH", Path.GetDirectoryName(msBuild));
                }
                break;
            }

            case TargetPlatform.Linux:
            {
                envVars.Add("CC", "clang-7");
                envVars.Add("CC_FOR_BUILD", "clang-7");
                break;
            }

            default: throw new InvalidPlatformException(BuildPlatform);
            }
            if (AndroidNdk.Instance.IsValid)
            {
                envVars.Add("PM_ANDROIDNDK_PATH", AndroidNdk.Instance.RootPath);
            }

            // Print the PhysX version
            Log.Info("Building PhysX version " + File.ReadAllText(Path.Combine(root, "physx", "version.txt")) + " to " + binariesSubDir);

            // Generate project files
            Utilities.Run(projectGenPath, preset, null, projectGenDir, Utilities.RunOptions.Default, envVars);

            switch (targetPlatform)
            {
            case TargetPlatform.PS4:
                // Hack: PS4 uses .o extension for compiler output files but CMake uses .obj even if CMAKE_CXX_OUTPUT_EXTENSION/CMAKE_C_OUTPUT_EXTENSION are specified
                Utilities.ReplaceInFiles(Path.Combine(root, "physx\\compiler\\ps4"), "*.vcxproj", SearchOption.AllDirectories, ".obj", ".o");
                break;

            case TargetPlatform.XboxOne:
            case TargetPlatform.XboxScarlett:
                // Hack: force to use proper Win10 SDK
                Utilities.ReplaceInFiles(Path.Combine(root, "physx\\compiler\\vc16win64"), "*.vcxproj", SearchOption.AllDirectories, "10.0.18362.0", "10.0.19041.0");

                // Hack: fix STL include
                Utilities.ReplaceInFile(Path.Combine(root, "physx\\source\\foundation\\include\\PsAllocator.h"), "#include <typeinfo.h>", "#include <typeinfo>");
                break;

            case TargetPlatform.Android:
                // Hack: fix compilation errors
                if (!File.ReadAllText(Path.Combine(root, "physx\\source\\foundation\\include\\PsUtilities.h")).Contains("#if PX_GCC_FAMILY && !PX_EMSCRIPTEN  && !PX_LINUX && !PX_ANDROID"))
                {
                    Utilities.ReplaceInFile(Path.Combine(root, "physx\\source\\foundation\\include\\PsUtilities.h"), "#if PX_GCC_FAMILY && !PX_EMSCRIPTEN  && !PX_LINUX", "#if PX_GCC_FAMILY && !PX_EMSCRIPTEN  && !PX_LINUX && !PX_ANDROID");
                }
                Utilities.ReplaceInFile(Path.Combine(root, "physx\\source\\compiler\\cmake\\android\\CMakeLists.txt"), "-Wno-maybe-uninitialized", "-Wno-unused-local-typedef -Wno-unused-private-field");

                // PhysX build system for Android is old and doesn't support new NDK with clang so invoke cmake manually
                if (!Directory.Exists(Path.Combine(root, "physx\\compiler\\android")))
                {
                    Directory.CreateDirectory(Path.Combine(root, "physx\\compiler\\android"));
                }
                envVars.Add("PHYSX_ROOT_DIR", Path.Combine(root, "physx"));
                envVars.Add("PM_CMAKEMODULES_PATH", Path.Combine(root, "externals/CMakeModules"));
                envVars.Add("PM_PXSHARED_PATH", Path.Combine(root, "pxshared"));
                envVars.Add("PM_TARGA_PATH", Path.Combine(root, "externals/targa"));
                envVars.Add("PM_PATHS", Path.Combine(root, "externals/CMakeModules") + ';' + Path.Combine(root, "externals/targa"));
                RunCmake(Path.Combine(root, "physx\\compiler\\android"), targetPlatform, architecture, string.Format("\"{0}/physx/compiler/public\" -Wno-dev -DANDROID_NATIVE_API_LEVEL=android-{1} -DTARGET_BUILD_PLATFORM=android --no-warn-unused-cli -DCMAKE_BUILD_TYPE={2} -DCMAKE_PREFIX_PATH=\"{0}/externals/CMakeModules;{0}/externals/targa\" -DPHYSX_ROOT_DIR=\"{0}/physx\" -DPX_OUTPUT_LIB_DIR=\"{0}/physx\" -DPX_OUTPUT_BIN_DIR=\"{0}/physx\" -DPX_BUILDSNIPPETS=FALSE -DPX_GENERATE_STATIC_LIBRARIES=TRUE -DCMAKE_INSTALL_PREFIX=\"{0}/physx/install/android-{1}/PhysX\"", root, Configuration.AndroidPlatformApi, configuration), envVars);
                break;
            }

            // Run building based on the platform
            var defaultPhysXLibs = new[]
            {
                "PhysX",
                "PhysXCharacterKinematic",
                "PhysXCommon",
                "PhysXCooking",
                "PhysXExtensions",
                "PhysXFoundation",
                "PhysXPvdSDK",
                "PhysXVehicle",
            };
            var dstBinaries = GetThirdPartyFolder(options, targetPlatform, architecture);
            var srcBinaries = Path.Combine(root, "physx", "bin", binariesSubDir, configuration);

            switch (BuildPlatform)
            {
            case TargetPlatform.Windows:
                switch (targetPlatform)
                {
                case TargetPlatform.Android:
                    Utilities.Run("cmake", "--build .", null, Path.Combine(root, "physx\\compiler\\android"), Utilities.RunOptions.None, envVars);
                    break;

                default:
                    VCEnvironment.BuildSolution(Path.Combine(solutionFilesRoot, preset, "PhysXSDK.sln"), configuration, buildPlatform);
                    break;
                }
                break;

            case TargetPlatform.Linux:
                Utilities.Run("make", null, null, Path.Combine(projectGenDir, "compiler", "linux-" + configuration), Utilities.RunOptions.None);
                break;

            default: throw new InvalidPlatformException(BuildPlatform);
            }

            // Deploy binaries
            var binariesExtension = platform.StaticLibraryFileExtension;

            Log.Verbose("Copy PhysX binaries from " + srcBinaries);
            foreach (var physXLib in defaultPhysXLibs)
            {
                var filename = suppressBitsPostfix ? string.Format("{0}{1}_static", binariesPrefix, physXLib) : string.Format("{0}{1}_static_{2}", binariesPrefix, physXLib, bits);
                filename += binariesExtension;
                Utilities.FileCopy(Path.Combine(srcBinaries, filename), Path.Combine(dstBinaries, filename));

                var filenamePdb = Path.ChangeExtension(filename, "pdb");
                if (File.Exists(Path.Combine(srcBinaries, filenamePdb)))
                {
                    Utilities.FileCopy(Path.Combine(srcBinaries, filenamePdb), Path.Combine(dstBinaries, filenamePdb));
                }
            }
            srcBinaries = Path.Combine(root, "physx", "compiler", preset, "sdk_source_bin", configuration);
            var additionalPhysXLibs = new[]
            {
                "FastXml",
                "LowLevel",
                "LowLevelAABB",
                "LowLevelDynamics",
                "PhysXTask",
                "SceneQuery",
                "SimulationController",
            };

            foreach (var additionalPhysXLib in additionalPhysXLibs)
            {
                var filenamePdb = suppressBitsPostfix ? string.Format("{0}{1}", binariesPrefix, additionalPhysXLib) : string.Format("{0}{1}_{2}", binariesPrefix, additionalPhysXLib, bits);
                filenamePdb += ".pdb";
                if (File.Exists(Path.Combine(srcBinaries, filenamePdb)))
                {
                    Utilities.FileCopy(Path.Combine(srcBinaries, filenamePdb), Path.Combine(dstBinaries, filenamePdb));
                }
            }
        }
Пример #8
0
        public override bool Execute()
        {
            TargetArchitecture architectures, deviceArchitectures, target = TargetArchitecture.Default;
            string             targetOperatingSystem;
            PDictionary        plist, device;
            PString            value, os;

            switch (PlatformFrameworkHelper.GetFramework(TargetFrameworkIdentifier))
            {
            case ApplePlatform.WatchOS:
                targetOperatingSystem = "watchOS";
                break;

            case ApplePlatform.TVOS:
                targetOperatingSystem = "tvOS";
                break;

            default:
                targetOperatingSystem = "iOS";
                break;
            }

            if (!Enum.TryParse(Architectures, out architectures))
            {
                Log.LogError("Invalid architectures: '{0}'.", Architectures);
                return(false);
            }

            if ((plist = PObject.FromString(TargetiOSDevice) as PDictionary) == null)
            {
                Log.LogError("Failed to parse the target device information.");
                return(false);
            }

            if (!plist.TryGetValue("device", out device))
            {
                Log.LogError("No target device found.");
                return(false);
            }

            if (!device.TryGetValue("architecture", out value))
            {
                Log.LogError("No device architecture information found.");
                return(false);
            }

            if (!Enum.TryParse(value.Value, out deviceArchitectures) || deviceArchitectures == TargetArchitecture.Default)
            {
                Log.LogError("Invalid target architecture: '{0}'", value.Value);
                return(false);
            }

            if (!device.TryGetValue("os", out os))
            {
                Log.LogError("No device operating system information found.");
                return(false);
            }

            if (os.Value != targetOperatingSystem || (architectures & deviceArchitectures) == 0)
            {
                // the TargetiOSDevice property conflicts with the build configuration (*.user file?), do not build this project for a specific device
                DeviceSpecificIntermediateOutputPath = IntermediateOutputPath;
                DeviceSpecificOutputPath             = OutputPath;
                TargetArchitectures   = Architectures;
                TargetDeviceOSVersion = string.Empty;
                TargetDeviceModel     = string.Empty;

                return(!Log.HasLoggedErrors);
            }

            for (int bit = 0; bit < 32; bit++)
            {
                var architecture = (TargetArchitecture)(1 << bit);

                if ((architectures & architecture) == 0)
                {
                    continue;
                }

                if ((deviceArchitectures & architecture) != 0)
                {
                    target = architecture;
                }
            }

            TargetArchitectures = target.ToString();

            if (!device.TryGetValue("model", out value))
            {
                Log.LogError("No device model information found.");
                return(false);
            }

            TargetDeviceModel = value.Value;

            if (!device.TryGetValue("os-version", out value))
            {
                Log.LogError("No iOS version information found.");
                return(false);
            }

            TargetDeviceOSVersion = value.Value;

            // Note: we replace ',' with '.' because the ',' breaks the Mono AOT compiler which tries to treat arguments with ','s in them as options.
            var dirName = TargetDeviceModel.ToLowerInvariant().Replace(",", ".") + "-" + TargetDeviceOSVersion;

            DeviceSpecificIntermediateOutputPath = Path.Combine(IntermediateOutputPath, "device-builds", dirName) + "/";
            DeviceSpecificOutputPath             = Path.Combine(OutputPath, "device-builds", dirName) + "/";

            return(!Log.HasLoggedErrors);
        }
        public override bool Execute()
        {
            TargetArchitecture architectures, deviceArchitectures, target = TargetArchitecture.Default;
            PDictionary        plist, device;
            PString            value;

            Log.LogTaskName("ParseDeviceSpecificBuildInformation");
            Log.LogTaskProperty("Architectures", Architectures);
            Log.LogTaskProperty("IntermediateOutputPath", IntermediateOutputPath);
            Log.LogTaskProperty("OutputPath", OutputPath);
            Log.LogTaskProperty("TargetiOSDevice", TargetiOSDevice);

            if (!Enum.TryParse(Architectures, out architectures))
            {
                Log.LogError("Invalid architectures: '{0}'.", Architectures);
                return(false);
            }

            if ((plist = PObject.FromString(TargetiOSDevice) as PDictionary) == null)
            {
                Log.LogError("Failed to parse the target iOS device information.");
                return(false);
            }

            if (!plist.TryGetValue("device", out device))
            {
                Log.LogError("No target device found.");
                return(false);
            }

            if (!device.TryGetValue("architecture", out value))
            {
                Log.LogError("No device architecture information found.");
                return(false);
            }

            if (!Enum.TryParse(value.Value, out deviceArchitectures) || deviceArchitectures == TargetArchitecture.Default)
            {
                Log.LogError("Invalid target architecture: '{0}'", value.Value);
                return(false);
            }

            if ((architectures & deviceArchitectures) == 0)
            {
                Log.LogError("The target iOS device architecture {0} is not supported by the build configuration: {1}", architectures, deviceArchitectures);
                return(false);
            }

            for (int bit = 0; bit < 32; bit++)
            {
                var architecture = (TargetArchitecture)(1 << bit);

                if ((architectures & architecture) == 0)
                {
                    continue;
                }

                if ((deviceArchitectures & architecture) != 0)
                {
                    target = architecture;
                }
            }

            TargetArchitectures = target.ToString();

            if (!device.TryGetValue("model", out value))
            {
                Log.LogError("No device model information found.");
                return(false);
            }

            TargetDeviceModel = value.Value;

            if (!device.TryGetValue("os-version", out value))
            {
                Log.LogError("No iOS version information found.");
                return(false);
            }

            TargetDeviceOSVersion = value.Value;

            // Note: we replace ',' with '.' because the ',' breaks the Mono AOT compiler which tries to treat arguments with ','s in them as options.
            var dirName = "build-" + TargetDeviceModel.ToLowerInvariant().Replace(",", ".") + "-" + TargetDeviceOSVersion;

            DeviceSpecificIntermediateOutputPath = Path.Combine(IntermediateOutputPath, dirName) + "/";
            DeviceSpecificOutputPath             = Path.Combine(OutputPath, dirName) + "/";

            return(!Log.HasLoggedErrors);
        }