public XDocument GetXML() { var sampleCSProjPath = (Type == ProjectStyle.PackageReference && ToolingVersion15) ? "Test.Utility.compiler.resources.project2.csproj" : "Test.Utility.compiler.resources.project1.csproj"; var s = ResourceTestUtility.GetResource(sampleCSProjPath, typeof(SimpleTestProjectContext)); var xml = XDocument.Parse(s); ProjectFileUtils.AddProperties(xml, new Dictionary <string, string>() { { "ProjectGuid", "{" + ProjectGuid.ToString() + "}" }, { "BaseIntermediateOutputPath", OutputPath }, { "AssemblyName", ProjectName } }); ProjectFileUtils.AddProperties(xml, Properties); if (Type == ProjectStyle.PackageReference) { if (WarningsAsErrors) { ProjectFileUtils.AddProperties(xml, new Dictionary <string, string>() { { "WarningsAsErrors", "true" } }); } ProjectFileUtils.AddProperties(xml, new Dictionary <string, string>() { { "Version", Version }, { "DebugType", "portable" } }); if (!IsLegacyPackageReference) { var tfPropName = SingleTargetFramework ? "TargetFramework" : "TargetFrameworks"; ProjectFileUtils.AddProperties(xml, new Dictionary <string, string>() { { tfPropName, OriginalFrameworkStrings.Count != 0 ? string.Join(";", OriginalFrameworkStrings): string.Join(";", Frameworks.Select(f => f.Framework.GetShortFolderName())) }, }); } var addedToAllProjectReferences = new HashSet <SimpleTestProjectContext>(); var addedToAllPackageReferences = new HashSet <SimpleTestPackageContext>(); foreach (var frameworkInfo in Frameworks) { // Add properties with a TFM condition ProjectFileUtils.AddProperties(xml, frameworkInfo.Properties, $" '$(TargetFramework)' == '{frameworkInfo.Framework.GetShortFolderName()}' "); foreach (var package in frameworkInfo.PackageReferences) { var referenceFramework = frameworkInfo.Framework; // Drop the conditional if it is not needed if (Frameworks.All(f => f.PackageReferences.Contains(package))) { referenceFramework = NuGetFramework.AnyFramework; if (!addedToAllPackageReferences.Add(package)) { // Skip since this was already added continue; } } var props = new Dictionary <string, string>(); var attributes = new Dictionary <string, string>(); if (ToolingVersion15) { attributes.Add("Version", package.Version.ToString()); } else { props.Add("Version", package.Version.ToString()); } if (!string.IsNullOrEmpty(package.Include)) { props.Add("IncludeAssets", package.Include); } if (!string.IsNullOrEmpty(package.Exclude)) { props.Add("ExcludeAssets", package.Exclude); } if (!string.IsNullOrEmpty(package.PrivateAssets)) { props.Add("PrivateAssets", package.PrivateAssets); } if (!string.IsNullOrEmpty(package.NoWarn)) { props.Add("NoWarn", package.NoWarn); } ProjectFileUtils.AddItem( xml, "PackageReference", package.Id, referenceFramework, props, attributes); } foreach (var project in frameworkInfo.ProjectReferences) { var referenceFramework = frameworkInfo.Framework; // Drop the conditional if it is not needed if (Frameworks.All(f => f.ProjectReferences.Contains(project))) { referenceFramework = NuGetFramework.AnyFramework; if (!addedToAllProjectReferences.Add(project)) { // Skip since this was already added continue; } } var props = new Dictionary <string, string> { { "Name", project.ProjectName }, { "Project", project.ProjectGuid.ToString() } }; if (!string.IsNullOrEmpty(project.ExcludeAssets)) { props.Add("IncludeAssets", project.ExcludeAssets); } if (!string.IsNullOrEmpty(project.ExcludeAssets)) { props.Add("ExcludeAssets", project.ExcludeAssets); } if (!string.IsNullOrEmpty(project.PrivateAssets)) { props.Add("PrivateAssets", project.PrivateAssets); } ProjectFileUtils.AddItem( xml, "ProjectReference", $"{project.ProjectPath}", referenceFramework, props, new Dictionary <string, string>()); } } // Add tool references foreach (var tool in DotnetCLIToolReferences) { var props = new Dictionary <string, string>(); var attributes = new Dictionary <string, string>(); if (ToolingVersion15) { attributes.Add("Version", tool.Version.ToString()); } else { props.Add("Version", tool.Version.ToString()); } ProjectFileUtils.AddItem( xml, "DotNetCliToolReference", $"{tool.Id}", NuGetFramework.AnyFramework, props, attributes); } } else { // Add all project references directly foreach (var project in Frameworks.SelectMany(f => f.ProjectReferences).Distinct()) { var props = new Dictionary <string, string> { { "Name", project.ProjectName }, { "Project", project.ProjectGuid.ToString() } }; if (!string.IsNullOrEmpty(project.ExcludeAssets)) { props.Add("IncludeAssets", project.ExcludeAssets); } if (!string.IsNullOrEmpty(project.ExcludeAssets)) { props.Add("ExcludeAssets", project.ExcludeAssets); } if (!string.IsNullOrEmpty(project.PrivateAssets)) { props.Add("PrivateAssets", project.PrivateAssets); } ProjectFileUtils.AddItem( xml, "ProjectReference", $"{project.ProjectPath}", NuGetFramework.AnyFramework, props, new Dictionary <string, string>()); } } return(xml); }
public XDocument GetXML() { var sampleCSProjPath = (Type == ProjectStyle.PackageReference && ToolingVersion15) ? "Test.Utility.compiler.resources.project2.csproj" : "Test.Utility.compiler.resources.project1.csproj"; var s = ResourceTestUtility.GetResource(sampleCSProjPath, typeof(SimpleTestProjectContext)); var xml = XDocument.Parse(s); // MSBuildProjectExtensionsPath needs to be set before Microsoft.Common.props is imported, so add a new // PropertyGroup as the first element under the Project var ns = xml.Root.GetDefaultNamespace(); if (SetMSBuildProjectExtensionsPath) { var propertyGroup = new XElement(ns + "PropertyGroup"); propertyGroup.Add(new XElement(ns + "MSBuildProjectExtensionsPath", OutputPath)); xml.Root.AddFirst(propertyGroup); } ProjectFileUtils.AddProperties(xml, new Dictionary <string, string>() { { "ProjectGuid", "{" + ProjectGuid.ToString() + "}" }, { "AssemblyName", ProjectName } }); ProjectFileUtils.AddProperties(xml, Properties); if (Type == ProjectStyle.PackageReference) { if (WarningsAsErrors) { ProjectFileUtils.AddProperties(xml, new Dictionary <string, string>() { { "WarningsAsErrors", "true" } }); } ProjectFileUtils.AddProperties(xml, new Dictionary <string, string>() { { "Version", Version }, { "DebugType", "portable" } }); if (!IsLegacyPackageReference) { var tfPropName = SingleTargetFramework ? "TargetFramework" : "TargetFrameworks"; ProjectFileUtils.AddProperties(xml, new Dictionary <string, string>() { { tfPropName, OriginalFrameworkStrings.Count != 0 ? string.Join(";", OriginalFrameworkStrings): string.Join(";", Frameworks.Select(f => f.Framework.GetShortFolderName())) }, }); } var addedToAllProjectReferences = new HashSet <SimpleTestProjectContext>(); var addedToAllPackageReferences = new HashSet <SimpleTestPackageContext>(); var addedToAllPackageDownloads = new HashSet <SimpleTestPackageContext>(); foreach (var frameworkInfo in Frameworks) { // Add properties with a TFM condition ProjectFileUtils.AddProperties(xml, frameworkInfo.Properties, $" '$(TargetFramework)' == '{frameworkInfo.Framework.GetShortFolderName()}' "); foreach (var package in frameworkInfo.PackageReferences) { var referenceFramework = frameworkInfo.Framework; // Drop the conditional if it is not needed if (Frameworks.All(f => f.PackageReferences.Contains(package))) { referenceFramework = NuGetFramework.AnyFramework; if (!addedToAllPackageReferences.Add(package)) { // Skip since this was already added continue; } } var props = new Dictionary <string, string>(); var attributes = new Dictionary <string, string>(); // To support CPVM scenarios the Version can be null // In these cases do not write any version if (package.Version != null) { if (ToolingVersion15) { attributes.Add("Version", package.Version.ToString()); } else { props.Add("Version", package.Version.ToString()); } } if (!string.IsNullOrEmpty(package.Include)) { props.Add("IncludeAssets", package.Include); } if (!string.IsNullOrEmpty(package.Exclude)) { props.Add("ExcludeAssets", package.Exclude); } if (!string.IsNullOrEmpty(package.PrivateAssets)) { props.Add("PrivateAssets", package.PrivateAssets); } if (!string.IsNullOrEmpty(package.Aliases)) { props.Add("Aliases", package.Aliases); } if (!string.IsNullOrEmpty(package.NoWarn)) { props.Add("NoWarn", package.NoWarn); } ProjectFileUtils.AddItem( xml, "PackageReference", package.Id, referenceFramework, props, attributes); } foreach (var package in frameworkInfo.PackageDownloads) { var referenceFramework = frameworkInfo.Framework; // Drop the conditional if it is not needed if (Frameworks.All(f => f.PackageDownloads.Contains(package))) { referenceFramework = NuGetFramework.AnyFramework; if (!addedToAllPackageDownloads.Add(package)) { // Skip since this was already added continue; } } var props = new Dictionary <string, string>(); var attributes = new Dictionary <string, string>(); props.Add("Version", $"[{package.Version.ToString()}]"); ProjectFileUtils.AddItem( xml, "PackageDownload", package.Id, referenceFramework, props, attributes); } foreach (var project in frameworkInfo.ProjectReferences) { var referenceFramework = frameworkInfo.Framework; // Drop the conditional if it is not needed if (Frameworks.All(f => f.ProjectReferences.Contains(project))) { referenceFramework = NuGetFramework.AnyFramework; if (!addedToAllProjectReferences.Add(project)) { // Skip since this was already added continue; } } var props = new Dictionary <string, string> { { "Name", project.ProjectName }, { "Project", project.ProjectGuid.ToString() } }; if (!string.IsNullOrEmpty(project.ExcludeAssets)) { props.Add("IncludeAssets", project.ExcludeAssets); } if (!string.IsNullOrEmpty(project.ExcludeAssets)) { props.Add("ExcludeAssets", project.ExcludeAssets); } if (!string.IsNullOrEmpty(project.PrivateAssets)) { props.Add("PrivateAssets", project.PrivateAssets); } ProjectFileUtils.AddItem( xml, "ProjectReference", $"{project.ProjectPath}", referenceFramework, props, new Dictionary <string, string>()); } } // Add tool references foreach (var tool in DotnetCLIToolReferences) { var props = new Dictionary <string, string>(); var attributes = new Dictionary <string, string>(); if (ToolingVersion15) { attributes.Add("Version", tool.Version.ToString()); } else { props.Add("Version", tool.Version.ToString()); } ProjectFileUtils.AddItem( xml, "DotNetCliToolReference", $"{tool.Id}", NuGetFramework.AnyFramework, props, attributes); } } else { // Add all project references directly foreach (var project in Frameworks.SelectMany(f => f.ProjectReferences).Distinct()) { var props = new Dictionary <string, string> { { "Name", project.ProjectName }, { "Project", project.ProjectGuid.ToString() } }; if (!string.IsNullOrEmpty(project.ExcludeAssets)) { props.Add("IncludeAssets", project.ExcludeAssets); } if (!string.IsNullOrEmpty(project.ExcludeAssets)) { props.Add("ExcludeAssets", project.ExcludeAssets); } if (!string.IsNullOrEmpty(project.PrivateAssets)) { props.Add("PrivateAssets", project.PrivateAssets); } ProjectFileUtils.AddItem( xml, "ProjectReference", $"{project.ProjectPath}", NuGetFramework.AnyFramework, props, new Dictionary <string, string>()); } } return(xml); }
public override bool Execute() { /* This process does something similar to what ScanSwiftTask and IncludeSwiftTask does * but with caveats. * * The goal was to have a single code base for both macOS and Windows, but due to how * iOS compilation works on Windows it was not possible. * * When you do iOS specific compilation steps on Windows it is not actually running on * Windows, it is sending a message and required files to a build server on macOS that * runs and copy the results back to Windows when necessary. * * This messaging from Windows to macOS happens through a class called TaskRunner * that lives on Xamarin.Messaging... * * My first attempt to make the compilation work on Windows was to add a copy of * Xamarin.Messaging (or try reflection), access the TaskRunner and send the message * to macOS to run IncludeSwiftTask and ScanSwiftTask, but it turns out these classes are * nowhere to be found on macOS build agent - are all dependencies installed when * Xamarin first install the build agent or is there a way to inject code on demand? * I assumed the former was the case and stopped persuing this route as it was too * hacky and I couldnt find the code for Xamarin.Messaging dlls, all I had was * disassembled versions of those. * * The second attempt was to convert these Tasks into commands to be executed * by Xamarin version of Microsoft.Build.Tasks.Exec that has all the plubling necessary * to make the communication between macOS and Windows work, as it is pre-installed on * the build agent, but it didnt work. For some reason I was getting a * *"Exec" task received an invalid value for the "StdOutEncoding" parameter.* * while trying to read the command output. What?! * * Tried to save the ouput to a file that was going to be synchronized between macOS and * Windows, but only the file existence was, not its content. * * End result? I can only do one-way communication and send the entire thing as a single command. * * I need to say that even though it doenst work the same as macOS it is a huge * improvement over the original version - more on that on another comment. * * There are only 2 missing features: * * - There is no way to figure out what are the architectures available on the * current version of Swift on your Xcode, so I had to include a precomputed list. * Search for GetPlatformName method. macOS version learns this on demand so when * the day comes for a new architecture to be included I dont have to do anything * and it will just work. Windows version will require an update. * * - When searching for Swift dependencies on the Swift runtime the script is only * doing a single check, so if one day it happens that your Framework depends on A * that depends on B that depends on C and B is not a direct dependency from any of * your Frameworks and only B have a reference to C. C will be missing. I should * draw this, because dependency explanations are a pain to understand from text. * macOS version continue to scan all libraries it finds until there is nothing else * to scan. * * Sorry Windows users, at least for now I dont know what can be done to unify things! */ string args = GetLipoArgs(); var xcodePath = GetRuntimePath(); var sb = new StringBuilder(); // Scan app Frameworks for Swift Dependencies sb.Append("{ "); sb.Append(string.Join(" & ", Frameworks.Select(c => $"otool -l {c.ItemSpec}"))); sb.Append("; }"); // Clean and sort the results to get single, unique Swift dependency per line sb.Append(@" | grep @rpath/libswift"); sb.Append(@" | awk -Frpath/ '{print $2}' | awk -F..offset '{print $1}'"); //sb.Append(@" | sed -E 's/^.*@rpath.(.*.dylib).*$/\1/'"); // !#$!@# path translation sb.Append(@" | sort | uniq"); // Swift libraries can depend on another Swift library - this only goes 1 level deep // need to test if it will be an issue - macOS version doesnt work the same way. sb.Append(@" | while read line; do "); sb.Append($@"otool -l {xcodePath}/$line"); sb.Append(@" | grep @rpath/libswift"); sb.Append(@" | awk -Frpath/ '{print $2}' | awk -F..offset '{print $1}'"); //sb.Append(@" | sed -E 's/^.*@rpath.(.*.dylib).*$/\1/'"); // !#$!@# path translation sb.Append(@";done"); sb.Append(@" | sort | uniq"); // For each dependency found copy it to the /Frameworks folder // Removing the architectures we dont need for the final project sb.Append(@" | while read line; do "); sb.Append($@"lipo {xcodePath}/$line {args} {GetOutputPath(string.Empty)}/$line"); // TODO: how to make it parallel? sb.Append(@";done"); Command = new TaskItem(sb.ToString()); return(true); }