// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Linq;
using AutomationTool;
using UnrealBuildTool;
using System.Threading.Tasks;
namespace Gauntlet
{
///
/// Base class implementing Installer functionality
///
public class InstallUnreal
{
///
/// A method which is responsible for the core Installer functionality.
/// Iterates through devices and installs the project to each device.
///
///
public static bool RunInstall(
string PlatformParam,
string BuildPath,
string ProjectName,
string CommandLine,
string DevicesArg,
int ParallelTasks )
{
if (string.IsNullOrEmpty(ProjectName))
{
throw new Exception("need -project=");
}
if (string.IsNullOrEmpty(PlatformParam) || string.IsNullOrEmpty(BuildPath))
{
throw new Exception("need -platform= and -path=\"path to build\"");
}
if (Directory.Exists(BuildPath) == false)
{
throw new AutomationException("Path {0} does not exist", BuildPath);
}
UnrealTargetPlatform Platform = UnrealTargetPlatform.Parse(PlatformParam);
// find all build sources that can be created a folder path
IEnumerable BuildSources = Gauntlet.Utils.InterfaceHelpers.FindImplementations();
if (BuildSources.Count() == 0)
{
throw new AutomationException("No BuildSources found for platform {0}", Platform);
}
IReadOnlyCollection Builds = BuildSources.Where(S => S.CanSupportPlatform(Platform)).SelectMany(S => S.GetBuildsAtPath(ProjectName, BuildPath)).ToList();
if (Builds.Count() == 0)
{
throw new AutomationException("No builds for {0} found at {1}", Platform, BuildPath);
}
IEnumerable DeviceList = null;
if (string.IsNullOrEmpty(DevicesArg))
{
// find all build sources that can be created a folder path
IEnumerable DeviceSources = Gauntlet.Utils.InterfaceHelpers.FindImplementations();
DeviceList = DeviceSources.Where(S => S.CanSupportPlatform(Platform)).SelectMany(S => S.GetDefaultDevices());
}
else
{
IEnumerable Devices = DevicesArg.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries).Select(S => S.Trim());
IDeviceFactory Factory = Gauntlet.Utils.InterfaceHelpers.FindImplementations()
.Where(F => F.CanSupportPlatform(Platform))
.FirstOrDefault();
if (Factory == null)
{
throw new AutomationException("No IDeviceFactory implmenetation that supports {0}", Platform);
}
DeviceList = Devices.Select(D => Factory.CreateDevice(D, null)).ToArray();
}
if (DeviceList.Count() == 0)
{
throw new AutomationException("No devices found for {0}", Platform);
}
if (ParallelTasks == -1)
{
//If a value is not passed in as a paramter, set ParallelTasks equal to the number of devices, MAX 4
ParallelTasks = (DeviceList.Count() > 4) ? 4 : DeviceList.Count();
}
var POptions = new ParallelOptions { MaxDegreeOfParallelism = ParallelTasks };
// now copy it up to four builds at a time
Parallel.ForEach(DeviceList, POptions, Device =>
{
DateTime StartTime = DateTime.Now;
UnrealAppConfig Config = new UnrealAppConfig();
Config.CommandLine = CommandLine;
Config.ProjectName = ProjectName;
Config.Name = ProjectName;
string BuildSandbox = Globals.Params.ParseValue("sandbox", string.Empty);
Config.Sandbox = string.IsNullOrEmpty(BuildSandbox) ? Config.Sandbox : BuildSandbox;
// We always (currently..) need to be able to replace the command line
BuildFlags Flags = BuildFlags.CanReplaceCommandLine;
if (Globals.Params.ParseParam("dev"))
{
Flags |= BuildFlags.CanReplaceExecutable;
}
if (Globals.Params.ParseParam("bulk"))
{
Flags |= BuildFlags.Bulk;
}
if (Globals.Params.ParseParam("notbulk"))
{
Flags |= BuildFlags.NotBulk;
}
if (Globals.Params.ParseParam("packaged"))
{
Flags |= BuildFlags.Packaged;
}
if (Globals.Params.ParseParam("staged"))
{
Flags |= BuildFlags.Loose;
}
IEnumerable FlaggedBuilds = Builds.Where(S => S.Flags.HasFlag(Flags));
if (FlaggedBuilds.Count() == 0)
{
throw new AutomationException("Unable to find build at {0} with build flag(s): {1} ", BuildPath, Flags.ToString());
}
string BuildConfiguration = Globals.Params.ParseValue("configuration", String.Empty);
switch (BuildConfiguration.ToLower())
{
case "shipping":
Config.Build = FlaggedBuilds.Where(S => S.Configuration == UnrealTargetConfiguration.Shipping).FirstOrDefault();
break;
case "test":
Config.Build = FlaggedBuilds.Where(S => S.Configuration == UnrealTargetConfiguration.Test).FirstOrDefault();
break;
case "development":
Config.Build = FlaggedBuilds.Where(S => S.Configuration == UnrealTargetConfiguration.Development).FirstOrDefault();
break;
default:
Log.Info("No build configuration was provided, selecting first available build.");
Config.Build = FlaggedBuilds.FirstOrDefault();
break;
}
if (!Device.Connect())
{
throw new AutomationException("Failed to connect to device: {0}", Device);
}
if (Config.Build != null)
{
Log.Info("Installing build on device {DeviceName}", Device.Name);
Device.InstallBuild(Config);
}
else
{
throw new AutomationException("Unable to find build at {0} with build flag(s)={1} and configuration type={2} for {3} platform type", BuildPath, Flags, BuildConfiguration, Platform);
}
TimeSpan Elapsed = (DateTime.Now - StartTime);
Log.Info("Installed on device {0} in {1:D2}m:{2:D2}s", Device.Name, Elapsed.Minutes, Elapsed.Seconds);
});
DeviceList = null;
// wait for all the adb tasks to start up or UAT will kill them on exit...
Thread.Sleep(5000);
return true;
}
}
}