// 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);
}
}
}