diff --git a/Make.config b/Make.config index ea876dab0895..b8469aefc669 100644 --- a/Make.config +++ b/Make.config @@ -225,14 +225,14 @@ EXTRA_SIMULATORS=com.apple.pkg.iPhoneSimulatorSDK12_4 com.apple.pkg.AppleTVSimul INCLUDE_IOS=1 INCLUDE_MAC=1 -INCLUDE_WATCH=1 -INCLUDE_TVOS=1 -INCLUDE_MACCATALYST=1 +INCLUDE_WATCH= +INCLUDE_TVOS= +INCLUDE_MACCATALYST= INCLUDE_DEVICE=1 INCLUDE_DOTNET_WATCHOS= INCLUDE_XAMARIN_LEGACY=1 -ENABLE_DOTNET=1 +ENABLE_DOTNET= # disable source code install by default (it's enabled for CI builds) ENABLE_INSTALL_SOURCE= diff --git a/mk/xamarin.mk b/mk/xamarin.mk index dea0a9d791e0..1d6ce8fdf9e2 100644 --- a/mk/xamarin.mk +++ b/mk/xamarin.mk @@ -7,8 +7,8 @@ MONO_BRANCH := $(shell cd $(MONO_PATH) 2> /dev/null && git symbolic-ref --sho endif ifdef ENABLE_XAMARIN -NEEDED_MACCORE_VERSION := 29a1c1382e005e568b57d1b45e3877c8081dfc8b -NEEDED_MACCORE_BRANCH := d17-2 +NEEDED_MACCORE_VERSION := f0121539680861877436991efcd14817eb678a54 +NEEDED_MACCORE_BRANCH := d17-2-dotnet-disabled-aware MACCORE_DIRECTORY := maccore MACCORE_MODULE := git@github.com:xamarin/maccore.git diff --git a/src/Foundation/NSUrlSessionHandler.cs b/src/Foundation/NSUrlSessionHandler.cs index 93e6eb3b4a01..4092e1cd6835 100644 --- a/src/Foundation/NSUrlSessionHandler.cs +++ b/src/Foundation/NSUrlSessionHandler.cs @@ -62,58 +62,6 @@ namespace Foundation { #endif public delegate bool NSUrlSessionHandlerTrustOverrideForUrlCallback (NSUrlSessionHandler sender, string url, SecTrust trust); - // useful extensions for the class in order to set it in a header - static class NSHttpCookieExtensions - { - static void AppendSegment (StringBuilder builder, string name, string? value) - { - if (builder.Length > 0) - builder.Append ("; "); - - builder.Append (name); - if (value is not null) - builder.Append ("=").Append (value); - } - - // returns the header for a cookie - public static string GetHeaderValue (this NSHttpCookie cookie) - { - var header = new StringBuilder(); - AppendSegment (header, cookie.Name, cookie.Value); - AppendSegment (header, NSHttpCookie.KeyPath.ToString (), cookie.Path.ToString ()); - AppendSegment (header, NSHttpCookie.KeyDomain.ToString (), cookie.Domain.ToString ()); - AppendSegment (header, NSHttpCookie.KeyVersion.ToString (), cookie.Version.ToString ()); - - if (cookie.Comment is not null) - AppendSegment (header, NSHttpCookie.KeyComment.ToString (), cookie.Comment.ToString()); - - if (cookie.CommentUrl is not null) - AppendSegment (header, NSHttpCookie.KeyCommentUrl.ToString (), cookie.CommentUrl.ToString()); - - if (cookie.Properties.ContainsKey (NSHttpCookie.KeyDiscard)) - AppendSegment (header, NSHttpCookie.KeyDiscard.ToString (), null); - - if (cookie.ExpiresDate is not null) { - // Format according to RFC1123; 'r' uses invariant info (DateTimeFormatInfo.InvariantInfo) - var dateStr = ((DateTime) cookie.ExpiresDate).ToUniversalTime ().ToString("r", CultureInfo.InvariantCulture); - AppendSegment (header, NSHttpCookie.KeyExpires.ToString (), dateStr); - } - - if (cookie.Properties.ContainsKey (NSHttpCookie.KeyMaximumAge)) { - var timeStampString = (NSString) cookie.Properties[NSHttpCookie.KeyMaximumAge]; - AppendSegment (header, NSHttpCookie.KeyMaximumAge.ToString (), timeStampString); - } - - if (cookie.IsSecure) - AppendSegment (header, NSHttpCookie.KeySecure.ToString(), null); - - if (cookie.IsHttpOnly) - AppendSegment (header, "httponly", null); // Apple does not show the key for the httponly - - return header.ToString (); - } - } - public partial class NSUrlSessionHandler : HttpMessageHandler { private const string SetCookie = "Set-Cookie"; @@ -152,7 +100,7 @@ public NSUrlSessionHandler () : this (CreateConfig ()) public NSUrlSessionHandler (NSUrlSessionConfiguration configuration) { if (configuration is null) - throw new ArgumentNullException (nameof (configuration)); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (configuration)); // HACK: we need to store the following because session.Configuration gets a copy of the object and the value gets lost sessionType = configuration.SessionType; @@ -578,7 +526,7 @@ public int MaxAutomaticRedirections { set { // I believe it's possible to implement support for MaxAutomaticRedirections (it just has to be done) if (value != int.MaxValue) - throw new ArgumentOutOfRangeException (nameof (value), value, "It's not possible to lower the max number of automatic redirections.");; + ObjCRuntime.ThrowHelper.ThrowArgumentOutOfRangeException (nameof (value), value, "It's not possible to lower the max number of automatic redirections.");; } } @@ -669,7 +617,7 @@ public bool UseProxy { get => true; set { if (!value) - throw new ArgumentOutOfRangeException (nameof (value), value, "It's not possible to disable the use of system proxies.");; + ObjCRuntime.ThrowHelper.ThrowArgumentOutOfRangeException (nameof (value), value, "It's not possible to disable the use of system proxies.");; } } #endif // NET @@ -1083,10 +1031,10 @@ public MonoStreamContent (Stream content) public MonoStreamContent (Stream content, int bufferSize) { if (content is null) - throw new ArgumentNullException ("content"); + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (content)); if (bufferSize <= 0) - throw new ArgumentOutOfRangeException ("bufferSize"); + ObjCRuntime.ThrowHelper.ThrowArgumentOutOfRangeException (nameof (bufferSize), bufferSize, "Buffer size must be >0"); this.content = content; this.bufferSize = bufferSize; diff --git a/src/Foundation/NSUrlSessionHandler/MonoStreamContent.cs b/src/Foundation/NSUrlSessionHandler/MonoStreamContent.cs new file mode 100644 index 000000000000..e4dc84f6999f --- /dev/null +++ b/src/Foundation/NSUrlSessionHandler/MonoStreamContent.cs @@ -0,0 +1,111 @@ +// +// Copied from https://github.com/mono/mono/blob/2019-02/mcs/class/System.Net.Http/System.Net.Http/StreamContent.cs. +// +// This is not a perfect solution, but the most robust and risk-free approach. +// +// The implementation depends on Mono-specific behavior, which makes SerializeToStreamAsync() cancellable. +// Unfortunately, the CoreFX implementation of HttpClient does not support this. +// +// By copying Mono's old implementation here, we ensure that we're compatible with both HttpClient implementations, +// so when we eventually adopt the CoreFX version in all of Mono's profiles, we don't regress here. +// +using System; +using System.Net; +using System.Net.Http; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +#nullable enable + +#if !MONOMAC +namespace System.Net.Http { +#else +namespace Foundation { +#endif + + class MonoStreamContent : HttpContent + { + readonly Stream content; + readonly int bufferSize; + readonly CancellationToken cancellationToken; + readonly long startPosition; + bool contentCopied; + + public MonoStreamContent (Stream content) + : this (content, 16 * 1024) + { + } + + public MonoStreamContent (Stream content, int bufferSize) + { + if (content is null) + ObjCRuntime.ThrowHelper.ThrowArgumentNullException (nameof (content)); + + if (bufferSize <= 0) + ObjCRuntime.ThrowHelper.ThrowArgumentOutOfRangeException (nameof (bufferSize), bufferSize, "Buffer size must be >0"); + + this.content = content; + this.bufferSize = bufferSize; + + if (content.CanSeek) { + startPosition = content.Position; + } + } + + // + // Workarounds for poor .NET API + // Instead of having SerializeToStreamAsync with CancellationToken as public API. Only LoadIntoBufferAsync + // called internally from the send worker can be cancelled and user cannot see/do it + // + internal MonoStreamContent (Stream content, CancellationToken cancellationToken) + : this (content) + { + // We don't own the token so don't worry about disposing it + this.cancellationToken = cancellationToken; + } + + protected override Task CreateContentReadStreamAsync () + { + return Task.FromResult (content); + } + + protected override void Dispose (bool disposing) + { + if (disposing) { + content.Dispose (); + } + + base.Dispose (disposing); + } + + protected override Task SerializeToStreamAsync (Stream stream, TransportContext? context) + { + if (contentCopied) { + if (!content.CanSeek) { + throw new InvalidOperationException ("The stream was already consumed. It cannot be read again."); + } + + content.Seek (startPosition, SeekOrigin.Begin); + } else { + contentCopied = true; + } + + return content.CopyToAsync (stream, bufferSize, cancellationToken); + } + +#if !NET + internal +#endif + protected override bool TryComputeLength (out long length) + { + if (!content.CanSeek) { + length = 0; + return false; + } + length = content.Length - startPosition; + return true; + } + } + +} diff --git a/src/Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs b/src/Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs new file mode 100644 index 000000000000..fe91f5804a23 --- /dev/null +++ b/src/Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs @@ -0,0 +1,74 @@ +using System; +using System.Globalization; +using System.Text; +using System.Net.Http; +using System.Net.Http.Headers; + +using Foundation; + +#if !MONOMAC +using UIKit; +#endif + +#nullable enable + +// useful extensions for the class in order to set it in a header used by the NSUrlSessionHandler +// for cookie management. +#if !MONOMAC +namespace System.Net.Http { +#else +namespace Foundation { +#endif + + static class NSHttpCookieExtensions + { + static void AppendSegment (StringBuilder builder, string name, string? value) + { + if (builder.Length > 0) + builder.Append ("; "); + + builder.Append (name); + if (value is not null) + builder.Append ("=").Append (value); + } + + // returns the header for a cookie + public static string GetHeaderValue (this NSHttpCookie cookie) + { + var header = new StringBuilder(); + AppendSegment (header, cookie.Name, cookie.Value); + AppendSegment (header, NSHttpCookie.KeyPath.ToString (), cookie.Path.ToString ()); + AppendSegment (header, NSHttpCookie.KeyDomain.ToString (), cookie.Domain.ToString ()); + AppendSegment (header, NSHttpCookie.KeyVersion.ToString (), cookie.Version.ToString ()); + + if (cookie.Comment is not null) + AppendSegment (header, NSHttpCookie.KeyComment.ToString (), cookie.Comment.ToString()); + + if (cookie.CommentUrl is not null) + AppendSegment (header, NSHttpCookie.KeyCommentUrl.ToString (), cookie.CommentUrl.ToString()); + + if (cookie.Properties.ContainsKey (NSHttpCookie.KeyDiscard)) + AppendSegment (header, NSHttpCookie.KeyDiscard.ToString (), null); + + if (cookie.ExpiresDate is not null) { + // Format according to RFC1123; 'r' uses invariant info (DateTimeFormatInfo.InvariantInfo) + var dateStr = ((DateTime) cookie.ExpiresDate).ToUniversalTime ().ToString("r", CultureInfo.InvariantCulture); + AppendSegment (header, NSHttpCookie.KeyExpires.ToString (), dateStr); + } + + if (cookie.Properties.ContainsKey (NSHttpCookie.KeyMaximumAge)) { + var timeStampString = (NSString) cookie.Properties[NSHttpCookie.KeyMaximumAge]; + AppendSegment (header, NSHttpCookie.KeyMaximumAge.ToString (), timeStampString); + } + + if (cookie.IsSecure) + AppendSegment (header, NSHttpCookie.KeySecure.ToString(), null); + + if (cookie.IsHttpOnly) + AppendSegment (header, "httponly", null); // Apple does not show the key for the httponly + + return header.ToString (); + } + } + +} diff --git a/src/Makefile b/src/Makefile index dddc17f6d56a..f4b7435603c8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -134,11 +134,15 @@ IOS_EXTRA_SOURCES = \ IOS_DOTNET_HTTP_SOURCES = \ Foundation/NSUrlSessionHandler.cs \ + Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \ + Foundation/NSUrlSessionHandler/MonoStreamContent.cs \ System.Net.Http/CFContentStream.cs \ System.Net.Http/CFNetworkHandler.cs \ IOS_HTTP_SOURCES = \ Foundation/NSUrlSessionHandler.cs \ + Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \ + Foundation/NSUrlSessionHandler/MonoStreamContent.cs \ System.Net.Http/CFContentStream.cs \ System.Net.Http/CFNetworkHandler.cs \ @@ -515,6 +519,8 @@ MAC_CFNETWORK_SOURCES = \ MAC_HTTP_SOURCES = \ Foundation/NSUrlSessionHandler.cs \ + Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \ + Foundation/NSUrlSessionHandler/MonoStreamContent.cs \ System.Net.Http/CFContentStream.cs \ System.Net.Http/CFNetworkHandler.cs \ @@ -829,6 +835,8 @@ WATCHOS_EXTRA_CORE_SOURCES = \ WATCHOS_HTTP_SOURCES = \ Foundation/NSUrlSessionHandler.cs \ + Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \ + Foundation/NSUrlSessionHandler/MonoStreamContent.cs \ WATCHOS_CORE_SOURCES += \ $(WATCHOS_EXTRA_CORE_SOURCES) \ @@ -1121,12 +1129,16 @@ TVOS_CORE_SOURCES += \ TVOS_DOTNET_HTTP_SOURCES = \ Foundation/NSUrlSessionHandler.cs \ + Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \ + Foundation/NSUrlSessionHandler/MonoStreamContent.cs \ System.Net.Http/CFContentStream.cs \ System.Net.Http/CFNetworkHandler.cs \ $(SHARED_DESIGNER_CS) \ TVOS_HTTP_SOURCES = \ Foundation/NSUrlSessionHandler.cs \ + Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \ + Foundation/NSUrlSessionHandler/MonoStreamContent.cs \ System.Net.Http/CFContentStream.cs \ System.Net.Http/CFNetworkHandler.cs \ $(SHARED_DESIGNER_CS) \ @@ -1338,12 +1350,16 @@ MACCATALYST_DOTNET_CORE_SOURCES += \ MACCATALYST_DOTNET_HTTP_SOURCES = \ Foundation/NSUrlSessionHandler.cs \ + Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \ + Foundation/NSUrlSessionHandler/MonoStreamContent.cs \ System.Net.Http/CFContentStream.cs \ System.Net.Http/CFNetworkHandler.cs \ $(SHARED_DESIGNER_CS) \ MACCATALYST_HTTP_SOURCES = \ Foundation/NSUrlSessionHandler.cs \ + Foundation/NSUrlSessionHandler/NSHttpCookieExtensions.cs \ + Foundation/NSUrlSessionHandler/MonoStreamContent.cs \ System.Net.Http/CFContentStream.cs \ System.Net.Http/CFNetworkHandler.cs \ $(SHARED_DESIGNER_CS) \ diff --git a/src/ObjCRuntime/ThrowHelper.cs b/src/ObjCRuntime/ThrowHelper.cs index a3b3d8ceec52..ce2f93045f36 100644 --- a/src/ObjCRuntime/ThrowHelper.cs +++ b/src/ObjCRuntime/ThrowHelper.cs @@ -34,6 +34,12 @@ public static void ThrowArgumentOutOfRangeException (string argumentName, string throw new ArgumentOutOfRangeException (argumentName, message); } + [DoesNotReturn] + public static void ThrowArgumentOutOfRangeException (string argumentName, object actualValue, string message) + { + throw new ArgumentOutOfRangeException (argumentName, actualValue, message); + } + [DoesNotReturn] public static void ThrowObjectDisposedException (object o) { diff --git a/tests/test-libraries/nugets/FrameworksInRuntimesNativeDirectory/Makefile b/tests/test-libraries/nugets/FrameworksInRuntimesNativeDirectory/Makefile index d9645d926c3b..6288fee2aeb7 100644 --- a/tests/test-libraries/nugets/FrameworksInRuntimesNativeDirectory/Makefile +++ b/tests/test-libraries/nugets/FrameworksInRuntimesNativeDirectory/Makefile @@ -27,4 +27,6 @@ $(INSTALLED_PACKAGE): .libs/FrameworksInRuntimesNativeDirectory.nupkg mkdir -p $(abspath $(NUGET_TEST_FEED)) nuget add "$<" -source $(abspath $(NUGET_TEST_FEED)) -NonInteractive +ifdef ENABLE_DOTNET all-local:: $(INSTALLED_PACKAGE) +endif diff --git a/tools/devops/automation/templates/build/build-nugets.yml b/tools/devops/automation/templates/build/build-nugets.yml index fb9eac27dfd2..661107ace0f6 100644 --- a/tools/devops/automation/templates/build/build-nugets.yml +++ b/tools/devops/automation/templates/build/build-nugets.yml @@ -20,6 +20,7 @@ steps: -bl:$(Build.ArtifactStagingDirectory)/build-binlogs/generate-bar-manifest.binlog displayName: Generate and Upload Build Asset Registry Manifest condition: and(succeeded(), contains(variables['configuration.BuildNugets'], 'True')) + continueOnError: true - task: PublishPipelineArtifact@1 displayName: 'Publish Artifact: build-binlogs' diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml index 83724375c32f..2f7490ad776b 100644 --- a/tools/devops/automation/templates/build/build.yml +++ b/tools/devops/automation/templates/build/build.yml @@ -115,6 +115,7 @@ steps: inputs: provisioning_script: $(Build.SourcesDirectory)/xamarin-macios/tools/devops/provision-brew-packages.csx provisioning_extra_args: '-vvvv' + github_token: ${{ parameters.gitHubToken }} timeoutInMinutes: 30 enabled: true @@ -122,11 +123,12 @@ steps: make -C $(Build.SourcesDirectory)/xamarin-macios/tools/devops build-provisioning.csx displayName: 'Generate provisionator files.' -- task: xamops.azdevex.provisionator-task.provisionator@1 +- task: xamops.azdevex.provisionator-task.provisionator@2 displayName: 'Provision Products & Frameworks' inputs: provisioning_script: $(Build.SourcesDirectory)/xamarin-macios/tools/devops/build-provisioning.csx provisioning_extra_args: '-vvvv' + github_token: ${{ parameters.gitHubToken }} timeoutInMinutes: 250 # Use the env variables that were set by the label parsing in the configure step @@ -268,11 +270,6 @@ steps: CONFIGURE_FLAGS="--enable-xamarin" - if [[ "$EnableDotNet" == "True" ]]; then - echo "Enabling dotnet builds." - CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-dotnet" - fi - CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-install-source" echo "Configuration falgs are '$CONFIGURE_FLAGS'" @@ -317,11 +314,11 @@ steps: displayName: 'Build' timeoutInMinutes: 180 -# run ASAP so that we do not have any files that got generated confusing git -- ${{ if eq(parameters.enableAPIDiff, true) }}: - - template: api-diff.yml - parameters: - prID: variables['PrID'] +# # run ASAP so that we do not have any files that got generated confusing git +# - ${{ if eq(parameters.enableAPIDiff, true) }}: +# - template: api-diff.yml +# parameters: +# prID: variables['PrID'] - ${{ if ne(parameters.skipPackages, true) }}: diff --git a/tools/devops/automation/templates/mac/build.yml b/tools/devops/automation/templates/mac/build.yml index 7d62154702e5..3bb8c5ebd530 100644 --- a/tools/devops/automation/templates/mac/build.yml +++ b/tools/devops/automation/templates/mac/build.yml @@ -110,6 +110,7 @@ steps: inputs: provisioning_script: $(Build.SourcesDirectory)/xamarin-macios/tools/devops/provision-brew-packages.csx provisioning_extra_args: '-vvvv' + github_token: $(Github.Token) timeoutInMinutes: 30 enabled: false @@ -139,11 +140,12 @@ steps: make -C $(System.DefaultWorkingDirectory)/xamarin-macios/tools/devops/ mac-tests-provisioning.csx displayName: 'Generate Provisionator csx file' -- task: xamops.azdevex.provisionator-task.provisionator@1 +- task: xamops.azdevex.provisionator-task.provisionator@2 displayName: 'Provision Products & Frameworks' inputs: provisioning_script: $(Build.SourcesDirectory)/xamarin-macios/tools/devops/mac-tests-provisioning.csx provisioning_extra_args: '-vvvv' + github_token: $(Github.Token) timeoutInMinutes: 250 # Executed ONLY if we want to clear the provisionator cache. diff --git a/tools/devops/automation/templates/tests/build.yml b/tools/devops/automation/templates/tests/build.yml index a79390170733..3f346e00bc46 100644 --- a/tools/devops/automation/templates/tests/build.yml +++ b/tools/devops/automation/templates/tests/build.yml @@ -160,11 +160,12 @@ steps: condition: ${{ parameters.clearProvisionatorCache }} # Use the provisionator to install the test dependencies. Those have been generated in the 'Generate Provisionator csx file' step. -- task: xamops.azdevex.provisionator-task.provisionator@1 +- task: xamops.azdevex.provisionator-task.provisionator@2 displayName: 'Provision Xamarin.iOS' inputs: provisioning_script: $(System.DefaultWorkingDirectory)/xamarin-macios/tools/devops/device-tests-provisioning.csx provisioning_extra_args: '-vvvv' + github_token: ${{ parameters.gitHubToken }} timeoutInMinutes: 250 - bash: | @@ -172,11 +173,12 @@ steps: make -C $(System.DefaultWorkingDirectory)/xamarin-macios/tools/devops/ mac-tests-provisioning.csx displayName: 'Generate Provisionator csx file for Mac' -- task: xamops.azdevex.provisionator-task.provisionator@1 +- task: xamops.azdevex.provisionator-task.provisionator@2 displayName: 'Provision Xamarin.Mac' inputs: provisioning_script: $(Build.SourcesDirectory)/xamarin-macios/tools/devops/mac-tests-provisioning.csx provisioning_extra_args: '-vvvv' + github_token: ${{ parameters.gitHubToken }} timeoutInMinutes: 250 - bash: |