Skip to content

Commit

Permalink
[xharness] Add support for generating a tvOS version of .NET iOS proj…
Browse files Browse the repository at this point in the history
…ects.
  • Loading branch information
rolfbjarne committed Jul 13, 2020
1 parent 773df34 commit 696cdd2
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 29 deletions.
2 changes: 1 addition & 1 deletion tests/xharness/Harness.cs
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ void AutoConfigureIOS ()
IOSTestProjects.Add (new iOSTestProject (Path.GetFullPath (Path.Combine (RootDirectory, p + "/" + p + ".fsproj")), false) { Name = p });

IOSTestProjects.Add (new iOSTestProject (Path.GetFullPath (Path.Combine (RootDirectory, "introspection", "iOS", "introspection-ios.csproj"))) { Name = "introspection" });
IOSTestProjects.Add (new iOSTestProject (Path.GetFullPath (Path.Combine (RootDirectory, "introspection", "iOS", "introspection-ios-dotnet.csproj"))) { Name = "introspection", IsDotNetProject = true, SkipiOSVariation = false, SkiptvOSVariation = true, SkipwatchOSVariation = true, SkipTodayExtensionVariation = true, SkipDeviceVariations = true, SkipiOS32Variation = true, });
IOSTestProjects.Add (new iOSTestProject (Path.GetFullPath (Path.Combine (RootDirectory, "introspection", "iOS", "introspection-ios-dotnet.csproj"))) { Name = "introspection", IsDotNetProject = true, SkipiOSVariation = false, SkiptvOSVariation = false, SkipwatchOSVariation = true, SkipTodayExtensionVariation = true, SkipDeviceVariations = true, SkipiOS32Variation = true, });
IOSTestProjects.Add (new iOSTestProject (Path.GetFullPath (Path.Combine (RootDirectory, "linker", "ios", "dont link", "dont link.csproj"))) { Configurations = new string [] { "Debug", "Release" } });
IOSTestProjects.Add (new iOSTestProject (Path.GetFullPath (Path.Combine (RootDirectory, "linker", "ios", "link all", "link all.csproj"))) { Configurations = new string [] { "Debug", "Release" } });
IOSTestProjects.Add (new iOSTestProject (Path.GetFullPath (Path.Combine (RootDirectory, "linker", "ios", "link sdk", "link sdk.csproj"))) { Configurations = new string [] { "Debug", "Release" } });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,14 @@ public static string GetOutputPath (this XmlDocument csproj, string platform, st
return GetElementValue (csproj, platform, configuration, "OutputPath");
}

