// Copyright Epic Games, Inc. All Rights Reserved. using AutomationTool; using Gauntlet; using System; using System.Collections.Generic; using System.IO; using System.Linq; using EpicGames.Core; using UnrealBuildTool; using UnrealBuildBase; namespace Turnkey.Commands { class VerifySdk : TurnkeyCommand { protected override CommandGroup Group => CommandGroup.Sdk; protected override void Execute(string[] CommandOptions) { bool bUnattended = TurnkeyUtils.ParseParam("Unattended", CommandOptions); bool bPreferFullSdk = TurnkeyUtils.ParseParam("PreferFull", CommandOptions); bool bForceSdkInstall = TurnkeyUtils.ParseParam("ForceSdkInstall", CommandOptions); bool bForceDeviceInstall = TurnkeyUtils.ParseParam("ForceDeviceInstall", CommandOptions); bool bUpdateIfNeeded = bForceSdkInstall || bForceDeviceInstall || TurnkeyUtils.ParseParam("UpdateIfNeeded", CommandOptions); bool bSkipPlatformCheck = TurnkeyUtils.ParseParam("SkipPlatform", CommandOptions); bool bAutoChooseBest = bUnattended || bUpdateIfNeeded; List PlatformsToCheck; List DevicesToCheck; TurnkeyUtils.GetPlatformsAndDevicesFromCommandLineOrUser(CommandOptions, true, out PlatformsToCheck, out DevicesToCheck); // if we got no devices, requested some, and platforms were not specified, then we don't want to continue. // if -device and -platform were specified, and no devices found, we will still continue with the platforms if (DevicesToCheck.Count == 0 && TurnkeyUtils.ParseParamValue("Device", null, CommandOptions) != null && TurnkeyUtils.ParseParamValue("Platform", null, CommandOptions) == null) { TurnkeyUtils.Log("Devices were requested, but none of them were found. Since -platforms was not specified, exiting command..."); } if (PlatformsToCheck.Count == 0 && !bSkipPlatformCheck) { TurnkeyUtils.Log("Platform(s) and/or device(s) needed for VerifySdk command. Check parameters or selections."); return; } TurnkeyUtils.Log("Installed Sdk validity:"); TurnkeyUtils.ExitCode = ExitCode.Success; TurnkeyContextImpl TurnkeyContext = new TurnkeyContextImpl(); TurnkeyUtils.StartTrackingExternalEnvVarChanges(); // check all the platforms foreach (UnrealTargetPlatform Platform in PlatformsToCheck) { UEBuildPlatformSDK PlatformSDK = UEBuildPlatformSDK.GetSDKForPlatform(Platform.ToString()); // get the platform object AutomationTool.Platform AutomationPlatform = AutomationTool.Platform.GetPlatform(Platform); // reset the errors for each device TurnkeyContext.ErrorMessages.Clear(); // checking availability may generate errors, if prerequisites are failing SdkUtils.LocalAvailability LocalState = SdkUtils.GetLocalAvailability(AutomationPlatform, bUpdateIfNeeded, TurnkeyContext); SdkUtils.LocalAvailability ReportedState = LocalState; string StatusString; bool bHasOutOfDateSDK = false; if ((LocalState & SdkUtils.LocalAvailability.Platform_ValidHostPrerequisites) == 0) { StatusString = "Invalid"; } else if ((LocalState & (SdkUtils.LocalAvailability.AutoSdk_ValidVersionExists | SdkUtils.LocalAvailability.InstalledSdk_ValidVersionExists)) == 0) { StatusString = "Invalid"; bHasOutOfDateSDK = true; } else { StatusString = "Valid"; ReportedState &= (SdkUtils.LocalAvailability.AutoSdk_ValidVersionExists | SdkUtils.LocalAvailability.InstalledSdk_ValidVersionExists | SdkUtils.LocalAvailability.Support_FullSdk | SdkUtils.LocalAvailability.Support_AutoSdk | SdkUtils.LocalAvailability.Sdk_HasBestVersion); } // gather list of tags/values to write out SDKCollection SDKInfo = null; List PlatformProperties = new List(); { // overall status (valid/invalid) PlatformProperties.Add($"Status={StatusString}"); if (PlatformSDK != null) { SDKInfo = PlatformSDK.GetAllSDKInfo(); if (SDKInfo.FullSdks.Count() > 1) { PlatformProperties.Add($"SDKs=\"{string.Join(",", SDKInfo.FullSdks.Select(x => x.Name))}\""); } // get all the Min/Max versions and insert them right in, they will be in the proper format for parsing PlatformProperties.Add(SDKInfo.ToString("Allowed", "")); } PlatformProperties.Add($"Flags=\"{ReportedState}\""); if (TurnkeyContext.ErrorMessages.Count() > 0) { PlatformProperties.Add($"Error=\"{string.Join("|", TurnkeyContext.ErrorMessages)}\""); } } // write out all of the properties in unreal struct format: "Platform: (Props,...)" TurnkeyUtils.Report("{0}: ({1})", Platform, string.Join(", ", PlatformProperties)); if (PlatformSDK == null) { continue; } // install if out of date, or if forcing it if (bForceSdkInstall || (bUpdateIfNeeded && bHasOutOfDateSDK)) { // gather the SDKs that we need to install List SdksToInstall = new List(); if (!bPreferFullSdk) { // attempt to install Autosdk FileSource AutoSdk = FileSource.FindMatchingSdk(AutomationPlatform, new FileSource.SourceType[] { FileSource.SourceType.AutoSdk }, bSelectBest: bAutoChooseBest); if (AutoSdk != null) { SdksToInstall.Add(AutoSdk); } } // if no autosdk was chosen, look for manual sdk(s) if (SdksToInstall.Count == 0) { // find manual versions that need to be installed (or all if forceing) IEnumerable NeededManualSdks = SDKInfo.FullSdks.Where(x => (bForceSdkInstall || x.Validity == SDKStatus.Invalid)); List MissingInstallers = new List(); foreach (SDKDescriptor Desc in NeededManualSdks) { // if we only have one manual sdk (normal), then don't specify a type string SpecificType = NeededManualSdks.Count() == 1 ? null : Desc.Name; FileSource SdkSource = FileSource.FindMatchingSdk(AutomationPlatform, new FileSource.SourceType[] { FileSource.SourceType.Full }, bSelectBest: bAutoChooseBest, SpecificType: SpecificType ); if (SdkSource != null) { SdksToInstall.Add(SdkSource); } else { MissingInstallers.Add(Desc.Name); } } if (NeededManualSdks.Count() > 1 && MissingInstallers.Count > 0) { TurnkeyUtils.Log($"ERROR: {Platform}: Unable to find all Sdk installers. Missing '{string.Join(", ", MissingInstallers)}'. Will the ones that were found..."); } } // if we went manual first, and still don't have any sdks, try for autosdk now if (bPreferFullSdk && SdksToInstall.Count == 0) { // attempt to install Autosdk FileSource AutoSdk = FileSource.FindMatchingSdk(AutomationPlatform, new FileSource.SourceType[] { FileSource.SourceType.AutoSdk }, bSelectBest: bAutoChooseBest); if (AutoSdk != null) { SdksToInstall.Add(AutoSdk); } } //FileSource BestSdk = null; //// find the best Sdk, prioritizing as request //if (bPreferFullSdk) //{ // BestSdk = FileSource.FindMatchingSdk(AutomationPlatform, new FileSource.SourceType[] { FileSource.SourceType.Full, FileSource.SourceType.BuildOnly, FileSource.SourceType.AutoSdk }, bSelectBest: bAutoChooseBest); //} //else //{ // BestSdk = FileSource.FindMatchingSdk(AutomationPlatform, new FileSource.SourceType[] { FileSource.SourceType.AutoSdk, FileSource.SourceType.BuildOnly, FileSource.SourceType.Full }, bSelectBest: bAutoChooseBest); //} if (SdksToInstall == null) { TurnkeyUtils.Log("ERROR: {0}: Unable to find any Sdks that could be installed", Platform); TurnkeyUtils.ExitCode = ExitCode.Error_SDKNotFound; } else { TurnkeyUtils.Log("Will install '{0}'", string.Join(", ", SdksToInstall.Select(x => x.Name))); foreach (FileSource Sdk in SdksToInstall) { bool bSdkAlreadyInstalled = Sdk.Type == FileSource.SourceType.Full && PlatformSDK.GetAllInstalledSDKVersions().Contains(Sdk.Version); // attempt to fast switch to the best one if it's already fully installed, unless we are forcing a reinstall if (!bForceSdkInstall && bSdkAlreadyInstalled) { bool bWasSwitched = PlatformSDK.SwitchToAlternateSDK(Sdk.Version, false); if (bWasSwitched == true) { TurnkeyUtils.Log("Fast-switched to already-installed version {0}", Sdk.Version); // if SwitchToAlternateSDK returns true, then we are good to go! continue; } } if (Sdk.DownloadOrInstall(Platform, TurnkeyContext, null, bUnattended, bSdkAlreadyInstalled) == false) { TurnkeyUtils.Log("Failed to install {0}", Sdk.Name); TurnkeyUtils.ExitCode = ExitCode.Error_SDKInstallFailed; } } // update LocalState // LocalState = SdkUtils.GetLocalAvailability(AutomationPlatform, false); // @todo turnkey: validate! } } // now check software version of each device if (DevicesToCheck != null) { foreach (DeviceInfo Device in DevicesToCheck.Where(x => x.Platform == Platform)) { // reset the errors for each device TurnkeyContext.ErrorMessages.Clear(); bool bArePrerequisitesValid = AutomationPlatform.UpdateDevicePrerequisites(Device, TurnkeyUtils.CommandUtilHelper, TurnkeyContext, !bUpdateIfNeeded); // get the min/max versions from PlatformSDK SDKCollection SoftwareInfo = PlatformSDK.GetAllSoftwareInfo(Device.Type, Device.SoftwareVersion); if (!SoftwareInfo.Sdks.Any()) { // if we found no supported software versions for the specific device type, query for device type independent versions SoftwareInfo = PlatformSDK.GetAllSoftwareInfo(null, Device.SoftwareVersion); } SdkUtils.LocalAvailability DeviceState = SdkUtils.LocalAvailability.None; if (!bArePrerequisitesValid) { StatusString = "Invalid"; DeviceState |= SdkUtils.LocalAvailability.Device_InvalidPrerequisites; } else if (SoftwareInfo.AreAllManualSDKsValid()) { StatusString = "Valid"; DeviceState |= SdkUtils.LocalAvailability.Device_InstallSoftwareValid; } else { StatusString = "Invalid"; DeviceState |= SdkUtils.LocalAvailability.Device_InstallSoftwareInvalid; } if (Device.bCanConnect == false) { DeviceState |= SdkUtils.LocalAvailability.Device_CannotConnect; } if (Device.AutoSoftwareUpdates == AutomationTool.DeviceInfo.AutoSoftwareUpdateMode.Disabled) { DeviceState |= SdkUtils.LocalAvailability.Device_AutoSoftwareUpdates_Disabled; } else if (Device.AutoSoftwareUpdates == AutomationTool.DeviceInfo.AutoSoftwareUpdateMode.Enabled) { DeviceState |= SdkUtils.LocalAvailability.Device_AutoSoftwareUpdates_Enabled; } List DeviceProperties = new List() { $"Name={Device.Name}", $"Type={Device.Type}", $"Status={StatusString}", SoftwareInfo.ToString("Allowed", ""), $"Flags=\"{DeviceState}\"", }; foreach (KeyValuePair Pair in Device.PlatformValues) { DeviceProperties.Add($"{Pair.Key}=\"{Pair.Value}\""); } if (TurnkeyContext.ErrorMessages.Count() > 0) { DeviceProperties.Add(string.Format("Error=\"{0}\"", string.Join("|", TurnkeyContext.ErrorMessages))); } // write out all of the properties in unreal struct format: "Platform: (Props,...)" TurnkeyUtils.Report("{0}@{1}: ({2})", Platform, Device.Id, string.Join(", ", DeviceProperties)); //TurnkeyUtils.Report("{0}@{1}: (Name={2}, Status={3}, Installed={4}, MinAllowed={5}, MaxAllowed={6}, Flags=\"{7}\"{8})", Platform, Device.Id, Device.Name, StatusString, Device.SoftwareVersion, // MinSoftwareAllowedVersion, MaxSoftwareAllowedVersion, DeviceState.ToString(), DeviceErrorString); if (bForceDeviceInstall || !SoftwareInfo.AreAllManualSDKsValid()) { if (bUpdateIfNeeded) { if (Device.bCanConnect) { FileSource MatchingInstallableSdk = FileSource.FindMatchingSdk(AutomationPlatform, new FileSource.SourceType[] { FileSource.SourceType.Flash }, bSelectBest: bUnattended, DeviceType: Device.Type, CurrentSdk: Device.SoftwareVersion); if (MatchingInstallableSdk == null) { TurnkeyUtils.Log("ERROR: {0}: Unable to find any Sdks that could be installed on {1}", Platform, Device.Name); TurnkeyUtils.ExitCode = ExitCode.Error_SDKNotFound; } else { if (MatchingInstallableSdk.DownloadOrInstall(Platform, TurnkeyContext, Device, bUnattended, false) == false) { TurnkeyUtils.Log("Failed to update Device '{0}' with '{0}'", Device.Name, MatchingInstallableSdk.Name); TurnkeyUtils.ExitCode = ExitCode.Error_DeviceUpdateFailed; } } } else { TurnkeyUtils.Log("Skipping device {0} because it cannot connect.", Device.Name); } } } } } } TurnkeyUtils.EndTrackingExternalEnvVarChanges(); } } }