2016-04-21 16:40:25 +03:00
using System ;
using System.IO ;
using System.Linq ;
using Microsoft.Build.Framework ;
using Microsoft.Build.Utilities ;
using Xamarin.MacDev.Tasks ;
using Xamarin.MacDev ;
namespace Xamarin.iOS.Tasks
{
public abstract class DetectSdkLocationsTaskBase : Task
{
#region Inputs
public string SessionId { get ; set ; }
// This is also an input
[Output]
public string SdkVersion {
get ; set ;
}
[Required]
public string TargetFrameworkIdentifier {
get ; set ;
}
public string TargetArchitectures {
get ; set ;
}
public string XamarinSdkRoot {
get ; set ;
}
#endregion Inputs
#region Outputs
[Output]
public string SdkRoot {
get ; set ;
}
[Output]
public string SdkBinPath {
get ; set ;
}
[Output]
public string SdkDevPath {
get ; set ;
}
[Output]
public string SdkUsrPath {
get ; set ;
}
[Output]
public string SdkPlatform {
get ; set ;
}
[Output]
public bool SdkIsSimulator {
get ; set ;
}
2017-03-07 23:30:31 +03:00
[Output]
public bool IsXcode8 {
get ; set ;
}
2016-04-21 16:40:25 +03:00
#endregion Outputs
public PlatformFramework Framework {
get { return PlatformFrameworkHelper . GetFramework ( TargetFrameworkIdentifier ) ; }
}
public AppleSdk CurrentSdk {
get {
return IPhoneSdks . GetSdk ( Framework ) ;
}
}
public override bool Execute ( )
{
AppleSdkSettings . CheckChanged ( ) ;
IPhoneSdks . Reload ( ) ;
TargetArchitecture architectures ;
if ( string . IsNullOrEmpty ( TargetArchitectures ) | | ! Enum . TryParse ( TargetArchitectures , out architectures ) )
architectures = TargetArchitecture . Default ;
SdkIsSimulator = ( architectures & ( TargetArchitecture . i386 | TargetArchitecture . x86_64 ) ) ! = 0 ;
2017-03-07 23:30:31 +03:00
IsXcode8 = AppleSdkSettings . XcodeVersion . Major > = 8 ;
2017-04-18 23:51:43 +03:00
if ( EnsureAppleSdkRoot ( ) ) {
switch ( Framework ) {
case PlatformFramework . iOS :
EnsureiOSSdkPath ( ) ;
break ;
case PlatformFramework . TVOS :
EnsureTVOSSdkPath ( ) ;
break ;
case PlatformFramework . WatchOS :
EnsureWatchSdkPath ( ) ;
break ;
default :
throw new InvalidOperationException ( string . Format ( "Invalid framework: {0}" , Framework ) ) ;
}
2016-04-21 16:40:25 +03:00
}
2017-04-18 23:51:43 +03:00
EnsureXamarinSdkRoot ( ) ;
2016-04-21 16:40:25 +03:00
Log . LogTaskName ( "DetectSdkLocations" ) ;
Log . LogTaskProperty ( "TargetFrameworkIdentifier" , TargetFrameworkIdentifier ) ;
Log . LogTaskProperty ( "TargetArchitectures" , TargetArchitectures ) ;
Log . LogTaskProperty ( "SdkVersion" , SdkVersion ) ;
Log . LogTaskProperty ( "XamarinSdkRoot" , XamarinSdkRoot ) ;
Log . LogTaskProperty ( "SdkRoot" , SdkRoot ) ;
Log . LogTaskProperty ( "SdkDevPath" , SdkDevPath ) ;
Log . LogTaskProperty ( "SdkUsrPath" , SdkUsrPath ) ;
Log . LogTaskProperty ( "SdkPlatform" , SdkPlatform ) ;
Log . LogTaskProperty ( "SdkIsSimulator" , SdkIsSimulator ) ;
return ! Log . HasLoggedErrors ;
}
bool IsWatchFramework {
get { return TargetFrameworkIdentifier = = "Xamarin.WatchOS" ; }
}
bool IsTVOSFramework {
get { return TargetFrameworkIdentifier = = "Xamarin.TVOS" ; }
}
void EnsureTVOSSdkPath ( )
{
var currentSdk = IPhoneSdks . GetSdk ( Framework ) ;
IPhoneSdkVersion requestedSdkVersion ;
if ( string . IsNullOrEmpty ( SdkVersion ) ) {
requestedSdkVersion = IPhoneSdkVersion . UseDefault ;
} else if ( ! IPhoneSdkVersion . TryParse ( SdkVersion , out requestedSdkVersion ) ) {
Log . LogError ( "Could not parse the SDK version '{0}'" , SdkVersion ) ;
return ;
}
var sdkVersion = requestedSdkVersion . ResolveIfDefault ( currentSdk , SdkIsSimulator ) ;
if ( ! currentSdk . SdkIsInstalled ( sdkVersion , SdkIsSimulator ) ) {
sdkVersion = currentSdk . GetClosestInstalledSdk ( sdkVersion , SdkIsSimulator ) ;
if ( sdkVersion . IsUseDefault | | ! currentSdk . SdkIsInstalled ( sdkVersion , SdkIsSimulator ) ) {
if ( requestedSdkVersion . IsUseDefault ) {
Log . LogError ( "The Apple TVOS SDK is not installed." ) ;
} else {
Log . LogError ( "The TVOS SDK version '{0}' is not installed, and no newer version was found." , requestedSdkVersion . ToString ( ) ) ;
}
return ;
}
Log . LogWarning ( "The TVOS SDK version '{0}' is not installed. Using newer version '{1}' instead'." , requestedSdkVersion , sdkVersion ) ;
}
SdkVersion = sdkVersion . ToString ( ) ;
var platformDir = currentSdk . GetPlatformPath ( SdkIsSimulator ) ;
if ( string . IsNullOrEmpty ( platformDir ) | | ! Directory . Exists ( platformDir ) )
Log . LogError ( "Could not locate the TVOS platform directory at path '{0}'" , platformDir ) ;
SdkRoot = currentSdk . GetSdkPath ( sdkVersion , SdkIsSimulator ) ;
if ( string . IsNullOrEmpty ( SdkRoot ) | | ! Directory . Exists ( SdkRoot ) )
Log . LogError ( "Could not locate the TVOS '{0}' SDK at path '{1}'" , SdkVersion , SdkRoot ) ;
SdkUsrPath = DirExists ( "SDK Usr directory" , Path . Combine ( currentSdk . DeveloperRoot , "usr" ) ) ;
if ( string . IsNullOrEmpty ( SdkUsrPath ) )
Log . LogError ( "Could not locate the TVOS '{0}' SDK usr path at '{1}'" , SdkVersion , SdkRoot ) ;
SdkBinPath = DirExists ( "SDK bin directory" , Path . Combine ( SdkUsrPath , "bin" ) ) ;
if ( string . IsNullOrEmpty ( SdkBinPath ) )
Log . LogError ( "Could not locate SDK bin directory" ) ;
SdkPlatform = SdkIsSimulator ? "AppleTVSimulator" : "AppleTVOS" ;
}
void EnsureWatchSdkPath ( )
{
var currentSDK = IPhoneSdks . GetSdk ( Framework ) ;
IPhoneSdkVersion requestedSdkVersion ;
if ( string . IsNullOrEmpty ( SdkVersion ) ) {
requestedSdkVersion = IPhoneSdkVersion . UseDefault ;
} else if ( ! IPhoneSdkVersion . TryParse ( SdkVersion , out requestedSdkVersion ) ) {
Log . LogError ( "Could not parse the SDK version '{0}'" , SdkVersion ) ;
return ;
}
var sdkVersion = requestedSdkVersion . ResolveIfDefault ( currentSDK , SdkIsSimulator ) ;
if ( ! currentSDK . SdkIsInstalled ( sdkVersion , SdkIsSimulator ) ) {
sdkVersion = currentSDK . GetClosestInstalledSdk ( sdkVersion , SdkIsSimulator ) ;
if ( sdkVersion . IsUseDefault | | ! currentSDK . SdkIsInstalled ( sdkVersion , SdkIsSimulator ) ) {
if ( requestedSdkVersion . IsUseDefault ) {
Log . LogError ( "The Apple Watch SDK is not installed." ) ;
} else {
Log . LogError ( "The Watch SDK version '{0}' is not installed, and no newer version was found." , requestedSdkVersion . ToString ( ) ) ;
}
return ;
}
Log . LogWarning ( "The Watch SDK version '{0}' is not installed. Using newer version '{1}' instead'." , requestedSdkVersion , sdkVersion ) ;
}
SdkVersion = sdkVersion . ToString ( ) ;
var platformDir = currentSDK . GetPlatformPath ( SdkIsSimulator ) ;
if ( string . IsNullOrEmpty ( platformDir ) | | ! Directory . Exists ( platformDir ) )
Log . LogError ( "Could not locate the WatchOS platform directory at path '{0}'" , platformDir ) ;
SdkRoot = currentSDK . GetSdkPath ( sdkVersion , SdkIsSimulator ) ;
if ( string . IsNullOrEmpty ( SdkRoot ) | | ! Directory . Exists ( SdkRoot ) )
Log . LogError ( "Could not locate the WatchOS '{0}' SDK at path '{1}'" , SdkVersion , SdkRoot ) ;
SdkUsrPath = DirExists ( "SDK Usr directory" , Path . Combine ( currentSDK . DeveloperRoot , "usr" ) ) ;
if ( string . IsNullOrEmpty ( SdkUsrPath ) )
Log . LogError ( "Could not locate the WatchOS '{0}' SDK usr path at '{1}'" , SdkVersion , SdkRoot ) ;
SdkBinPath = DirExists ( "SDK bin directory" , Path . Combine ( SdkUsrPath , "bin" ) ) ;
if ( string . IsNullOrEmpty ( SdkBinPath ) )
Log . LogError ( "Could not locate SDK bin directory" ) ;
SdkPlatform = SdkIsSimulator ? "WatchSimulator" : "WatchOS" ;
}
void EnsureiOSSdkPath ( )
{
var currentSDK = IPhoneSdks . GetSdk ( Framework ) ;
IPhoneSdkVersion requestedSdkVersion ;
if ( string . IsNullOrEmpty ( SdkVersion ) ) {
requestedSdkVersion = IPhoneSdkVersion . UseDefault ;
} else if ( ! IPhoneSdkVersion . TryParse ( SdkVersion , out requestedSdkVersion ) ) {
Log . LogError ( "Could not parse the SDK version '{0}'" , SdkVersion ) ;
return ;
}
var sdkVersion = requestedSdkVersion . ResolveIfDefault ( currentSDK , SdkIsSimulator ) ;
if ( ! currentSDK . SdkIsInstalled ( sdkVersion , SdkIsSimulator ) ) {
sdkVersion = currentSDK . GetClosestInstalledSdk ( sdkVersion , SdkIsSimulator ) ;
if ( sdkVersion . IsUseDefault | | ! currentSDK . SdkIsInstalled ( sdkVersion , SdkIsSimulator ) ) {
if ( requestedSdkVersion . IsUseDefault ) {
Log . LogError ( "The Apple iOS SDK is not installed." ) ;
} else {
Log . LogError ( "The iOS SDK version '{0}' is not installed, and no newer version was found." , requestedSdkVersion . ToString ( ) ) ;
}
return ;
}
Log . LogWarning ( "The iOS SDK version '{0}' is not installed. Using newer version '{1}' instead'." , requestedSdkVersion , sdkVersion ) ;
}
SdkVersion = sdkVersion . ToString ( ) ;
var platformDir = currentSDK . GetPlatformPath ( SdkIsSimulator ) ;
if ( string . IsNullOrEmpty ( platformDir ) | | ! Directory . Exists ( platformDir ) )
Log . LogError ( "Could not locate the iOS platform directory at path '{0}'" , platformDir ) ;
SdkRoot = currentSDK . GetSdkPath ( sdkVersion , SdkIsSimulator ) ;
if ( string . IsNullOrEmpty ( SdkRoot ) | | ! Directory . Exists ( SdkRoot ) )
Log . LogError ( "Could not locate the iOS '{0}' SDK at path '{1}'" , SdkVersion , SdkRoot ) ;
// Note: Developer/Platforms/iPhoneOS.platform/Developer/usr is a physical directory, but
// Developer/Platforms/iPhoneSimulator.platform/Developer/bin has always been a symlink
// to Developer/bin and starting with Xcode 7 Beta 2, the usr symlink no longer exists.
SdkUsrPath = DirExists ( "SDK Usr directory" , Path . Combine ( platformDir , "Developer" , "usr" ) ) ;
if ( string . IsNullOrEmpty ( SdkUsrPath ) ) {
SdkUsrPath = DirExists ( "SDK Usr directory" , Path . Combine ( currentSDK . DeveloperRoot , "usr" ) ) ;
if ( string . IsNullOrEmpty ( SdkUsrPath ) )
Log . LogError ( "Could not locate the iOS '{0}' SDK usr path at '{1}'" , SdkVersion , SdkRoot ) ;
}
SdkBinPath = DirExists ( "SDK bin directory" , Path . Combine ( SdkUsrPath , "bin" ) ) ;
if ( string . IsNullOrEmpty ( SdkBinPath ) )
Log . LogError ( "Could not locate SDK bin directory" ) ;
SdkPlatform = SdkIsSimulator ? "iPhoneSimulator" : "iPhoneOS" ;
}
2017-04-18 23:51:43 +03:00
bool EnsureAppleSdkRoot ( )
2016-04-21 16:40:25 +03:00
{
if ( ! CurrentSdk . IsInstalled ) {
2017-04-19 23:41:34 +03:00
string ideSdkPath ;
if ( string . IsNullOrEmpty ( SessionId ) )
// SessionId is only and always defined on windows.
// We can't check 'Environment.OSVersion.Platform' since the base tasks are always executed on the Mac.
ideSdkPath = "(Project > SDK Locations > Apple > Apple SDK)" ;
else
ideSdkPath = "(Tools > Options > Xamarin > iOS Settings > Apple SDK)" ;
2017-04-18 23:51:43 +03:00
Log . LogError ( "Could not find a valid Xcode app bundle at '{0}'. Please update your Apple SDK location in Visual Studio's preferences {1}." , AppleSdkSettings . InvalidDeveloperRoot , ideSdkPath ) ;
return false ;
}
Log . LogMessage ( MessageImportance . Low , "DeveloperRoot: {0}" , CurrentSdk . DeveloperRoot ) ;
Log . LogMessage ( MessageImportance . Low , "DevicePlatform: {0}" , CurrentSdk . DevicePlatform ) ;
Log . LogMessage ( MessageImportance . Low , "GetPlatformPath: {0}" , CurrentSdk . GetPlatformPath ( false ) ) ;
SdkDevPath = CurrentSdk . DeveloperRoot ;
if ( string . IsNullOrEmpty ( SdkDevPath ) ) {
Log . LogError ( "Could not find valid a usable Xcode developer path" ) ;
return false ;
2016-04-21 16:40:25 +03:00
}
2017-04-18 23:51:43 +03:00
return true ;
2016-04-21 16:40:25 +03:00
}
void EnsureXamarinSdkRoot ( )
{
if ( string . IsNullOrEmpty ( XamarinSdkRoot ) )
XamarinSdkRoot = IPhoneSdks . MonoTouch . SdkDir ;
if ( string . IsNullOrEmpty ( XamarinSdkRoot ) | | ! Directory . Exists ( XamarinSdkRoot ) )
2017-04-18 23:51:43 +03:00
Log . LogError ( "Could not find 'Xamarin.iOS'" ) ;
2016-04-21 16:40:25 +03:00
}
string DirExists ( string checkingFor , string path )
{
try {
if ( path = = null )
return null ;
path = Path . GetFullPath ( path ) ;
2017-04-18 23:51:43 +03:00
Log . LogMessage ( MessageImportance . Low , "Searching for '{0}' in '{1}'" , checkingFor , path ) ;
2016-04-21 16:40:25 +03:00
return Directory . Exists ( path ) ? path : null ;
} catch {
return null ;
}
}
}
}