// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using EpicGames.Core; using Microsoft.Extensions.Logging; using UnrealBuildBase; namespace UnrealBuildTool { /// /// Base class for platform-specific project generators /// class AndroidProjectGenerator : PlatformProjectGenerator { /// /// Whether Android Game Development Extension is installed in the system. See https://developer.android.com/games/agde for more details. /// May be disabled by using -noagde on commandline /// private bool AGDEInstalled = false; public AndroidProjectGenerator(CommandLineArguments Arguments, ILogger Logger) : base(Arguments, Logger) { AGDEInstalled = false; if (OperatingSystem.IsWindows() && !Arguments.HasOption("-noagde")) { AGDEInstalled = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Google\AndroidGameDevelopmentExtension")?.ValueCount > 0; if (!AGDEInstalled) { try { string? programFiles86 = Environment.GetEnvironmentVariable("ProgramFiles(x86)"); if (programFiles86 != null) { string vswhereExe = Path.Join(programFiles86, @"Microsoft Visual Studio\Installer\vswhere.exe"); if (File.Exists(vswhereExe)) { using (Process p = new Process()) { ProcessStartInfo info = new ProcessStartInfo { FileName = vswhereExe, Arguments = @"-find Common7\IDE\Extensions\*\Google.VisualStudio.Android.dll", RedirectStandardOutput = true, UseShellExecute = false }; p.StartInfo = info; p.Start(); AGDEInstalled = p.StandardOutput.ReadToEnd().Contains("Google.VisualStudio.Android.dll"); } } } } catch (Exception ex) { Logger.LogInformation("Failed to identify AGDE installation status: {Message}", ex.Message); } } } } /// /// Enumerate all the platforms that this generator supports /// public override IEnumerable GetPlatforms() { yield return UnrealTargetPlatform.Android; } /// /// Whether this build platform has native support for VisualStudio /// /// The UnrealTargetPlatform being built /// The UnrealTargetConfiguration being built /// /// bool true if native VisualStudio support (or custom VSI) is available public override bool HasVisualStudioSupport(UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration, VCProjectFileFormat ProjectFileFormat) { // Debugging, etc. are dependent on the TADP being installed return AGDEInstalled; } /// /// Return the VisualStudio platform name for this build platform /// /// The UnrealTargetPlatform being built /// The UnrealTargetConfiguration being built /// string The name of the platform that VisualStudio recognizes public override string GetVisualStudioPlatformName(UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration) { string PlatformName = InPlatform.ToString(); if (InPlatform == UnrealTargetPlatform.Android && AGDEInstalled) { PlatformName = "Android-arm64-v8a"; } return PlatformName; } /// /// Return any custom property group lines /// /// The UnrealTargetPlatform being built /// /// String builder for the project file public override void GetAdditionalVisualStudioPropertyGroups(UnrealTargetPlatform InPlatform, VCProjectFileFormat ProjectFileFormat, StringBuilder ProjectFileBuilder) { if (AGDEInstalled) { base.GetAdditionalVisualStudioPropertyGroups(InPlatform, ProjectFileFormat, ProjectFileBuilder); } } /// /// Return any custom paths for VisualStudio this platform requires /// This include ReferencePath, LibraryPath, LibraryWPath, IncludePath and ExecutablePath. /// /// The UnrealTargetPlatform being built /// The configuration being built /// The type of target (game or program) /// Path to the target.cs file /// Path to the project file /// /// Format for the generated project files /// String builder for the project file /// The custom path lines for the project file; Empty string if it doesn't require one public override void GetVisualStudioPathsEntries(UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration, TargetType TargetType, FileReference TargetRulesPath, FileReference ProjectFilePath, FileReference NMakeOutputPath, VCProjectFileFormat InProjectFileFormat, StringBuilder ProjectFileBuilder) { if (AGDEInstalled) { string apkLocation = Path.Combine( Path.GetDirectoryName(NMakeOutputPath.FullName)!, Path.GetFileNameWithoutExtension(NMakeOutputPath.FullName) + "-arm64.apk"); ProjectFileBuilder.AppendLine($" {apkLocation}"); string intermediatePath = Path.GetFullPath(Path.GetDirectoryName(NMakeOutputPath.FullName) + @"\..\..\Intermediate\Android\arm64\"); string symbolLocations = $@"{intermediatePath}jni\arm64-v8a;{intermediatePath}libs\arm64-v8a"; ProjectFileBuilder.AppendLine($" {symbolLocations}"); } else { base.GetVisualStudioPathsEntries(InPlatform, InConfiguration, TargetType, TargetRulesPath, ProjectFilePath, NMakeOutputPath, InProjectFileFormat, ProjectFileBuilder); } } public override string GetExtraBuildArguments(UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration) { // do not need to check InPlatform since it will always be UnrealTargetPlatform.Android return (AGDEInstalled ? " -Architectures=arm64 -ForceAPKGeneration" : "") + base.GetExtraBuildArguments(InPlatform, InConfiguration); } public override string GetVisualStudioUserFileStrings(UnrealTargetPlatform InPlatform, UnrealTargetConfiguration InConfiguration, string InConditionString, TargetRules InTargetRules, FileReference TargetRulesPath, FileReference ProjectFilePath) { if (AGDEInstalled && (InPlatform == UnrealTargetPlatform.Android) && ((InTargetRules.Type == TargetRules.TargetType.Client) || (InTargetRules.Type == TargetRules.TargetType.Game))) { string UserFileEntry = "\n"; UserFileEntry += " " + "command script import \"" + Path.Combine(Unreal.EngineDirectory.FullName, "Extras", "LLDBDataFormatters", "UEDataFormatters_2ByteChars.py") + "\";" + "$(AndroidLldbStartupCommands)" + "\n"; UserFileEntry += "\n"; return UserFileEntry; } return base.GetVisualStudioUserFileStrings(InPlatform, InConfiguration, InConditionString, InTargetRules, TargetRulesPath, ProjectFilePath); } } }