diff --git a/.gitignore b/.gitignore index b02321a54319..f152fefaf654 100644 --- a/.gitignore +++ b/.gitignore @@ -40,3 +40,6 @@ provision-xcode.csx mono_crash.*.json *.binlog .vscode +# Xcode +xcuserdata/ +.build/ diff --git a/docs/build-apps/build-items.md b/docs/build-apps/build-items.md new file mode 100644 index 000000000000..608836c94d40 --- /dev/null +++ b/docs/build-apps/build-items.md @@ -0,0 +1,39 @@ +--- +title: .NET for iOS, Mac Catalyst, macOS, and tvOS Build Items +description: .NET for iOS, Mac Catalyst, macOS, and tvOS Build Items +ms.date: 09/19/2024 +--- + +# Build Items + +Build items control how .NET for iOS, Mac Catalyst, macOS, and tvOS +application or library projects are built. + +## XcodeProject + +`` can be used to build and consume the outputs +of Xcode framework projects created in Xcode or elsewehere. + +The `Include` metadata should point to the path of the XCODEPROJ file to be built. + +```xml + + + +``` + +The following MSBuild metadata are supported: + +- `%(SchemeName)`: The name of the build scheme or target that should be used to build the project. + +- `%(Configuration)`: The name of the configuration to use to build the project. + The default value is `Release`. + +- `%(CreateNativeReference)`: Output XCFRAMEWORK files will be added as a `@(NativeReference)` to the project. + Metadata supported by `@(NativeReference)` like `%(Kind)`, `%(Frameworks)`, or `%(SmartLink)` will be forwarded if set. + The default value is `true`. + +- `%(OutputPath)`: Can be set to override the XCARCHIVE and XCFRAMEWORK output path of the Xcode project. + The default value is `$(IntermediateOutputPath)xcode/{SchemeName}-{Hash}`. + +This build action was introduced in .NET 9. diff --git a/docs/build-apps/build-properties.md b/docs/build-apps/build-properties.md new file mode 100644 index 000000000000..5a478bbcbe6d --- /dev/null +++ b/docs/build-apps/build-properties.md @@ -0,0 +1,32 @@ +--- +title: .NET for iOS, Mac Catalyst, macOS, and tvOS Build Properties +description: .NET for iOS, Mac Catalyst, macOS, and tvOS Build Properties +ms.date: 09/19/2024 +--- + +# Build Properties + +MSBuild properties control the behavior of the +[targets](build-targets.md). +They're specified within the project file, for example **MyApp.csproj**, within +an MSBuild PropertyGroup. + +## MaciOSPrepareForBuildDependsOn + +A semi-colon delimited property that can be used to extend the build process. +MSBuild targets added to this property will execute early in the build for both +application and library project types. This property is empty by default. + +Example: + +```xml + + MyCustomTarget + + + + + +``` + +This property was introduced in .NET 9. diff --git a/docs/build-apps/build-targets.md b/docs/build-apps/build-targets.md new file mode 100644 index 000000000000..e8dcf4fee7d4 --- /dev/null +++ b/docs/build-apps/build-targets.md @@ -0,0 +1,26 @@ +--- +title: .NET for iOS, Mac Catalyst, macOS, and tvOS Build Targets +description: .NET for iOS, Mac Catalyst, macOS, and tvOS Build Targets +ms.date: 09/19/2024 +--- + +# Build Targets + +The following build targets are defined in .NET for iOS, Mac Catalyst, macOS, and tvOS projects. + +## Build (Default) + +Builds the source code within a project and all dependencies. + +## Clean + +Removes all files generated by the build process. + +## Run + +Builds the source code within a project and all dependencies, and then deploys and runs it +on a default simulator/device. A specific deployment target can be set by using the `$(_DeviceName)` property. + +``` +dotnet build -t:Run project.csproj -p:_DeviceName=$(MY_DEVICE_UDID) +``` diff --git a/docs/native-library-interop.md b/docs/native-library-interop.md new file mode 100644 index 000000000000..eb70f789d4fe --- /dev/null +++ b/docs/native-library-interop.md @@ -0,0 +1,30 @@ +# NativeLibraryInterop + +## Overview +Native Library Interop (formerly referred to as the "Slim Binding" approach), refers to a +pattern for accessing native SDKs in .NET for iOS, Mac Catalyst, macOS, and tvOS projects. + +Starting in .NET 9, the .NET for iOS, Mac Catalyst, macOS, and tvOS SDKs support building +Xcode framework projects by using the `@(XcodeProjet)` build action. This is declared in +an MSBuild ItemGroup in a project file: + +```xml + + + +``` + +When an `@(XcodeProject)` item is added to a .NET for iOS, Mac Catalyst, macOS, or tvOS binding project, +the build process will attempt to create an XCFramework from the specified Xcode project. The XCFramework +output will be added as a `@(NativeReference)` to the .NET project so that it can be bound and have its +API surfaced via an [API definition][0] file. + +Please see the [build-items](build-apps/build-items.md) docs for more information about +the `@(XcodeProjet)` build action. + +Additional documentation and references can be found below: + +* https://learn.microsoft.com/en-us/dotnet/communitytoolkit/maui/native-library-interop +* https://github.com/CommunityToolkit/Maui.NativeLibraryInterop + +[0]: https://learn.microsoft.com/en-us/previous-versions/xamarin/cross-platform/macios/binding/objective-c-libraries?tabs=macos#The_API_definition_file diff --git a/dotnet/Makefile b/dotnet/Makefile index 573518a1d320..6e54ef326dcf 100644 --- a/dotnet/Makefile +++ b/dotnet/Makefile @@ -41,6 +41,7 @@ $(1)_NUGET_TARGETS = \ $(DOTNET_DESTDIR)/$($(1)_NUGET_SDK_NAME)/targets/Xamarin.Shared.Sdk.Trimming.props \ $(DOTNET_DESTDIR)/$($(1)_NUGET_SDK_NAME)/targets/Xamarin.Shared.Sdk.targets \ $(DOTNET_DESTDIR)/$($(1)_NUGET_SDK_NAME)/targets/dotnet-xcsync.targets \ + $(DOTNET_DESTDIR)/$($(1)_NUGET_SDK_NAME)/targets/Microsoft.MaciOS.Sdk.Xcode.targets \ endef $(foreach platform,$(DOTNET_PLATFORMS),$(eval $(call DefineTargets,$(platform)))) diff --git a/dotnet/targets/Microsoft.MaciOS.Sdk.Xcode.targets b/dotnet/targets/Microsoft.MaciOS.Sdk.Xcode.targets new file mode 100644 index 000000000000..cea2b1dfa146 --- /dev/null +++ b/dotnet/targets/Microsoft.MaciOS.Sdk.Xcode.targets @@ -0,0 +1,146 @@ + + + + + + + + + <_XcodeProjectDefaultOutputPathRoot>$(MSBuildProjectDirectory)/$(IntermediateOutputPath)xcode/ + <_BuildXcodeProjectsStamp>$(_MaciOSStampDirectory)_BuildXcodeProjects.stamp + + + + + Release + true + + + Framework + + + + + + + + + + + <_AllXcbFiles Include="%(XcodeProject.RootDir)%(XcodeProject.Directory)/**/*" + Condition= " Exists('%(XcodeProject.RootDir)%(XcodeProject.Directory)') "/> + <_XcbInputs Include="@(_AllXcbFiles)" + Condition=" '%(Extension)' == '.swift' + or '%(Extension)' == '.h' + or '%(Extension)' == '.pbxproj' + or '%(Extension)' == '.xcworkspace' " /> + <_XcbInputs Include="$(MSBuildProjectFullPath)" /> + + + + + + + + + + $(_XcodeProjectDefaultOutputPathRoot)%(SchemeName)-$([System.String]::Copy($(_XcodeProjectHash)).Substring(0, 5))/ + + + + + + + + + + <_XcArchivePlatform Condition=" '$(TargetPlatformIdentifier)' == 'maccatalyst' ">generic/platform=macOS,variant=Mac Catalyst + <_XcArchivePlatform Condition=" '$(TargetPlatformIdentifier)' == 'tvos' ">generic/platform=iOS + <_XcArchivePlatform Condition=" '$(_XcArchivePlatform)' == '' ">generic/platform=$(TargetPlatformIdentifier) + + + + + + + + + + + + + + + + + <_XcodeProjectXcFrameworkOutputs Include="%(XcodeProject.OutputPath)xcframeworks/%(XcodeProject.SchemeName)$(TargetPlatformIdentifier).xcframework" + Condition=" '%(CreateNativeReference)' == 'true' " + ForceLoad="%(ForceLoad)" + Frameworks="%(Frameworks)" + Kind="%(Kind)" + SmartLink="%(SmartLink)" + Visible="%(Visible)" /> + + + + + + + + + + + + + + + + + + + diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets index 293b547d7547..8599839ca8f3 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.targets @@ -14,6 +14,7 @@ + <_CanOutputAppBundle Condition="'$(_CanOutputAppBundle)' == '' And ('$(OutputType)' == 'Exe' Or '$(IsAppExtension)' == 'true' Or '$(IsWatchApp)' == 'true')">true <_CanOutputAppBundle Condition="'$(_CanOutputAppBundle)' == ''">false + + + <_MaciOSStampDirectory>$(IntermediateOutputPath)stamp\ @@ -186,6 +190,8 @@ BuildOnlySettings; + MaciOSPrepareForBuild; + _BuildXcodeProjects; _CollectBundleResources; _PackLibraryResources; $(BuildDependsOn); @@ -197,6 +203,8 @@ _WarnRuntimeIdentifiersClash; _ComputePublishTrimmed; BuildOnlySettings; + MaciOSPrepareForBuild; + _BuildXcodeProjects; _CollectBundleResources; _PackLibraryResources; _UnpackLibraryResources; @@ -213,6 +221,8 @@ _ErrorRuntimeIdentifiersClash; _ComputePublishTrimmed; BuildOnlySettings; + MaciOSPrepareForBuild; + _BuildXcodeProjects; _CollectBundleResources; _PackLibraryResources; _UnpackLibraryResources; @@ -286,6 +296,15 @@ + + + + + @@ -2456,4 +2475,6 @@ global using nfloat = global::System.Runtime.InteropServices.NFloat%3B + + diff --git a/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx b/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx index 4c6fb887fd07..c67d34d77480 100644 --- a/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx +++ b/msbuild/Xamarin.Localization.MSBuild/MSBStrings.resx @@ -1576,4 +1576,20 @@ RuntimeIdentifier: don't translate (it's the name of a MSBuild property) + + Adding reference to Xcode project output: '{0}'. The '%(CreateNativeReference)' metadata can be set to 'false' to opt out of this behavior. + + The following are literal names and should not be translated: Xcode, '%(CreateNativeReference)', metadata, 'false' + {0}: The path to the XCFramework output that will be referenced + + + + + The Xcode project item: '{0}' could not be found. Please update the 'Include' value to a path containing a valid '.xcodeproj' file. + + The following are literal names and should not be translated: Xcode, 'Include', '.xcodeproj' + {0}: An invalid path to an Xcode project file + + + diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateXcArchive.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateXcArchive.cs new file mode 100644 index 000000000000..1a30bf71e968 --- /dev/null +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateXcArchive.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; +using System.Collections.Generic; + +using Microsoft.Build.Framework; + +using Xamarin.Localization.MSBuild; + +namespace Xamarin.MacDev.Tasks { + public class CreateXcArchive : XcodeBuildTask { + + // Task input parameters + public string ProjectPath { get; set; } = string.Empty; + + public string SchemeName { get; set; } = string.Empty; + + public string Configuration { get; set; } = string.Empty; + + public string ArchivePlatform { get; set; } = string.Empty; + + public string DerivedDataPath { get; set; } = string.Empty; + + public string PackageCachePath { get; set; } = string.Empty; + + + readonly string [] archive_args = new string [] { + "BUILD_LIBRARY_FOR_DISTRIBUTION=YES", "ENABLE_BITCODE=NO", "OBJC_CFLAGS=\"-fno-objc-msgsend-selector-stubs -ObjC\"", + "OTHER_LDFLAGS=\"-ObjC\"", "OTHER_SWIFT_FLAGS=\"-no-verify-emitted-module-interface\"", + "SKIP_INSTALL=NO", "SWIFT_INSTALL_OBJC_HEADER=YES", + }; + + protected override string Command { get; set; } = "archive"; + + protected override IList GenerateCommandLineCommands () + { + var args = new List (); + + if (!string.IsNullOrEmpty (ProjectPath)) { + args.Add ("-project"); + args.Add (ProjectPath); + } + + if (!string.IsNullOrEmpty (SchemeName)) { + args.Add ("-scheme"); + args.Add (SchemeName); + } + + if (!string.IsNullOrEmpty (Configuration)) { + args.Add ("-configuration"); + args.Add (Configuration); + } + + if (!string.IsNullOrEmpty (ArchivePlatform)) { + args.Add ("-destination"); + args.Add (ArchivePlatform); + } + + if (!string.IsNullOrEmpty (OutputPath)) { + args.Add ("-archivePath"); + args.Add (OutputPath); + } + + if (!string.IsNullOrEmpty (DerivedDataPath)) { + args.Add ("-derivedDataPath"); + args.Add (DerivedDataPath); + } + + if (!string.IsNullOrEmpty (PackageCachePath)) { + args.Add ("-packageCachePath"); + args.Add (PackageCachePath); + } + + args.AddRange (archive_args); + + return args; + } + + public override bool Execute () + { + if (!Directory.Exists (ProjectPath)) { + Log.LogError (MSBStrings.XcodeBuild_InvalidItem, ProjectPath); + return false; + } + + return base.Execute (); + } + + } +} diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateXcFramework.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateXcFramework.cs new file mode 100644 index 000000000000..f7ab3fa48d74 --- /dev/null +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/CreateXcFramework.cs @@ -0,0 +1,42 @@ +using System; +using System.IO; +using System.Collections.Generic; + +using Microsoft.Build.Framework; + +namespace Xamarin.MacDev.Tasks { + public class CreateXcFramework : XcodeBuildTask { + + // Task input parameters + [Required] + public string XcArchivePath { get; set; } = string.Empty; + + [Required] + public string FrameworkName { get; set; } = string.Empty; + + + protected override string Command { get; set; } = "-create-xcframework"; + + protected override IList GenerateCommandLineCommands () + { + var args = new List (); + + if (Directory.Exists (XcArchivePath)) { + foreach (var frameworkArchive in Directory.EnumerateDirectories (XcArchivePath, "*.xcarchive")) { + args.Add ("-archive"); + args.Add (frameworkArchive); + args.Add ("-framework"); + args.Add (FrameworkName); + } + } + + if (!string.IsNullOrEmpty (OutputPath)) { + args.Add ("-output"); + args.Add (OutputPath); + } + + return args; + } + + } +} diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/MacDevMessage.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/MacDevMessage.cs new file mode 100644 index 000000000000..ed3a18e9d915 --- /dev/null +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/MacDevMessage.cs @@ -0,0 +1,36 @@ +using System; +using System.Resources; + +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; + +using Xamarin.Localization.MSBuild; + +namespace Xamarin.MacDev.Tasks { + + /// + /// Provides a localizable way to log a message from an MSBuild target. + /// + public class MacDevMessage : Task { + + /// + /// The name of the resource from Properties\Resources.resx that contains the message + /// + [Required] + public string ResourceName { get; set; } = string.Empty; + + /// + /// The string format arguments to use for any numbered format items in the resource provided by ResourceName + /// + public string [] FormatArguments { get; set; } = Array.Empty (); + + public override bool Execute () + { + Log.LogMessage ( + MSBStrings.ResourceManager.GetString (ResourceName, MSBStrings.Culture), + FormatArguments + ); + return true; + } + } +} diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/XcodeBuildTask.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/XcodeBuildTask.cs new file mode 100644 index 000000000000..dff2c77c4f0f --- /dev/null +++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/XcodeBuildTask.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; + +namespace Xamarin.MacDev.Tasks { + public abstract class XcodeBuildTask : XamarinTask { + + // Task input parameters + public string SdkDevPath { get; set; } = string.Empty; + + public string WorkingDirectory { get; set; } = string.Empty; + + public string OutputPath { get; set; } = string.Empty; + + + protected virtual string Command { get; set; } = string.Empty; + + protected virtual IList GenerateCommandLineCommands () => new List (); + + public override bool Execute () + { + var args = new List (); + args.Add ("xcodebuild"); + if (!string.IsNullOrEmpty (Command)) { + args.Add (Command); + } + args.AddRange (GenerateCommandLineCommands ()); + + ExecuteAsync ("xcrun", args, sdkDevPath: SdkDevPath, mergeOutput: false, workingDirectory: WorkingDirectory).Wait (); + return !Log.HasLoggedErrors; + } + + } +} diff --git a/msbuild/Xamarin.Shared/Xamarin.Shared.targets b/msbuild/Xamarin.Shared/Xamarin.Shared.targets index 06c0a01265f3..063918d55c9e 100644 --- a/msbuild/Xamarin.Shared/Xamarin.Shared.targets +++ b/msbuild/Xamarin.Shared/Xamarin.Shared.targets @@ -299,6 +299,7 @@ Copyright (C) 2018 Microsoft. All rights reserved. _CleanDeviceSpecificOutput; _CleanIntermediateToolOutput; _CleanITunesArtwork; + _CleanXcodeProjects; diff --git a/tests/common/TestProjects/Xcode/TemplateFx/TemplateFx.xcodeproj/project.pbxproj b/tests/common/TestProjects/Xcode/TemplateFx/TemplateFx.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..d2bb505649ee --- /dev/null +++ b/tests/common/TestProjects/Xcode/TemplateFx/TemplateFx.xcodeproj/project.pbxproj @@ -0,0 +1,372 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + D01A22F32C9CBF9C0090773D /* TemplateFx.h in Headers */ = {isa = PBXBuildFile; fileRef = D01A22F22C9CBF9C0090773D /* TemplateFx.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D08E31682C9CD1AD00899062 /* SwiftBindTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08E31672C9CD1AD00899062 /* SwiftBindTest.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + D01A22EF2C9CBF9C0090773D /* TemplateFx.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TemplateFx.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D01A22F22C9CBF9C0090773D /* TemplateFx.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TemplateFx.h; sourceTree = ""; }; + D08E31672C9CD1AD00899062 /* SwiftBindTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftBindTest.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D01A22EC2C9CBF9C0090773D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + D01A22E52C9CBF9C0090773D = { + isa = PBXGroup; + children = ( + D01A22F12C9CBF9C0090773D /* TemplateFx */, + D01A22F02C9CBF9C0090773D /* Products */, + ); + sourceTree = ""; + }; + D01A22F02C9CBF9C0090773D /* Products */ = { + isa = PBXGroup; + children = ( + D01A22EF2C9CBF9C0090773D /* TemplateFx.framework */, + ); + name = Products; + sourceTree = ""; + }; + D01A22F12C9CBF9C0090773D /* TemplateFx */ = { + isa = PBXGroup; + children = ( + D08E31672C9CD1AD00899062 /* SwiftBindTest.swift */, + D01A22F22C9CBF9C0090773D /* TemplateFx.h */, + ); + path = TemplateFx; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + D01A22EA2C9CBF9C0090773D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D01A22F32C9CBF9C0090773D /* TemplateFx.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + D01A22EE2C9CBF9C0090773D /* TemplateFx */ = { + isa = PBXNativeTarget; + buildConfigurationList = D01A22F62C9CBF9C0090773D /* Build configuration list for PBXNativeTarget "TemplateFx" */; + buildPhases = ( + D01A22EA2C9CBF9C0090773D /* Headers */, + D01A22EB2C9CBF9C0090773D /* Sources */, + D01A22EC2C9CBF9C0090773D /* Frameworks */, + D01A22ED2C9CBF9C0090773D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TemplateFx; + productName = TemplateFx; + productReference = D01A22EF2C9CBF9C0090773D /* TemplateFx.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D01A22E62C9CBF9C0090773D /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1540; + TargetAttributes = { + D01A22EE2C9CBF9C0090773D = { + CreatedOnToolsVersion = 15.4; + LastSwiftMigration = 1540; + }; + }; + }; + buildConfigurationList = D01A22E92C9CBF9C0090773D /* Build configuration list for PBXProject "TemplateFx" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = D01A22E52C9CBF9C0090773D; + productRefGroup = D01A22F02C9CBF9C0090773D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D01A22EE2C9CBF9C0090773D /* TemplateFx */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + D01A22ED2C9CBF9C0090773D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D01A22EB2C9CBF9C0090773D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D08E31682C9CD1AD00899062 /* SwiftBindTest.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + D01A22F42C9CBF9C0090773D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + D01A22F52C9CBF9C0090773D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + D01A22F72C9CBF9C0090773D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.5; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 14.5; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.TemplateFx; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + }; + name = Debug; + }; + D01A22F82C9CBF9C0090773D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.5; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 14.5; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.TemplateFx; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D01A22E92C9CBF9C0090773D /* Build configuration list for PBXProject "TemplateFx" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D01A22F42C9CBF9C0090773D /* Debug */, + D01A22F52C9CBF9C0090773D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D01A22F62C9CBF9C0090773D /* Build configuration list for PBXNativeTarget "TemplateFx" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D01A22F72C9CBF9C0090773D /* Debug */, + D01A22F82C9CBF9C0090773D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D01A22E62C9CBF9C0090773D /* Project object */; +} diff --git a/tests/common/TestProjects/Xcode/TemplateFx/TemplateFx/SwiftBindTest.swift b/tests/common/TestProjects/Xcode/TemplateFx/TemplateFx/SwiftBindTest.swift new file mode 100644 index 000000000000..f6cf22a666a8 --- /dev/null +++ b/tests/common/TestProjects/Xcode/TemplateFx/TemplateFx/SwiftBindTest.swift @@ -0,0 +1,18 @@ +// +// TemplateFx.swift +// TemplateFx +// + +import Foundation + +@objc(SwiftBindTest) +public class SwiftBindTest : NSObject +{ + @objc + public static func getString(myString: String) -> String { + return myString + " from swift!" + } + + //REPLACE + +} diff --git a/tests/common/TestProjects/Xcode/TemplateFx/TemplateFx/TemplateFx.h b/tests/common/TestProjects/Xcode/TemplateFx/TemplateFx/TemplateFx.h new file mode 100644 index 000000000000..1041a913859e --- /dev/null +++ b/tests/common/TestProjects/Xcode/TemplateFx/TemplateFx/TemplateFx.h @@ -0,0 +1,16 @@ +// +// TemplateFx.h +// TemplateFx +// + +#import + +//! Project version number for TemplateFx. +FOUNDATION_EXPORT double TemplateFxVersionNumber; + +//! Project version string for TemplateFx. +FOUNDATION_EXPORT const unsigned char TemplateFxVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/tests/common/TestProjects/Xcode/TwoSchemeFx/TwoSchemeFx.xcodeproj/project.pbxproj b/tests/common/TestProjects/Xcode/TwoSchemeFx/TwoSchemeFx.xcodeproj/project.pbxproj new file mode 100644 index 000000000000..cdd548aeadc1 --- /dev/null +++ b/tests/common/TestProjects/Xcode/TwoSchemeFx/TwoSchemeFx.xcodeproj/project.pbxproj @@ -0,0 +1,515 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + D022304A2C9CD08900CB72B4 /* TestClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = D02230492C9CD08900CB72B4 /* TestClass.swift */; }; + D06216732C9CBBB60057E268 /* TwoSchemeFx.h in Headers */ = {isa = PBXBuildFile; fileRef = D06216722C9CBBB60057E268 /* TwoSchemeFx.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D06216B92C9CBEDE0057E268 /* iOSFx.h in Headers */ = {isa = PBXBuildFile; fileRef = D06216B82C9CBEDE0057E268 /* iOSFx.h */; settings = {ATTRIBUTES = (Public, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + D02230492C9CD08900CB72B4 /* TestClass.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestClass.swift; sourceTree = ""; }; + D062166F2C9CBBB60057E268 /* TwoSchemeFx.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TwoSchemeFx.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D06216722C9CBBB60057E268 /* TwoSchemeFx.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TwoSchemeFx.h; sourceTree = ""; }; + D06216B62C9CBEDE0057E268 /* iOSFx.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = iOSFx.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D06216B82C9CBEDE0057E268 /* iOSFx.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = iOSFx.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D062166C2C9CBBB60057E268 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D06216B32C9CBEDE0057E268 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + D06216652C9CBBB60057E268 = { + isa = PBXGroup; + children = ( + D06216712C9CBBB60057E268 /* TwoSchemeFx */, + D06216B72C9CBEDE0057E268 /* iOSFx */, + D06216702C9CBBB60057E268 /* Products */, + ); + sourceTree = ""; + }; + D06216702C9CBBB60057E268 /* Products */ = { + isa = PBXGroup; + children = ( + D062166F2C9CBBB60057E268 /* TwoSchemeFx.framework */, + D06216B62C9CBEDE0057E268 /* iOSFx.framework */, + ); + name = Products; + sourceTree = ""; + }; + D06216712C9CBBB60057E268 /* TwoSchemeFx */ = { + isa = PBXGroup; + children = ( + D02230492C9CD08900CB72B4 /* TestClass.swift */, + D06216722C9CBBB60057E268 /* TwoSchemeFx.h */, + ); + path = TwoSchemeFx; + sourceTree = ""; + }; + D06216B72C9CBEDE0057E268 /* iOSFx */ = { + isa = PBXGroup; + children = ( + D06216B82C9CBEDE0057E268 /* iOSFx.h */, + ); + path = iOSFx; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + D062166A2C9CBBB60057E268 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D06216732C9CBBB60057E268 /* TwoSchemeFx.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D06216B12C9CBEDE0057E268 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + D06216B92C9CBEDE0057E268 /* iOSFx.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + D062166E2C9CBBB60057E268 /* TwoSchemeFx */ = { + isa = PBXNativeTarget; + buildConfigurationList = D06216762C9CBBB60057E268 /* Build configuration list for PBXNativeTarget "TwoSchemeFx" */; + buildPhases = ( + D062166A2C9CBBB60057E268 /* Headers */, + D062166B2C9CBBB60057E268 /* Sources */, + D062166C2C9CBBB60057E268 /* Frameworks */, + D062166D2C9CBBB60057E268 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = TwoSchemeFx; + productName = TwoSchemeFx; + productReference = D062166F2C9CBBB60057E268 /* TwoSchemeFx.framework */; + productType = "com.apple.product-type.framework"; + }; + D06216B52C9CBEDE0057E268 /* iOSFx */ = { + isa = PBXNativeTarget; + buildConfigurationList = D06216BA2C9CBEDE0057E268 /* Build configuration list for PBXNativeTarget "iOSFx" */; + buildPhases = ( + D06216B12C9CBEDE0057E268 /* Headers */, + D06216B22C9CBEDE0057E268 /* Sources */, + D06216B32C9CBEDE0057E268 /* Frameworks */, + D06216B42C9CBEDE0057E268 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iOSFx; + productName = iOSFx; + productReference = D06216B62C9CBEDE0057E268 /* iOSFx.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D06216662C9CBBB60057E268 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1540; + LastUpgradeCheck = 1540; + TargetAttributes = { + D062166E2C9CBBB60057E268 = { + CreatedOnToolsVersion = 15.4; + LastSwiftMigration = 1540; + }; + D06216B52C9CBEDE0057E268 = { + CreatedOnToolsVersion = 15.4; + }; + }; + }; + buildConfigurationList = D06216692C9CBBB60057E268 /* Build configuration list for PBXProject "TwoSchemeFx" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = D06216652C9CBBB60057E268; + productRefGroup = D06216702C9CBBB60057E268 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D062166E2C9CBBB60057E268 /* TwoSchemeFx */, + D06216B52C9CBEDE0057E268 /* iOSFx */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + D062166D2C9CBBB60057E268 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D06216B42C9CBEDE0057E268 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D062166B2C9CBBB60057E268 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D022304A2C9CD08900CB72B4 /* TestClass.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D06216B22C9CBEDE0057E268 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + D06216742C9CBBB60057E268 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + D06216752C9CBBB60057E268 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SWIFT_COMPILATION_MODE = wholemodule; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + D06216772C9CBBB60057E268 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.5; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 14.5; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.TwoSchemeFx; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + }; + name = Debug; + }; + D06216782C9CBBB60057E268 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALLOW_TARGET_PLATFORM_SPECIALIZATION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.5; + LD_RUNPATH_SEARCH_PATHS = ( + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = ( + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 14.5; + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.TwoSchemeFx; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = auto; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator macosx"; + SUPPORTS_MACCATALYST = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2,3"; + }; + name = Release; + }; + D06216BB2C9CBEDE0057E268 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.iOSFx; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + D06216BC2C9CBEDE0057E268 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 17.5; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.example.iOSFx; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + D06216692C9CBBB60057E268 /* Build configuration list for PBXProject "TwoSchemeFx" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D06216742C9CBBB60057E268 /* Debug */, + D06216752C9CBBB60057E268 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D06216762C9CBBB60057E268 /* Build configuration list for PBXNativeTarget "TwoSchemeFx" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D06216772C9CBBB60057E268 /* Debug */, + D06216782C9CBBB60057E268 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D06216BA2C9CBEDE0057E268 /* Build configuration list for PBXNativeTarget "iOSFx" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D06216BB2C9CBEDE0057E268 /* Debug */, + D06216BC2C9CBEDE0057E268 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D06216662C9CBBB60057E268 /* Project object */; +} diff --git a/tests/common/TestProjects/Xcode/TwoSchemeFx/TwoSchemeFx/TestClass.swift b/tests/common/TestProjects/Xcode/TwoSchemeFx/TwoSchemeFx/TestClass.swift new file mode 100644 index 000000000000..c35569a9c12a --- /dev/null +++ b/tests/common/TestProjects/Xcode/TwoSchemeFx/TwoSchemeFx/TestClass.swift @@ -0,0 +1,10 @@ +// +// TwoSchemeFx.swift +// TwoSchemeFx +// + +import Foundation + +class TestClass { + +} diff --git a/tests/common/TestProjects/Xcode/TwoSchemeFx/TwoSchemeFx/TwoSchemeFx.h b/tests/common/TestProjects/Xcode/TwoSchemeFx/TwoSchemeFx/TwoSchemeFx.h new file mode 100644 index 000000000000..2b469a458a6e --- /dev/null +++ b/tests/common/TestProjects/Xcode/TwoSchemeFx/TwoSchemeFx/TwoSchemeFx.h @@ -0,0 +1,16 @@ +// +// TwoSchemeFx.h +// TwoSchemeFx +// + +#import + +//! Project version number for TwoSchemeFx. +FOUNDATION_EXPORT double TwoSchemeFxVersionNumber; + +//! Project version string for TwoSchemeFx. +FOUNDATION_EXPORT const unsigned char TwoSchemeFxVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/tests/common/TestProjects/Xcode/TwoSchemeFx/iOSFx/iOSFx.h b/tests/common/TestProjects/Xcode/TwoSchemeFx/iOSFx/iOSFx.h new file mode 100644 index 000000000000..fc453651103c --- /dev/null +++ b/tests/common/TestProjects/Xcode/TwoSchemeFx/iOSFx/iOSFx.h @@ -0,0 +1,18 @@ +// +// iOSFx.h +// iOSFx +// +// Created by Peter Collins on 9/19/24. +// + +#import + +//! Project version number for iOSFx. +FOUNDATION_EXPORT double iOSFxVersionNumber; + +//! Project version string for iOSFx. +FOUNDATION_EXPORT const unsigned char iOSFxVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + + diff --git a/tests/dotnet/UnitTests/FileHelpers.cs b/tests/dotnet/UnitTests/FileHelpers.cs new file mode 100644 index 000000000000..60bd8c6ff143 --- /dev/null +++ b/tests/dotnet/UnitTests/FileHelpers.cs @@ -0,0 +1,27 @@ +using System.IO; + +#nullable enable + +namespace Xamarin.Tests { + public static class FileHelpers { + public static void CopyDirectory (string sourceDir, string destinationDir) + { + if (!Directory.Exists (sourceDir)) + throw new DirectoryNotFoundException ($"Unable to copy source dir, it was not found: {sourceDir}"); + + if (!Directory.Exists (destinationDir)) + Directory.CreateDirectory (destinationDir); + + foreach (var file in Directory.GetFiles (sourceDir)) { + var destFile = Path.Combine (destinationDir, Path.GetFileName (file)); + File.Copy (file, destFile, true); + } + + foreach (var subdir in Directory.GetDirectories (sourceDir)) { + var destSubdir = Path.Combine (destinationDir, Path.GetFileName (subdir)); + CopyDirectory (subdir, destSubdir); + } + } + + } +} diff --git a/tests/dotnet/UnitTests/IncrementalBuildTest.cs b/tests/dotnet/UnitTests/IncrementalBuildTest.cs index de1292558657..c19eacb42834 100644 --- a/tests/dotnet/UnitTests/IncrementalBuildTest.cs +++ b/tests/dotnet/UnitTests/IncrementalBuildTest.cs @@ -53,20 +53,5 @@ static int Main () Assert.That (File.GetLastWriteTimeUtc (executable), Is.GreaterThan (timestamp), "B: Executable modified"); } - void AssertTargetExecuted (IEnumerable executedTargets, string targetName, string message) - { - var targets = executedTargets.Where (v => v.TargetName == targetName); - if (!targets.Any ()) - Assert.Fail ($"The target '{targetName}' was not executed: no corresponding targets found in binlog ({message})"); - if (!targets.Any (v => !v.Skipped)) - Assert.Fail ($"The target '{targetName}' was not executed: the target was found {targets.Count ()} time(s) in the binlog, but they were all skipped ({message})"); - } - - void AssertTargetNotExecuted (IEnumerable executedTargets, string targetName, string message) - { - var targets = executedTargets.Where (v => v.TargetName == targetName); - if (targets.Any (v => !v.Skipped)) - Assert.Fail ($"The target '{targetName}' was unexpectedly executed ({message})"); - } } } diff --git a/tests/dotnet/UnitTests/PreBuildTest.cs b/tests/dotnet/UnitTests/PreBuildTest.cs new file mode 100644 index 000000000000..78e926314269 --- /dev/null +++ b/tests/dotnet/UnitTests/PreBuildTest.cs @@ -0,0 +1,40 @@ +using System; + +using NUnit.Framework; + +using Microsoft.Build.Logging.StructuredLogger; + +namespace Xamarin.Tests { + public class PreBuildTest : TestBaseClass { + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacOSX)] + [TestCase (ApplePlatform.MacCatalyst)] + public void PrepareTarget (ApplePlatform platform) + { + Configuration.IgnoreIfIgnoredPlatform (platform); + var testDir = Cache.CreateTemporaryDirectory (TestName); + DotNet.AssertNew (testDir, $"{platform.AsString ().ToLower ()}", TestName); + testDir = Path.Combine (testDir, TestName); + var proj = Path.Combine (testDir, $"{TestName}.csproj"); + var prepareTargetContent = @" + + MyPrepareTarget + + + + +"; + var existingProjContent = File.ReadAllText (proj); + var newProjContent = existingProjContent.Replace ("", prepareTargetContent + ""); + File.WriteAllText (proj, newProjContent); + + var rv = DotNet.AssertBuild (proj); + var allTargets = BinLog.GetAllTargets (rv.BinLogPath); + AssertTargetExecuted (allTargets, "MyPrepareTarget", "MyPrepareTarget"); + } + + } +} diff --git a/tests/dotnet/UnitTests/TestBaseClass.cs b/tests/dotnet/UnitTests/TestBaseClass.cs index d345f9f50a06..7d58311359fd 100644 --- a/tests/dotnet/UnitTests/TestBaseClass.cs +++ b/tests/dotnet/UnitTests/TestBaseClass.cs @@ -1,10 +1,11 @@ #nullable enable - using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using Mono.Cecil; +using NUnit.Framework; + using Xamarin.MacDev; using Xamarin.Tests; @@ -15,6 +16,17 @@ public abstract class TestBaseClass { { "_BundlerVerbosity", "1" }, }; + readonly char [] invalidChars = { '{', '}', '(', ')', '$', ':', ';', '\"', '\'', ',', '=', '|' }; + protected string TestName { + get { + var result = TestContext.CurrentContext.Test.Name; + foreach (var c in invalidChars.Concat (Path.GetInvalidPathChars ().Concat (Path.GetInvalidFileNameChars ()))) { + result = result.Replace (c, '_'); + } + return result.Replace ("_", string.Empty); + } + } + protected static Dictionary GetDefaultProperties (string? runtimeIdentifiers = null, Dictionary? extraProperties = null) { var rv = new Dictionary (verbosity); @@ -510,6 +522,23 @@ public void AssertThatLinkerDidNotExecute (ExecutionResult result) Assert.That (output, Does.Not.Contain ("LinkerConfiguration:"), "Custom steps did not run as expected."); } + + public void AssertTargetExecuted (IEnumerable executedTargets, string targetName, string message) + { + var targets = executedTargets.Where (v => v.TargetName == targetName); + if (!targets.Any ()) + Assert.Fail ($"The target '{targetName}' was not executed: no corresponding targets found in binlog ({message})"); + if (!targets.Any (v => !v.Skipped)) + Assert.Fail ($"The target '{targetName}' was not executed: the target was found {targets.Count ()} time(s) in the binlog, but they were all skipped ({message})"); + } + + public void AssertTargetNotExecuted (IEnumerable executedTargets, string targetName, string message) + { + var targets = executedTargets.Where (v => v.TargetName == targetName); + if (targets.Any (v => !v.Skipped)) + Assert.Fail ($"The target '{targetName}' was unexpectedly executed ({message})"); + } + static bool? is_in_ci; public static bool IsInCI { get { diff --git a/tests/dotnet/UnitTests/XcodeProjectTests.cs b/tests/dotnet/UnitTests/XcodeProjectTests.cs new file mode 100644 index 000000000000..30a7485df8d8 --- /dev/null +++ b/tests/dotnet/UnitTests/XcodeProjectTests.cs @@ -0,0 +1,393 @@ +using System; +using System.Linq; +using System.Text; + +using NUnit.Framework; + +using Microsoft.Build.Logging.StructuredLogger; + +namespace Xamarin.Tests { + public class XcodeProjectTests : TestBaseClass { + + readonly string XCodeTestProjectDir = Path.Combine (Configuration.SourceRoot, "tests", "common", "TestProjects", "Xcode"); + + void AddXcodeProjectItem (string project, string path, Dictionary metadata) + { + string xcProjItem = $@" + + + {string.Join ("\n", metadata.Select (v => $"<{v.Key}>{v.Value}"))} + + +"; + var existingProjContent = File.ReadAllText (project); + var newProjContent = existingProjContent.Replace ("", xcProjItem + ""); + File.WriteAllText (project, newProjContent); + } + + void AssertXcFrameworkOutput (ApplePlatform platform, string testDir, string xcodeProjName, string config = "Debug") + { + if (platform == ApplePlatform.iOS || platform == ApplePlatform.TVOS) { + var expectedXcodeFxOutput = Path.Combine (testDir, "bin", config, platform.ToFramework (), $"{TestName}.resources", $"{xcodeProjName}{platform.AsString ()}.xcframework"); + Assert.That (expectedXcodeFxOutput, Does.Exist, $"Expected xcframework output '{expectedXcodeFxOutput}' did not exist."); + } else { + var resourcesZip = Path.Combine (testDir, "bin", config, platform.ToFramework (), $"{TestName}.resources.zip"); + Assert.Contains ($"{xcodeProjName}{platform.AsString ()}.xcframework/Info.plist", ZipHelpers.List (resourcesZip), + $"Expected xcframework output was not found in '{resourcesZip}'."); + } + } + + + [Test] + [TestCase ("Debug", "iossimulator-x64")] + [TestCase ("Release", "ios-arm64")] + public void BuildAppiOS (string projConfig, string rid) + { + var platform = ApplePlatform.iOS; + Configuration.IgnoreIfIgnoredPlatform (platform); + + var testDir = Cache.CreateTemporaryDirectory (TestName); + var proj = Path.Combine (testDir, $"{TestName}.csproj"); + DotNet.AssertNew (testDir, "ios"); + + var xcodeProjName = "TemplateFx"; + var xcodeProjDirSrc = Path.Combine (XCodeTestProjectDir, xcodeProjName); + var xcodeProjDirDest = Cache.CreateTemporaryDirectory ($"{TestName}XcProj"); + FileHelpers.CopyDirectory (xcodeProjDirSrc, xcodeProjDirDest); + AddXcodeProjectItem (proj, Path.Combine (xcodeProjDirDest, $"{xcodeProjName}.xcodeproj"), + new Dictionary { + { "SchemeName", xcodeProjName }, + }); + + var projProps = new Dictionary { + { "Configuration", projConfig }, + { "RuntimeIdentifier", rid }, + }; + DotNet.AssertBuild (proj, properties: projProps); + var appDir = Path.Combine (testDir, "bin", projConfig, platform.ToFramework (), rid, $"{TestName}.app"); + Assert.That (appDir, Does.Exist, $"Expected app dir '{appDir}' did not exist."); + var appContent = Directory.GetFiles (appDir, "*", SearchOption.AllDirectories); + var expectedAppOutput = Path.Combine (testDir, "bin", projConfig, platform.ToFramework (), rid, $"{TestName}.app", "Frameworks", $"{xcodeProjName}.framework", "Info.plist"); + Assert.Contains (expectedAppOutput, appContent, $"Expected framework output '{expectedAppOutput}' did not exist."); + + } + + [Test] + [TestCase ("Release", "Release", false)] + [TestCase ("Debug", "Relase", true)] + [TestCase ("Release", "Debug", true)] + public void BuildBindingiOS (string projConfig, string fxConfig, bool createNativeReference) + { + var platform = ApplePlatform.iOS; + Configuration.IgnoreIfIgnoredPlatform (platform); + + var testDir = Cache.CreateTemporaryDirectory (TestName); + var proj = Path.Combine (testDir, $"{TestName}.csproj"); + DotNet.AssertNew (testDir, "iosbinding"); + + var xcodeProjName = "TemplateFx"; + var xcodeProjDirSrc = Path.Combine (XCodeTestProjectDir, xcodeProjName); + var xcodeProjDirDest = Cache.CreateTemporaryDirectory ($"{TestName}XcProj"); + FileHelpers.CopyDirectory (xcodeProjDirSrc, xcodeProjDirDest); + AddXcodeProjectItem (proj, Path.Combine (xcodeProjDirDest, $"{xcodeProjName}.xcodeproj"), + new Dictionary { + { "Configuration", fxConfig }, + { "CreateNativeReference", createNativeReference.ToString () }, + { "SchemeName", xcodeProjName }, + }); + + var projProps = new Dictionary { + { "Configuration", projConfig }, + }; + DotNet.AssertBuild (proj, properties: projProps); + var expectedXcodeFxOutput = Path.Combine (testDir, "bin", projConfig, platform.ToFramework (), $"{TestName}.resources", $"{xcodeProjName}{platform.AsString ()}.xcframework"); + Assert.That (expectedXcodeFxOutput, createNativeReference ? Does.Exist : Does.Not.Exist, $"Expected xcframework output '{expectedXcodeFxOutput}' to exist when CreateNativeReference=true."); + } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacOSX)] + [TestCase (ApplePlatform.MacCatalyst)] + public void BuildBinding (ApplePlatform platform) + { + Configuration.IgnoreIfIgnoredPlatform (platform); + + var testDir = Cache.CreateTemporaryDirectory (TestName); + var proj = Path.Combine (testDir, $"{TestName}.csproj"); + DotNet.AssertNew (testDir, $"{platform.AsString ().ToLower ()}binding"); + + var xcodeProjName = "TemplateFx"; + var xcodeProjDirSrc = Path.Combine (XCodeTestProjectDir, xcodeProjName); + var xcodeProjDirDest = Cache.CreateTemporaryDirectory ($"{TestName}XcProj"); + FileHelpers.CopyDirectory (xcodeProjDirSrc, xcodeProjDirDest); + AddXcodeProjectItem (proj, Path.Combine (xcodeProjDirDest, $"{xcodeProjName}.xcodeproj"), + new Dictionary { + { "SchemeName", xcodeProjName }, + }); + + // Add API definition to expose the API from the xcodeproj + var apiDefinition = @" +using Foundation; +namespace XcProjBinding; +[BaseType (typeof(NSObject))] +interface SwiftBindTest { + [Static,Export (""getStringWithMyString:"")] + string GetString (string myString); +}"; + File.WriteAllText (Path.Combine (testDir, "ApiDefinition.cs"), apiDefinition); + + // Add class file to ensure ensure the xcframework from our xcodeproj was built and bound + string classContent = @" +public class Binding +{ + public void BindTest () { Console.WriteLine (XcProjBinding.SwiftBindTest.GetString(""test"")); } +} +"; + File.WriteAllText (Path.Combine (testDir, "Binding.cs"), classContent); + + var rv = DotNet.AssertBuild (proj); + var warnings = BinLog.GetBuildLogWarnings (rv.BinLogPath).Select (v => v.Message); + Assert.That (warnings, Is.Empty, $"Build warnings:\n\t{string.Join ("\n\t", warnings)}"); + AssertXcFrameworkOutput (platform, testDir, xcodeProjName); + } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacOSX)] + [TestCase (ApplePlatform.MacCatalyst)] + public void PackBinding (ApplePlatform platform) + { + Configuration.IgnoreIfIgnoredPlatform (platform); + + var testDir = Cache.CreateTemporaryDirectory (TestName); + var proj = Path.Combine (testDir, $"{TestName}.csproj"); + DotNet.AssertNew (testDir, $"{platform.AsString ().ToLower ()}binding"); + + var xcodeProjName = "TemplateFx"; + var xcodeProjDirSrc = Path.Combine (XCodeTestProjectDir, xcodeProjName); + var xcodeProjDirDest = Cache.CreateTemporaryDirectory ($"{TestName}XcProj"); + FileHelpers.CopyDirectory (xcodeProjDirSrc, xcodeProjDirDest); + AddXcodeProjectItem (proj, Path.Combine (xcodeProjDirDest, $"{xcodeProjName}.xcodeproj"), + new Dictionary { + { "SchemeName", xcodeProjName }, + }); + + DotNet.AssertPack (proj); + var expectedNupkgOutput = Path.Combine (testDir, "bin", "Release", $"{TestName}.1.0.0.nupkg"); + Assert.That (expectedNupkgOutput, Does.Exist, $"Expected pack output '{expectedNupkgOutput}' did not exist."); + + List zipContent = ZipHelpers.List (expectedNupkgOutput); + var expectedFxPath = $"lib/{platform.ToFrameworkWithPlatformVersion ()}/{TestName}.resources/{xcodeProjName}{platform.AsString ()}.xcframework/Info.plist"; + if (platform == ApplePlatform.MacOSX || platform == ApplePlatform.MacCatalyst) { + zipContent = ZipHelpers.ListInnerZip (expectedNupkgOutput, $"lib/{platform.ToFrameworkWithPlatformVersion ()}/{TestName}.resources.zip"); + expectedFxPath = $"{xcodeProjName}{platform.AsString ()}.xcframework/Info.plist"; + } + Assert.Contains (expectedFxPath, zipContent, $"Expected xcframework output was not found in '{expectedNupkgOutput}'."); + } + + [Test] + [TestCase (ApplePlatform.iOS)] + [TestCase (ApplePlatform.TVOS)] + [TestCase (ApplePlatform.MacOSX)] + [TestCase (ApplePlatform.MacCatalyst)] + public void BuildIncremental (ApplePlatform platform) + { + Configuration.IgnoreIfIgnoredPlatform (platform); + + var testDir = Cache.CreateTemporaryDirectory (TestName); + var proj = Path.Combine (testDir, $"{TestName}.csproj"); + DotNet.AssertNew (testDir, $"{platform.AsString ().ToLower ()}binding"); + + var xcodeProjName = "TemplateFx"; + var xcodeProjDirSrc = Path.Combine (XCodeTestProjectDir, xcodeProjName); + var xcodeProjDirDest = Cache.CreateTemporaryDirectory ($"{TestName}XcProj"); + var xcodeProjPath = Path.Combine (xcodeProjDirDest, $"{xcodeProjName}.xcodeproj"); + FileHelpers.CopyDirectory (xcodeProjDirSrc, xcodeProjDirDest); + AddXcodeProjectItem (proj, xcodeProjPath, + new Dictionary { + { "SchemeName", xcodeProjName }, + }); + + // Build the first time + var rv = DotNet.AssertBuild (proj); + var allTargets = BinLog.GetAllTargets (rv.BinLogPath); + AssertTargetExecuted (allTargets, "_BuildXcodeProjects", "First _BuildXcodeProjects"); + var expectedXcodeFxOutput = Path.Combine (testDir, "bin", "Debug", platform.ToFramework (), $"{TestName}.resources", $"{xcodeProjName}{platform.AsString ().ToLower ()}.xcframework"); + if (platform == ApplePlatform.MacOSX || platform == ApplePlatform.MacCatalyst) + expectedXcodeFxOutput = Path.Combine (testDir, "bin", "Debug", platform.ToFramework (), $"{TestName}.resources.zip"); + Assert.That (expectedXcodeFxOutput, Does.Exist, $"Expected xcframework output '{expectedXcodeFxOutput}' did not exist."); + var outputFxFirstWriteTime = File.GetLastWriteTime (expectedXcodeFxOutput); + + // Build again, _BuildXcodeProjects should be skipped and outputs should not be updated + rv = DotNet.AssertBuild (proj); + allTargets = BinLog.GetAllTargets (rv.BinLogPath); + AssertTargetNotExecuted (allTargets, "_BuildXcodeProjects", "Second _BuildXcodeProjects"); + Assert.That (expectedXcodeFxOutput, Does.Exist, $"Expected xcframework output '{expectedXcodeFxOutput}' did not exist."); + var outputFxSecondWriteTime = File.GetLastWriteTime (expectedXcodeFxOutput); + Assert.That (outputFxFirstWriteTime, Is.EqualTo (outputFxSecondWriteTime), $"Expected '{expectedXcodeFxOutput}' write time to be '{outputFxFirstWriteTime}', but was '{outputFxSecondWriteTime}'"); + + // Update xcode project, _BuildXcodeProjects should run and outputs should be updated + File.SetLastWriteTime (Path.Combine (xcodeProjPath, "project.pbxproj"), DateTime.Now); + rv = DotNet.AssertBuild (proj); + allTargets = BinLog.GetAllTargets (rv.BinLogPath); + AssertTargetExecuted (allTargets, "_BuildXcodeProjects", "Third _BuildXcodeProjects"); + Assert.That (expectedXcodeFxOutput, Does.Exist, $"Expected xcframework output '{expectedXcodeFxOutput}' did not exist."); + var outputFxThirdWriteTime = File.GetLastWriteTime (expectedXcodeFxOutput); + Assert.IsTrue (outputFxThirdWriteTime > outputFxFirstWriteTime, $"Expected '{outputFxThirdWriteTime}' write time of '{outputFxThirdWriteTime}' to be greater than first write '{outputFxFirstWriteTime}'"); + } + + [Test] + public void BuildMultipleSchemesiOS () + { + var platform = ApplePlatform.iOS; + Configuration.IgnoreIfIgnoredPlatform (platform); + + var testDir = Cache.CreateTemporaryDirectory (TestName); + var proj = Path.Combine (testDir, $"{TestName}.csproj"); + DotNet.AssertNew (testDir, "iosbinding"); + + var xcodeProjName = "TwoSchemeFx"; + var xcodeProjDirSrc = Path.Combine (XCodeTestProjectDir, xcodeProjName); + var xcodeProjDirDest = Cache.CreateTemporaryDirectory ($"{TestName}XcProj"); + FileHelpers.CopyDirectory (xcodeProjDirSrc, xcodeProjDirDest); + AddXcodeProjectItem (proj, Path.Combine (xcodeProjDirDest, $"{xcodeProjName}.xcodeproj"), + new Dictionary { + { "SchemeName", xcodeProjName }, + }); + + var secondSchemeName = "iOSFx"; + AddXcodeProjectItem (proj, Path.Combine (xcodeProjDirDest, $"{xcodeProjName}.xcodeproj"), + new Dictionary { + { "SchemeName", secondSchemeName }, + }); + + DotNet.AssertBuild (proj); + AssertXcFrameworkOutput (platform, testDir, xcodeProjName); + AssertXcFrameworkOutput (platform, testDir, secondSchemeName); + } + + [Test] + public void BuildMultipleProjectsiOS () + { + var platform = ApplePlatform.iOS; + Configuration.IgnoreIfIgnoredPlatform (platform); + + var testDir = Cache.CreateTemporaryDirectory (TestName); + var proj = Path.Combine (testDir, $"{TestName}.csproj"); + DotNet.AssertNew (testDir, "iosbinding"); + + var xcodeProjName = "TemplateFx"; + var xcodeProjDirSrc = Path.Combine (XCodeTestProjectDir, xcodeProjName); + var xcodeProjDirDest = Cache.CreateTemporaryDirectory ($"{TestName}XcProj"); + FileHelpers.CopyDirectory (xcodeProjDirSrc, xcodeProjDirDest); + AddXcodeProjectItem (proj, Path.Combine (xcodeProjDirDest, $"{xcodeProjName}.xcodeproj"), + new Dictionary { + { "SchemeName", xcodeProjName }, + }); + + var xcodeProjName2 = "TwoSchemeFx"; + var xcodeProjDirSrc2 = Path.Combine (XCodeTestProjectDir, xcodeProjName2); + var xcodeProjDirDest2 = Cache.CreateTemporaryDirectory ($"{TestName}XcProjTwo"); + FileHelpers.CopyDirectory (xcodeProjDirSrc2, xcodeProjDirDest2); + AddXcodeProjectItem (proj, Path.Combine (xcodeProjDirDest2, $"{xcodeProjName2}.xcodeproj"), + new Dictionary { + { "SchemeName", xcodeProjName2 }, + }); + + DotNet.AssertBuild (proj); + AssertXcFrameworkOutput (platform, testDir, xcodeProjName); + AssertXcFrameworkOutput (platform, testDir, xcodeProjName2); + } + + [Test] + [Category ("Multiplatform")] + public void BuildMultiTargeting () + { + var enabledPlatforms = Configuration.GetIncludedPlatforms (dotnet: true); + var templatePlatform = enabledPlatforms.First (); + var testDir = Cache.CreateTemporaryDirectory (TestName); + var proj = Path.Combine (testDir, $"{TestName}.csproj"); + DotNet.AssertNew (testDir, $"{templatePlatform.AsString ().ToLower ()}binding"); + + var tfxs = $"{string.Join (";", enabledPlatforms.Select (p => p.ToFramework ()))}"; + var existingProjContent = File.ReadAllText (proj); + var newProjContent = existingProjContent.Replace ($"{templatePlatform.ToFramework ()}", tfxs); + File.WriteAllText (proj, newProjContent); + StringAssert.Contains (tfxs, File.ReadAllText (proj)); + + var xcodeProjName = "TemplateFx"; + var xcodeProjDirSrc = Path.Combine (XCodeTestProjectDir, xcodeProjName); + var xcodeProjDirDest = Cache.CreateTemporaryDirectory ($"{TestName}XcProj"); + FileHelpers.CopyDirectory (xcodeProjDirSrc, xcodeProjDirDest); + AddXcodeProjectItem (proj, Path.Combine (xcodeProjDirDest, $"{xcodeProjName}.xcodeproj"), + new Dictionary { + { "SchemeName", xcodeProjName }, + }); + + DotNet.AssertBuild (proj); + foreach (var platform in enabledPlatforms) { + AssertXcFrameworkOutput (platform, testDir, xcodeProjName); + } + } + + [Test] + public void InvalidItemErroriOS () + { + var platform = ApplePlatform.iOS; + Configuration.IgnoreIfIgnoredPlatform (platform); + + var testDir = Cache.CreateTemporaryDirectory (TestName); + var proj = Path.Combine (testDir, $"{TestName}.csproj"); + DotNet.AssertNew (testDir, "ioslib"); + + var xcodeProjName = "TemplateFx"; + var invalidXcodeProjPath = Path.Combine (testDir, "invalid", "path", $"{xcodeProjName}.xcodeproj"); + AddXcodeProjectItem (proj, invalidXcodeProjPath, + new Dictionary { + { "SchemeName", xcodeProjName }, + }); + + var rv = DotNet.AssertBuildFailure (proj); + var errors = BinLog.GetBuildLogErrors (rv.BinLogPath).ToArray (); + var expectedError = $"The Xcode project item: '{invalidXcodeProjPath}' could not be found. Please update the 'Include' value to a path containing a valid '.xcodeproj' file."; + AssertErrorMessages (errors, expectedError); + } + + [Test] + public void InvalidSchemeErroriOS () + { + var platform = ApplePlatform.iOS; + Configuration.IgnoreIfIgnoredPlatform (platform); + + var testDir = Cache.CreateTemporaryDirectory (TestName); + var proj = Path.Combine (testDir, $"{TestName}.csproj"); + DotNet.AssertNew (testDir, "ioslib"); + + var xcodeProjName = "TemplateFx"; + var invaldSchemeName = "InvalidScheme"; + var xcodeProjDirSrc = Path.Combine (XCodeTestProjectDir, xcodeProjName); + var xcodeProjDirDest = Cache.CreateTemporaryDirectory ($"{TestName}XcProj"); + FileHelpers.CopyDirectory (xcodeProjDirSrc, xcodeProjDirDest); + + AddXcodeProjectItem (proj, Path.Combine (xcodeProjDirDest, $"{xcodeProjName}.xcodeproj"), + new Dictionary { + { "SchemeName", invaldSchemeName }, + }); + + var rv = DotNet.AssertBuildFailure (proj); + var expectedErrorContent = $"xcodebuild: error: The project named \"{xcodeProjName}\" does not contain a scheme named \"{invaldSchemeName}\"."; + var errors = BinLog.GetBuildLogErrors (rv.BinLogPath).ToArray (); + AssertErrorMessages (errors, + new Func [] { + (msg) => msg?.Contains (expectedErrorContent) == true + }, + new Func [] { + () => expectedErrorContent + } + ); + } + + } +} diff --git a/tests/dotnet/UnitTests/ZipHelpers.cs b/tests/dotnet/UnitTests/ZipHelpers.cs index 292b0e66fe16..5a44d6b5465d 100644 --- a/tests/dotnet/UnitTests/ZipHelpers.cs +++ b/tests/dotnet/UnitTests/ZipHelpers.cs @@ -14,6 +14,18 @@ public static List List (string file) return zip.Entries.Select (entry => entry.FullName.TrimEnd ('/').Replace ('/', Path.DirectorySeparatorChar)).ToList (); } + public static List ListInnerZip (string file, string innerZipFileName) + { + using var zip = ZipFile.OpenRead (file); + var innerZipEntry = zip.GetEntry (innerZipFileName); + if (innerZipEntry is null) + return new List (); + + using Stream innerZipStream = innerZipEntry.Open (); + using ZipArchive innerZip = new ZipArchive (innerZipStream, ZipArchiveMode.Read); + return innerZip.Entries.Select (entry => entry.FullName.TrimEnd ('/').Replace ('/', Path.DirectorySeparatorChar)).ToList (); + } + public static void DumpZipFile (ZipArchive zip, string path) { #if TRACE