static string GetElementValue (this XmlDocument csproj, string platform, string configuration, string elementName)
static string GetElementValue (this XmlDocument csproj, string platform, string configuration, string elementName, bool throwIfNotFound = true)
{
var nodes = csproj.SelectNodes ($"/*/*/*[local-name() = '{elementName}']");
if (nodes.Count == 0)
throw new Exception ($"Could not find node {elementName}");
if (nodes.Count == 0) {
if (throwIfNotFound)
throw new Exception ($"Could not find node {elementName}");
return null;
}
foreach (XmlNode n in nodes) {
if (IsNodeApplicable (n, platform, configuration))
return n.InnerText.Replace ("$(Platform)", platform).Replace ("$(Configuration)", configuration);
Expand All @@ -182,6 +185,11 @@ public static string GetOutputAssemblyPath (this XmlDocument csproj, string plat
return outputPath + "\\" + assemblyName + "." + extension; // MSBuild-style paths.
}

public static string GetIsBindingProject (this XmlDocument csproj)
{
return GetElementValue (csproj, string.Empty, string.Empty, "IsBindingProject", throwIfNotFound: false);
}

public static void SetIntermediateOutputPath (this XmlDocument csproj, string value)
{
// Set any existing IntermediateOutputPath
Expand Down Expand Up @@ -433,19 +441,21 @@ public static string GetImport (this XmlDocument csproj)
}

public delegate bool FixReferenceDelegate (string reference, out string fixed_reference);
public static void FixProjectReferences (this XmlDocument csproj, string suffix, FixReferenceDelegate fixCallback = null)
public static void FixProjectReferences (this XmlDocument csproj, string suffix, FixReferenceDelegate fixCallback = null, FixReferenceDelegate fixIncludeCallback = null)
{
var nodes = csproj.SelectNodes ("/*/*/*[local-name() = 'ProjectReference']");
if (nodes.Count == 0)
return;
foreach (XmlNode n in nodes) {
var name = n ["Name"].InnerText;
var name = n ["Name"]?.InnerText;
string fixed_name = null;
if (fixCallback != null && !fixCallback (name, out fixed_name))
if (name != null && fixCallback != null && !fixCallback (name, out fixed_name))
continue;
var include = n.Attributes ["Include"];
string fixed_include;
if (fixed_name == null) {
if (fixIncludeCallback != null && fixIncludeCallback (include.Value, out fixed_include)) {
// we're done here
} else if (fixed_name == null) {
fixed_include = include.Value;
fixed_include = fixed_include.Replace (".csproj", suffix + ".csproj");
fixed_include = fixed_include.Replace (".fsproj", suffix + ".fsproj");
Expand All @@ -456,9 +466,11 @@ public static void FixProjectReferences (this XmlDocument csproj, string suffix,
fixed_include = fixed_include.Replace ('/', '\\');
}
n.Attributes ["Include"].Value = fixed_include;
var nameElement = n ["Name"];
name = System.IO.Path.GetFileNameWithoutExtension (fixed_include.Replace ('\\', '/'));
nameElement.InnerText = name;
if (name != null) {
var nameElement = n ["Name"];
name = System.IO.Path.GetFileNameWithoutExtension (fixed_include.Replace ('\\', '/'));
nameElement.InnerText = name;
}
}
}

Expand Down Expand Up @@ -632,12 +644,36 @@ public static IEnumerable<string> GetNunitAndXunitTestReferences (this XmlDocume
}
}

public static void SetSdk (this XmlDocument csproj, string sdk)
{
var node = csproj.SelectSingleNode ("//*[local-name() = 'Project']");
node.Attributes ["Sdk"].Value = sdk;
}

public static void SetRuntimeIdentifier (this XmlDocument csproj, string runtimeIdentifier)
{
var node = csproj.SelectSingleNode ("//*[local-name() = 'RuntimeIdentifier']");
node.InnerText = runtimeIdentifier;
}

public static void SetProjectReferenceValue (this XmlDocument csproj, string projectInclude, string node, string value)
{
var nameNode = csproj.SelectSingleNode ("//*[local-name() = 'ProjectReference' and @Include = '" + projectInclude + "']/*[local-name() = '" + node + "']");
nameNode.InnerText = value;
}

public static string GetAssetTargetFallback (this XmlDocument csproj)
{
return csproj.SelectSingleNode ("//*[local-name() = 'AssetTargetFallback']")?.InnerText;
}

public static void SetAssetTargetFallback (this XmlDocument csproj, string value)
{
var node = csproj.SelectSingleNode ("//*[local-name() = 'AssetTargetFallback']");
if (node != null)
node.InnerText = value;
}

public static void SetProjectReferenceInclude (this XmlDocument csproj, string projectInclude, string value)
{
var elements = csproj.SelectElementNodes ("ProjectReference");
Expand Down
8 changes: 7 additions & 1 deletion tests/xharness/Targets/MacTarget.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Microsoft.DotNet.XHarness.iOS.Shared;
using Microsoft.DotNet.XHarness.iOS.Shared.Hardware;
using Microsoft.DotNet.XHarness.iOS.Shared.Utilities;

namespace Xharness.Targets
Expand Down Expand Up @@ -112,7 +113,12 @@ public override string Platform {
return "mac";
}
}


public override string DotNetSdk => "Microsoft.macOS.Sdk";
public override string RuntimeIdentifier => "osx-x64";
public override DevicePlatform ApplePlatform => DevicePlatform.macOS;
public override string TargetFrameworkForNuGet => "xamarinmac10";

public MonoNativeInfo MonoNativeInfo { get; set; }

protected override bool FixProjectReference (string name, out string fixed_name)
Expand Down
5 changes: 5 additions & 0 deletions tests/xharness/Targets/TVOSTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ protected override bool SupportsBitcode {
}
}

public override string DotNetSdk => "Microsoft.tvOS.Sdk";
public override string RuntimeIdentifier => "tvos-x64";
public override DevicePlatform ApplePlatform => DevicePlatform.tvOS;
public override string TargetFrameworkForNuGet => "xamarintvos10";

static Dictionary<string, string> project_guids = new Dictionary<string, string> ();

protected override void ProcessProject ()
Expand Down
93 changes: 77 additions & 16 deletions tests/xharness/Targets/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using System.Xml;
using Microsoft.DotNet.XHarness.iOS.Shared.Hardware;
using Microsoft.DotNet.XHarness.iOS.Shared.Utilities;
using Microsoft.DotNet.XHarness.iOS.Shared;

Expand All @@ -26,6 +27,8 @@ public abstract class Target

public string TemplateProjectPath { get; set; }

bool? is_dotnet_project;
public bool IsDotNetProject { get { return is_dotnet_project ?? (is_dotnet_project = inputProject.IsDotNetProject ()).Value; } }
public string OutputType { get { return outputType; } }
public string TargetDirectory { get { return targetDirectory; } }
public bool IsLibrary { get { return outputType == "Library"; } }
Expand Down Expand Up @@ -67,12 +70,55 @@ public abstract class Target

public string LanguageGuid { get { return IsFSharp ? FSharpGuid : CSharpGuid; } }

public abstract string DotNetSdk { get; }
public abstract string RuntimeIdentifier { get; }
public abstract DevicePlatform ApplePlatform { get; }
public abstract string TargetFrameworkForNuGet { get; }

public string PlatformString {
get {
switch (ApplePlatform) {
case DevicePlatform.iOS:
return "iOS";
case DevicePlatform.tvOS:
return "tvOS";
case DevicePlatform.watchOS:
return "watchOS";
case DevicePlatform.macOS:
return "macOS";
default:
throw new Exception ($"Unknown platform: {ApplePlatform}");
}
}
}

protected virtual bool FixProjectReference (string name, out string fixed_name)
{
fixed_name = null;
return true;
}

protected virtual bool FixDotNetProjectReference (string include, out string fixed_include)
{
if (include.EndsWith ("Touch.Client-iOS.dotnet.csproj", StringComparison.Ordinal)) {
fixed_include = include.Replace ("-iOS", "-" + PlatformString);
} else {
fixed_include = include;
}

return true;
}

protected virtual void ProcessDotNetProject ()
{
inputProject.SetSdk (DotNetSdk);
inputProject.SetRuntimeIdentifier (RuntimeIdentifier);
inputProject.FixProjectReferences (Suffix, fixIncludeCallback: FixDotNetProjectReference);
var fixedAssetTargetFallback = inputProject.GetAssetTargetFallback ()?.Replace ("xamarinios10", TargetFrameworkForNuGet);
if (fixedAssetTargetFallback != null)
inputProject.SetAssetTargetFallback (fixedAssetTargetFallback);
}

protected virtual void ProcessProject ()
{
if (SupportsBitcode) {
Expand Down Expand Up @@ -143,8 +189,19 @@ protected void CreateLibraryProject ()
ProjectGuid = inputProject.GetProjectGuid ();
}

protected virtual void CreateDotNetProject ()
{
ProcessDotNetProject ();
inputProject.Save (ProjectPath, (l, m) => Harness.Log (l, m));
UpdateInfoPList ();
}

protected virtual void ExecuteInternal ()
{
if (IsDotNetProject) {
CreateDotNetProject ();
return;
}
switch (OutputType) {
case "Exe":
CreateExecutableProject ();
Expand Down Expand Up @@ -185,22 +242,26 @@ public void Execute ()
inputProject.LoadWithoutNetworkAccess (TemplateProjectPath);

outputType = inputProject.GetOutputType ();

switch (inputProject.GetImport ()) {
case "$(MSBuildExtensionsPath)\\Xamarin\\iOS\\Xamarin.iOS.CSharp.targets":
case "$(MSBuildExtensionsPath)\\Xamarin\\iOS\\Xamarin.iOS.FSharp.targets":
case "$(MSBuildExtensionsPath)\\Xamarin\\Mac\\Xamarin.Mac.CSharp.targets":
case "$(MSBuildExtensionsPath":
case "$(MSBuildBinPath)\\Microsoft.CSharp.targets":
IsBindingProject = false;
break;
case "$(MSBuildExtensionsPath)\\Xamarin\\iOS\\Xamarin.iOS.ObjCBinding.CSharp.targets":
case "$(MSBuildExtensionsPath)\\Xamarin\\iOS\\Xamarin.iOS.ObjCBinding.FSharp.targets":
case "$(MSBuildExtensionsPath)\\Xamarin\\Mac\\Xamarin.Mac.ObjcBinding.CSharp":
IsBindingProject = true;
break;
default:
throw new Exception (string.Format ("Unknown Imports: {0} in {1}", inputProject.GetImport (), TemplateProjectPath));

if (inputProject.IsDotNetProject ()) {
IsBindingProject = string.Equals (inputProject.GetIsBindingProject (), "true", StringComparison.OrdinalIgnoreCase);
} else {
switch (inputProject.GetImport ()) {
case "$(MSBuildExtensionsPath)\\Xamarin\\iOS\\Xamarin.iOS.CSharp.targets":
case "$(MSBuildExtensionsPath)\\Xamarin\\iOS\\Xamarin.iOS.FSharp.targets":
case "$(MSBuildExtensionsPath)\\Xamarin\\Mac\\Xamarin.Mac.CSharp.targets":
case "$(MSBuildExtensionsPath":
case "$(MSBuildBinPath)\\Microsoft.CSharp.targets":
IsBindingProject = false;
break;
case "$(MSBuildExtensionsPath)\\Xamarin\\iOS\\Xamarin.iOS.ObjCBinding.CSharp.targets":
case "$(MSBuildExtensionsPath)\\Xamarin\\iOS\\Xamarin.iOS.ObjCBinding.FSharp.targets":
case "$(MSBuildExtensionsPath)\\Xamarin\\Mac\\Xamarin.Mac.ObjcBinding.CSharp":
IsBindingProject = true;
break;
default:
throw new Exception (string.Format ("Unknown Imports: {0} in {1}", inputProject.GetImport (), TemplateProjectPath));
}
}

ExecuteInternal ();
Expand Down
5 changes: 5 additions & 0 deletions tests/xharness/Targets/UnifiedTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ protected override bool SupportsBitcode {
}
}

public override string DotNetSdk => "Microsoft.iOS.Sdk";
public override string RuntimeIdentifier => "ios-x64";
public override DevicePlatform ApplePlatform => DevicePlatform.iOS;
public override string TargetFrameworkForNuGet => "xamarinios10";

protected override void ExecuteInternal ()
{
if (MonoNativeInfo == null)
Expand Down
5 changes: 5 additions & 0 deletions tests/xharness/Targets/WatchOSTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ public override string DeviceArchitectures {
get { return "ARMv7k, ARM64_32"; }
}

public override string DotNetSdk => "Microsoft.watchOS.Sdk";
public override string RuntimeIdentifier => throw new NotImplementedException ();
public override DevicePlatform ApplePlatform => DevicePlatform.watchOS;
public override string TargetFrameworkForNuGet => "xamarinwatch10";

void CreateWatchOSAppProject ()
{
var csproj = new XmlDocument ();
Expand Down
2 changes: 1 addition & 1 deletion tests/xharness/Targets/iOSTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
namespace Xharness.Targets
{
// iOS here means Xamarin.iOS, not iOS as opposed to tvOS/watchOS.
public class iOSTarget : Target
public abstract class iOSTarget : Target
{
public iOSTestProject TestProject;

Expand Down

0 comments on commit 696cdd2

Please sign in to comment.