public List <IBuild> GetBuildsAtPath(string InProjectName, string InPath, int MaxRecursion = 3)
        {
            List <IBuild> Builds = new List <IBuild>();

            if (Directory.Exists(InPath))
            {
                // First try creating a build just from this path
                IEnumerable <LuminBuild> FoundBuilds = LuminBuild.CreateFromPath(InProjectName, InPath);

                // If we found one we were pointed at a specific case so we're done
                if (FoundBuilds.Any())
                {
                    Builds.AddRange(FoundBuilds);
                }
                else
                {
                    // Ok, find all directories that match our platform and add those
                    DirectoryInfo TopDir = new DirectoryInfo(InPath);

                    // find all directories that begin with Lumin
                    IEnumerable <string> LuminDirs = Directory.EnumerateDirectories(InPath, "Lumin*", SearchOption.AllDirectories);

                    foreach (string DirPath in LuminDirs)
                    {
                        FoundBuilds = LuminBuild.CreateFromPath(InProjectName, DirPath);
                        Builds.AddRange(FoundBuilds);
                    }
                }
            }

            return(Builds);
        }
        public static IEnumerable <LuminBuild> CreateFromPath(string InProjectName, string InPath)
        {
            string BuildPath = InPath;

            List <LuminBuild> DiscoveredBuilds = new List <LuminBuild>();

            DirectoryInfo Di = new DirectoryInfo(BuildPath);

            if (Di.Exists)
            {
                // find all install batchfiles
                FileInfo[] InstallFiles = Di.GetFiles("Install_*");

                foreach (FileInfo Fi in InstallFiles)
                {
                    Log.Verbose("Pulling install data from {0}", Fi.FullName);

                    string AbsPath = Fi.Directory.FullName;

                    // read contents and replace linefeeds (regex doesn't stop on them :((
                    string BatContents = File.ReadAllText(Fi.FullName).Replace(Environment.NewLine, "\n");

                    // Replace .bat with .mpk and strip up to and including the first _, that is then our MPK name
                    string SourceMpkPath = Regex.Replace(Fi.Name, ".bat", ".mpk", RegexOptions.IgnoreCase);
                    SourceMpkPath = SourceMpkPath.Substring(SourceMpkPath.IndexOf("_") + 1);
                    SourceMpkPath = Path.Combine(AbsPath, SourceMpkPath);

                    Match  Info             = Regex.Match(BatContents, @"install\s+(-u+)\s""%~dp0\\(.+)""");
                    string LuminPackageName = Info.Groups[2].ToString();

                    if (string.IsNullOrEmpty(SourceMpkPath))
                    {
                        Log.Warning("No MPK found for build at {0}", Fi.FullName);
                        continue;
                    }

                    if (string.IsNullOrEmpty(LuminPackageName))
                    {
                        Log.Warning("No product name found for build at {0}", Fi.FullName);
                        continue;
                    }

                    UnrealTargetConfiguration UnrealConfig = UnrealHelpers.GetConfigurationFromExecutableName(InProjectName, Fi.Name);

                    // Lumin builds are always packaged, and we can always replace the command line
                    BuildFlags Flags = BuildFlags.Packaged | BuildFlags.CanReplaceCommandLine;

                    if (AbsPath.Contains("Bulk"))
                    {
                        Flags |= BuildFlags.Bulk;
                    }
                    else
                    {
                        Flags |= BuildFlags.NotBulk;
                    }

                    LuminBuild NewBuild = new LuminBuild(UnrealConfig, LuminPackageName, SourceMpkPath, Flags);

                    DiscoveredBuilds.Add(NewBuild);

                    Log.Verbose("Found {0} {1} build at {2}", UnrealConfig, ((Flags & BuildFlags.Bulk) == BuildFlags.Bulk) ? "(bulk)" : "(not bulk)", AbsPath);
                }
            }

            return(DiscoveredBuilds);
        }
		public IAppInstall InstallApplication(UnrealAppConfig AppConfig)
		{
			LuminBuild Build = AppConfig.Build as LuminBuild;

			string QualifiedPackageName = GetQualifiedProjectName(AppConfig.ProjectName);

			InstalledAppName = AppConfig.ProjectName;

			// Ensure MPK exists
			if (Build == null)
			{
				throw new AutomationException("Invalid build for Lumin!");
			}

			// kill any currently running instance:
			KillRunningProcess(QualifiedPackageName);

			bool SkipDeploy = Globals.Params.ParseParam("SkipDeploy");

			if (SkipDeploy == false)
			{
				// remote dir used to save things
				string RemoteDir = "/documents/c2/" + AppConfig.ProjectName.ToLower();				
				//string DependencyDir = RemoteDir + "/deps";

				// device artifact path, always clear between runs
				// This clear is from andorid, but currently not needed on mldb.  Everything is removed when you remove the pak
				// If in the future mldb supports a "sidecar" of data like the obb on Android, might need to delete that here
				DeviceArtifactPath = string.Format("{0}/{1}/saved", RemoteDir.ToLower(), AppConfig.ProjectName.ToLower());

				if (Globals.Params.ParseParam("cleandevice"))
				{
					Log.Info("Cleaning previous builds due to presence of -cleandevice");

					// we need to ununstall then install the mpk - don't care if it fails, may have been deleted
					Log.Info("Uninstalling {0}", QualifiedPackageName);
					RunMldbDeviceCommand(string.Format("uninstall {0}", QualifiedPackageName));
				}

				// path to the MPK to install.
				string MpkPath = Build.SourceMpkPath;

				// check for a local newer executable
				if (Globals.Params.ParseParam("dev"))
				{
					string MpkFileName = UnrealHelpers.GetExecutableName(AppConfig.ProjectName, UnrealTargetPlatform.Lumin, AppConfig.Configuration, AppConfig.ProcessType, "mpk");

					string LocalMPK = Path.Combine(Environment.CurrentDirectory, AppConfig.ProjectName, "Binaries/Lumin", MpkFileName);

					bool LocalFileExists = File.Exists(LocalMPK);
					bool LocalFileNewer = LocalFileExists && File.GetLastWriteTime(LocalMPK) > File.GetLastWriteTime(MpkPath);

					Log.Verbose("Checking for newer binary at {0}", LocalMPK);
					Log.Verbose("LocalFile exists: {0}. Newer: {1}", LocalFileExists, LocalFileNewer);

					if (LocalFileExists && LocalFileNewer)
					{
						MpkPath = LocalMPK;
					}
				}

				// first install the MPK
				RunMldbDeviceCommand(string.Format("install -u {0}", MpkPath));

				// If in the future mldb supports a "sidecar" of data like the obb on Android, might need to install that here
				// The Gauntlet.TargetDeviceAndroid.cs implementation should be a good starting point
			}

			LuminAppInstall AppInstall = new LuminAppInstall(this, AppConfig.ProjectName, Build.LuminPackageName, AppConfig.CommandLine);

			return AppInstall;
		}