diff --git a/.github/workflows/autoformat-v2.yml b/.github/workflows/autoformat-v2.yml
index b7ead504ef9b..d906a323359b 100644
--- a/.github/workflows/autoformat-v2.yml
+++ b/.github/workflows/autoformat-v2.yml
@@ -21,7 +21,7 @@ jobs:
persist-credentials: false
- name: 'Install .NET'
- uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5
+ uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5
with:
global-json-file: ./global.json
@@ -67,7 +67,7 @@ jobs:
fetch-depth: 1
- name: 'Install .NET'
- uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5
+ uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5
with:
global-json-file: ./global.json
diff --git a/.github/workflows/maestro-changelog.yml b/.github/workflows/maestro-changelog.yml
index ab0ebf924389..c976b9e161fb 100644
--- a/.github/workflows/maestro-changelog.yml
+++ b/.github/workflows/maestro-changelog.yml
@@ -17,7 +17,7 @@ jobs:
if: github.event.pull_request.user.login == 'dotnet-maestro[bot]'
steps:
- - uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5
+ - uses: actions/setup-dotnet@26b0ec14cb23fa6904739307f278c14f94c95bf1 # v5
with:
dotnet-version: '9'
diff --git a/docs/api/Foundation/ModelAttribute.xml b/docs/api/Foundation/ModelAttribute.xml
deleted file mode 100644
index 661a5b458cf8..000000000000
--- a/docs/api/Foundation/ModelAttribute.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
- Flag a class as a model.
-
- Objective-C protocols are like interfaces, but they support optional methods, that is, not all of the methods need to be implemented for the protocol to work.
-
-There are two ways of implementing a model, you can either implement it manually or use the existing strongly typed definitions.
-
-
-MonoTouch provides already strongly typed declarations ready to use that do not require manual binding. To support this programming model, the MonoTouch runtime supports the [Model] attribute on a class declaration. This informs the runtime that it should not wire up all the methods in the class, unless the method is explicitly implemented.
-
-
-The Model attribute is applied to a class that can have optional methods, and it is typically used for declaring Objective-C delegates or data models that have a number of optional methods. The MonoTouch runtime treats classes with the Model attribute applied specially: only when a user overrides methods in a class, is the actual override exposed to the Objective-C world as existing.
-
-
-This attribute is used in all of the models and delegate classes to allow the user to only implement the methods that he is interested in.
-
-
-
-
\ No newline at end of file
diff --git a/docs/building-apps/build-properties.md b/docs/building-apps/build-properties.md
index 78a10c4c2d9d..97525d1af61e 100644
--- a/docs/building-apps/build-properties.md
+++ b/docs/building-apps/build-properties.md
@@ -466,6 +466,23 @@ The full path to the `ditto` executable.
The default behavior is to use `/usr/bin/ditto`.
+## DynamicRegistrationSupported
+
+Controls whether the dynamic registrar is available at runtime (as reported by
+`ObjCRuntime.Runtime.DynamicRegistrationSupported`).
+
+If this value is not specified, the build will compute a default value based
+on whether the app needs the dynamic registrar, and enables the
+`ObjCRuntime.Runtime.DynamicRegistrationSupported` trimmer feature switch
+accordingly (so the trimmer can remove the dynamic registrar when it's not
+needed, making the app smaller).
+
+Set this property to `true` or `false` to override the computed default.
+
+Removing the dynamic registrar requires a static registrar (`Registrar=static` or
+`Registrar=managed-static`) and trimming, so setting this property has no effect (and the
+build warns) when those conditions aren't met.
+
## EmbedOnDemandResources
If on-demand resources should be embedded in the app bundle.
diff --git a/dotnet/Workloads/SignList.xml b/dotnet/Workloads/SignList.xml
index 8c695597ea18..e674d1aa5c15 100644
--- a/dotnet/Workloads/SignList.xml
+++ b/dotnet/Workloads/SignList.xml
@@ -37,18 +37,15 @@
-
-
-
@@ -58,6 +55,9 @@
+
+
+
diff --git a/dotnet/targets/Xamarin.Shared.Sdk.targets b/dotnet/targets/Xamarin.Shared.Sdk.targets
index d0e5e10e5d2f..4c11d8f1ee04 100644
--- a/dotnet/targets/Xamarin.Shared.Sdk.targets
+++ b/dotnet/targets/Xamarin.Shared.Sdk.targets
@@ -272,6 +272,7 @@
_ExtendAppExtensionReferences;
_ComputeLinkerArguments;
_PrepareAssemblies;
+ _SetDynamicRegistrationSupportedFeature;
_ComputeFrameworkFilesToPublish;
_ComputeDynamicLibrariesToPublish;
ComputeFilesToPublish;
@@ -427,6 +428,8 @@
Targets="_BuildRidSpecificAppBundle"
Properties="
AppendRuntimeIdentifierToOutputPath=true;
+ BuildSessionId=$(BuildSessionId);
+ BuildAppName=$(BuildAppName);
RuntimeIdentifier=%(_RuntimeIdentifiersAsItems.Identity);
_CodesignItemsPath=%(_RuntimeIdentifiersAsItems.RidSpecificCodesignItemsPath);
_MtouchSymbolsList=%(_RuntimeIdentifiersAsItems.RidSpecificSymbolsListPath);
@@ -560,6 +563,37 @@
+
+
+
+
+
+
+ <_DynamicRegistrationSupportedLine Include="@(_AssemblyPreparerMSBuildOutputProperty)" Condition="$([System.String]::Copy('%(Identity)').StartsWith('DynamicRegistrationSupported='))" />
+
+
+
+ <_DynamicRegistrationSupportedLineText>@(_DynamicRegistrationSupportedLine)
+ <_DynamicRegistrationSupported>$(DynamicRegistrationSupported)
+ <_DynamicRegistrationSupported Condition="'$(_DynamicRegistrationSupported)' == '' And '$(_DynamicRegistrationSupportedLineText)' != ''">$(_DynamicRegistrationSupportedLineText.Replace('DynamicRegistrationSupported=', ''))
+
+
+
+
+
+
@@ -607,6 +641,10 @@
<_CustomLinkerOptionsFile>$([System.IO.Path]::GetFullPath('$(IntermediateOutputPath)custom-linker-options.txt'))
<_CustomLinkerOptionsFile Condition="'$(BuildSessionId)' != ''">$(IntermediateOutputPath)custom-linker-options.txt
+
+ <_AssemblyPreparerMSBuildOutputFile Condition="'$(PrepareAssemblies)' == 'true'">$(DeviceSpecificIntermediateOutputPath)assembly-preparer-msbuild-output.txt
+ <_AssemblyPostProcessorMSBuildOutputFile Condition="'$(PrepareAssemblies)' == 'true'">$(DeviceSpecificIntermediateOutputPath)assembly-postprocessor-msbuild-output.txt
+
<_LinkerItemsDirectory>$([System.IO.Path]::GetFullPath('$(IntermediateOutputPath)linker-items'))
<_LinkerItemsDirectory Condition="'$(BuildSessionId)' != ''">$(IntermediateOutputPath)linker-items
@@ -649,6 +687,7 @@
@(_BundlerDlsym -> 'Dlsym=%(Identity)')
Debug=$(_BundlerDebug)
DeploymentTarget=$(_MinimumOSVersion)
+ DynamicRegistrationSupported=$(DynamicRegistrationSupported)
@(_CustomLinkFlags -> 'CustomLinkFlags=%(Identity)')
EnableSGenConc=$(EnableSGenConc)
@(_BundlerEnvironmentVariables -> 'EnvironmentVariable=Overwrite=%(Overwrite)|%(Identity)=%(Value)')
@@ -668,6 +707,8 @@
MarshalManagedExceptionMode=$(MarshalManagedExceptionMode)
MarshalObjectiveCExceptionMode=$(MarshalObjectiveCExceptionMode)
@(_MonoLibrary -> 'MonoLibrary=%(Identity)')
+ MSBuildOutputFile=$(_AssemblyPreparerMSBuildOutputFile)
+ MSBuildPostProcessOutputFile=$(_AssemblyPostProcessorMSBuildOutputFile)
MtouchFloat32=$(MtouchFloat32)
NoWarn=$(_BundlerNoWarn)
Optimize=$(_BundlerOptimize)
diff --git a/eng/Version.Details.props b/eng/Version.Details.props
index 0edeaf326381..b940d445fb64 100644
--- a/eng/Version.Details.props
+++ b/eng/Version.Details.props
@@ -6,16 +6,16 @@ This file should be imported by eng/Versions.props
- 10.0.0-beta.26356.110
- 10.0.0-beta.26356.110
+ 10.0.0-beta.26357.110
+ 10.0.0-beta.26357.110
0.11.5-alpha.26070.104
- 10.0.0-beta.26356.110
+ 10.0.0-beta.26357.110
10.0.3-servicing.26070.104
10.0.3
10.0.3
- 10.0.400-preview.0.26356.110
+ 10.0.400-preview.0.26357.110
10.0.3
- 10.0.400-preview.26356.110
+ 10.0.400-preview.26357.110
26.0.11017
18.5.9227
@@ -30,7 +30,7 @@ This file should be imported by eng/Versions.props
18.5.9227
26.5.9004
- 11.0.0-prerelease.26353.1
+ 11.0.0-prerelease.26357.1
18.0.9617
18.0.9617
diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index ab891f2fbbee..5d2ae04cb01c 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -1,8 +1,8 @@
-
+
https://github.com/dotnet/dotnet
- 0e603833cc19f22076e5fcac787ccd22f3900a51
+ 2bd0e65f2efd50d56ae2fa3adfc1c3f440c1d223
https://github.com/dotnet/dotnet
@@ -95,25 +95,25 @@
-
+
https://github.com/dotnet/dotnet
- 0e603833cc19f22076e5fcac787ccd22f3900a51
+ 2bd0e65f2efd50d56ae2fa3adfc1c3f440c1d223
-
+
https://github.com/dotnet/dotnet
- 0e603833cc19f22076e5fcac787ccd22f3900a51
+ 2bd0e65f2efd50d56ae2fa3adfc1c3f440c1d223
-
+
https://github.com/dotnet/dotnet
- 0e603833cc19f22076e5fcac787ccd22f3900a51
+ 2bd0e65f2efd50d56ae2fa3adfc1c3f440c1d223
-
+
https://github.com/dotnet/xharness
- 4da0015e9dd1ed7d3992db7d1eb6878a5ee11ab2
+ b631e17803318d67046871027986a005f3db02bf
-
+
https://github.com/dotnet/dotnet
- 0e603833cc19f22076e5fcac787ccd22f3900a51
+ 2bd0e65f2efd50d56ae2fa3adfc1c3f440c1d223
diff --git a/eng/common/core-templates/steps/publish-logs.yml b/eng/common/core-templates/steps/publish-logs.yml
index 4eed0312b807..694f55a926e6 100644
--- a/eng/common/core-templates/steps/publish-logs.yml
+++ b/eng/common/core-templates/steps/publish-logs.yml
@@ -33,9 +33,6 @@ steps:
'$(publishing-dnceng-devdiv-code-r-build-re)'
'$(dn-bot-all-orgs-artifact-feeds-rw)'
'$(akams-client-id)'
- '$(microsoft-symbol-server-pat)'
- '$(symweb-symbol-server-pat)'
- '$(dnceng-symbol-server-pat)'
'$(dn-bot-all-orgs-build-rw-code-rw)'
'$(System.AccessToken)'
${{parameters.CustomSensitiveDataList}}
diff --git a/eng/common/core-templates/steps/source-index-stage1-publish.yml b/eng/common/core-templates/steps/source-index-stage1-publish.yml
index e9a694afa58e..6e7666b4dcf8 100644
--- a/eng/common/core-templates/steps/source-index-stage1-publish.yml
+++ b/eng/common/core-templates/steps/source-index-stage1-publish.yml
@@ -14,8 +14,8 @@ steps:
workingDirectory: $(Agent.TempDirectory)
- script: |
- $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version ${{parameters.sourceIndexProcessBinlogPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools
- $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version ${{parameters.sourceIndexUploadPackageVersion}} --add-source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools
+ $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version ${{parameters.sourceIndexProcessBinlogPackageVersion}} --source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools
+ $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version ${{parameters.sourceIndexUploadPackageVersion}} --source ${{parameters.SourceIndexPackageSource}} --tool-path $(Agent.TempDirectory)/.source-index/tools
displayName: "Source Index: Download netsourceindex Tools"
# Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk.
workingDirectory: $(Agent.TempDirectory)
diff --git a/eng/common/cross/build-rootfs.sh b/eng/common/cross/build-rootfs.sh
index 8abfb71f7275..3150ccac6fcd 100644
--- a/eng/common/cross/build-rootfs.sh
+++ b/eng/common/cross/build-rootfs.sh
@@ -5,10 +5,11 @@ set -e
usage()
{
echo "Usage: $0 [BuildArch] [CodeName] [lldbx.y] [llvmx[.y]] [--skipunmount] --rootfsdir ]"
- echo "BuildArch can be: arm(default), arm64, armel, armv6, loongarch64, ppc64le, riscv64, s390x, x64, x86"
+ echo "BuildArch can be: arm(default), arm64, loongarch64, ppc64le, riscv64, s390x, x64, x86"
echo "CodeName - optional, Code name for Linux, can be: xenial(default), zesty, bionic, alpine"
echo " for alpine can be specified with version: alpineX.YY or alpineedge"
echo " for FreeBSD can be: freebsd13, freebsd14"
+ echo " for OpenBSD can be: openbsd"
echo " for illumos can be: illumos"
echo " for Haiku can be: haiku."
echo "lldbx.y - optional, LLDB version, can be: lldb3.9(default), lldb4.0, lldb5.0, lldb6.0 no-lldb. Ignored for alpine and FreeBSD"
@@ -27,6 +28,8 @@ __BuildArch=arm
__AlpineArch=armv7
__FreeBSDArch=arm
__FreeBSDMachineArch=armv7
+__OpenBSDArch=arm
+__OpenBSDMachineArch=armv7
__IllumosArch=arm7
__HaikuArch=arm
__QEMUArch=arm
@@ -72,8 +75,8 @@ __AlpinePackages+=" krb5-dev"
__AlpinePackages+=" openssl-dev"
__AlpinePackages+=" zlib-dev"
-__FreeBSDBase="13.4-RELEASE"
-__FreeBSDPkg="1.21.3"
+__FreeBSDBase="13.5-RELEASE"
+__FreeBSDPkg="2.7.5"
__FreeBSDABI="13"
__FreeBSDPackages="libunwind"
__FreeBSDPackages+=" icu"
@@ -82,6 +85,13 @@ __FreeBSDPackages+=" openssl"
__FreeBSDPackages+=" krb5"
__FreeBSDPackages+=" terminfo-db"
+__OpenBSDVersion="7.8"
+__OpenBSDPackages="heimdal-libs"
+__OpenBSDPackages+=" icu4c"
+__OpenBSDPackages+=" libinotify"
+__OpenBSDPackages+=" openssl"
+__OpenBSDPackages+=" e2fsprogs"
+
__IllumosPackages="icu"
__IllumosPackages+=" mit-krb5"
__IllumosPackages+=" openssl"
@@ -130,7 +140,6 @@ __AlpineKeys='
616db30d:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnpUpyWDWjlUk3smlWeA0\nlIMW+oJ38t92CRLHH3IqRhyECBRW0d0aRGtq7TY8PmxjjvBZrxTNDpJT6KUk4LRm\na6A6IuAI7QnNK8SJqM0DLzlpygd7GJf8ZL9SoHSH+gFsYF67Cpooz/YDqWrlN7Vw\ntO00s0B+eXy+PCXYU7VSfuWFGK8TGEv6HfGMALLjhqMManyvfp8hz3ubN1rK3c8C\nUS/ilRh1qckdbtPvoDPhSbTDmfU1g/EfRSIEXBrIMLg9ka/XB9PvWRrekrppnQzP\nhP9YE3x/wbFc5QqQWiRCYyQl/rgIMOXvIxhkfe8H5n1Et4VAorkpEAXdsfN8KSVv\nLSMazVlLp9GYq5SUpqYX3KnxdWBgN7BJoZ4sltsTpHQ/34SXWfu3UmyUveWj7wp0\nx9hwsPirVI00EEea9AbP7NM2rAyu6ukcm4m6ATd2DZJIViq2es6m60AE6SMCmrQF\nwmk4H/kdQgeAELVfGOm2VyJ3z69fQuywz7xu27S6zTKi05Qlnohxol4wVb6OB7qG\nLPRtK9ObgzRo/OPumyXqlzAi/Yvyd1ZQk8labZps3e16bQp8+pVPiumWioMFJDWV\nGZjCmyMSU8V6MB6njbgLHoyg2LCukCAeSjbPGGGYhnKLm1AKSoJh3IpZuqcKCk5C\n8CM1S15HxV78s9dFntEqIokCAwEAAQ==
66ba20fe:MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtfB12w4ZgqsXWZDfUAV/\n6Y4aHUKIu3q4SXrNZ7CXF9nXoAVYrS7NAxJdAodsY3vPCN0g5O8DFXR+390LdOuQ\n+HsGKCc1k5tX5ZXld37EZNTNSbR0k+NKhd9h6X3u6wqPOx7SIKxwAQR8qeeFq4pP\nrt9GAGlxtuYgzIIcKJPwE0dZlcBCg+GnptCUZXp/38BP1eYC+xTXSL6Muq1etYfg\nodXdb7Yl+2h1IHuOwo5rjgY5kpY7GcAs8AjGk3lDD/av60OTYccknH0NCVSmPoXK\nvrxDBOn0LQRNBLcAfnTKgHrzy0Q5h4TNkkyTgxkoQw5ObDk9nnabTxql732yy9BY\ns+hM9+dSFO1HKeVXreYSA2n1ndF18YAvAumzgyqzB7I4pMHXq1kC/8bONMJxwSkS\nYm6CoXKyavp7RqGMyeVpRC7tV+blkrrUml0BwNkxE+XnwDRB3xDV6hqgWe0XrifD\nYTfvd9ScZQP83ip0r4IKlq4GMv/R5shcCRJSkSZ6QSGshH40JYSoiwJf5FHbj9ND\n7do0UAqebWo4yNx63j/wb2ULorW3AClv0BCFSdPsIrCStiGdpgJDBR2P2NZOCob3\nG9uMj+wJD6JJg2nWqNJxkANXX37Qf8plgzssrhrgOvB0fjjS7GYhfkfmZTJ0wPOw\nA8+KzFseBh4UFGgue78KwgkCAwEAAQ==
'
-__Keyring=
__KeyringFile="/usr/share/keyrings/ubuntu-archive-keyring.gpg"
__SkipSigCheck=0
__SkipEmulation=0
@@ -153,6 +162,10 @@ while :; do
__AlpineArch=armv7
__QEMUArch=arm
;;
+ armel)
+ # this is only used for tizen-build-rootfs.sh
+ __BuildArch=armel
+ ;;
arm64)
__BuildArch=arm64
__UbuntuArch=arm64
@@ -160,31 +173,8 @@ while :; do
__QEMUArch=aarch64
__FreeBSDArch=arm64
__FreeBSDMachineArch=aarch64
- ;;
- armel)
- __BuildArch=armel
- __UbuntuArch=armel
- __UbuntuRepo="http://archive.debian.org/debian/"
- __CodeName=buster
- __KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
- __LLDB_Package="liblldb-6.0-dev"
- __UbuntuPackages="${__UbuntuPackages// libomp-dev/}"
- __UbuntuPackages="${__UbuntuPackages// libomp5/}"
- __UbuntuSuites=
- ;;
- armv6)
- __BuildArch=armv6
- __UbuntuArch=armhf
- __QEMUArch=arm
- __UbuntuRepo="http://raspbian.raspberrypi.org/raspbian/"
- __CodeName=buster
- __KeyringFile="/usr/share/keyrings/raspbian-archive-keyring.gpg"
- __LLDB_Package="liblldb-6.0-dev"
- __UbuntuSuites=
-
- if [[ -e "$__KeyringFile" ]]; then
- __Keyring="--keyring $__KeyringFile"
- fi
+ __OpenBSDArch=arm64
+ __OpenBSDMachineArch=aarch64
;;
loongarch64)
__BuildArch=loongarch64
@@ -193,10 +183,6 @@ while :; do
__UbuntuArch=loong64
__UbuntuSuites=unreleased
__LLDB_Package="liblldb-19-dev"
-
- if [[ "$__CodeName" == "sid" ]]; then
- __UbuntuRepo="http://ftp.ports.debian.org/debian-ports/"
- fi
;;
riscv64)
__BuildArch=riscv64
@@ -212,7 +198,7 @@ while :; do
__AlpineArch=ppc64le
__QEMUArch=ppc64le
__UbuntuArch=ppc64el
- __UbuntuRepo="http://ports.ubuntu.com/ubuntu-ports/"
+ __UbuntuRepo="https://ports.ubuntu.com/ubuntu-ports/"
__UbuntuPackages="${__UbuntuPackages// libunwind8-dev/}"
__UbuntuPackages="${__UbuntuPackages// libomp-dev/}"
__UbuntuPackages="${__UbuntuPackages// libomp5/}"
@@ -223,7 +209,7 @@ while :; do
__AlpineArch=s390x
__QEMUArch=s390x
__UbuntuArch=s390x
- __UbuntuRepo="http://ports.ubuntu.com/ubuntu-ports/"
+ __UbuntuRepo="https://ports.ubuntu.com/ubuntu-ports/"
__UbuntuPackages="${__UbuntuPackages// libunwind8-dev/}"
__UbuntuPackages="${__UbuntuPackages// libomp-dev/}"
__UbuntuPackages="${__UbuntuPackages// libomp5/}"
@@ -235,15 +221,17 @@ while :; do
__UbuntuArch=amd64
__FreeBSDArch=amd64
__FreeBSDMachineArch=amd64
+ __OpenBSDArch=amd64
+ __OpenBSDMachineArch=amd64
__illumosArch=x86_64
__HaikuArch=x86_64
- __UbuntuRepo="http://archive.ubuntu.com/ubuntu/"
+ __UbuntuRepo="https://archive.ubuntu.com/ubuntu/"
;;
x86)
__BuildArch=x86
__UbuntuArch=i386
__AlpineArch=x86
- __UbuntuRepo="http://archive.ubuntu.com/ubuntu/"
+ __UbuntuRepo="https://archive.ubuntu.com/ubuntu/"
;;
lldb*)
version="$(echo "$lowerI" | tr -d '[:alpha:]-=')"
@@ -295,9 +283,7 @@ while :; do
;;
noble) # Ubuntu 24.04
__CodeName=noble
- if [[ -z "$__LLDB_Package" ]]; then
- __LLDB_Package="liblldb-19-dev"
- fi
+ __LLDB_Package="liblldb-19-dev"
;;
stretch) # Debian 9
__CodeName=stretch
@@ -305,7 +291,7 @@ while :; do
__KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
if [[ -z "$__UbuntuRepo" ]]; then
- __UbuntuRepo="http://ftp.debian.org/debian/"
+ __UbuntuRepo="https://archive.debian.org/debian/"
fi
;;
buster) # Debian 10
@@ -314,7 +300,7 @@ while :; do
__KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
if [[ -z "$__UbuntuRepo" ]]; then
- __UbuntuRepo="http://archive.debian.org/debian/"
+ __UbuntuRepo="https://archive.debian.org/debian/"
fi
;;
bullseye) # Debian 11
@@ -322,7 +308,7 @@ while :; do
__KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
if [[ -z "$__UbuntuRepo" ]]; then
- __UbuntuRepo="http://ftp.debian.org/debian/"
+ __UbuntuRepo="https://ftp.debian.org/debian/"
fi
;;
bookworm) # Debian 12
@@ -330,7 +316,7 @@ while :; do
__KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
if [[ -z "$__UbuntuRepo" ]]; then
- __UbuntuRepo="http://ftp.debian.org/debian/"
+ __UbuntuRepo="https://ftp.debian.org/debian/"
fi
;;
sid) # Debian sid
@@ -339,25 +325,21 @@ while :; do
# Debian-Ports architectures need different values
case "$__UbuntuArch" in
- amd64|arm64|armel|armhf|i386|mips64el|ppc64el|riscv64|s390x)
+ amd64|arm64|armhf|i386|mips64el|ppc64el|riscv64|s390x)
__KeyringFile="/usr/share/keyrings/debian-archive-keyring.gpg"
if [[ -z "$__UbuntuRepo" ]]; then
- __UbuntuRepo="http://ftp.debian.org/debian/"
+ __UbuntuRepo="https://ftp.debian.org/debian/"
fi
;;
*)
__KeyringFile="/usr/share/keyrings/debian-ports-archive-keyring.gpg"
if [[ -z "$__UbuntuRepo" ]]; then
- __UbuntuRepo="http://ftp.ports.debian.org/debian-ports/"
+ __UbuntuRepo="https://ftp.debian.org/debian-ports/"
fi
;;
esac
-
- if [[ -e "$__KeyringFile" ]]; then
- __Keyring="--keyring $__KeyringFile"
- fi
;;
tizen)
__CodeName=
@@ -383,10 +365,14 @@ while :; do
;;
freebsd14)
__CodeName=freebsd
- __FreeBSDBase="14.2-RELEASE"
+ __FreeBSDBase="14.3-RELEASE"
__FreeBSDABI="14"
__SkipUnmount=1
;;
+ openbsd)
+ __CodeName=openbsd
+ __SkipUnmount=1
+ ;;
illumos)
__CodeName=illumos
__SkipUnmount=1
@@ -457,7 +443,7 @@ fi
__UbuntuPackages+=" ${__LLDB_Package:-}"
if [[ -z "$__UbuntuRepo" ]]; then
- __UbuntuRepo="http://ports.ubuntu.com/"
+ __UbuntuRepo="https://ports.ubuntu.com/"
fi
if [[ -n "$__LLVM_MajorVersion" ]]; then
@@ -544,15 +530,15 @@ if [[ "$__CodeName" == "alpine" ]]; then
# initialize DB
# shellcheck disable=SC2086
"$__ApkToolsDir/apk.static" \
- -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \
- -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \
+ -X "https://dl-cdn.alpinelinux.org/alpine/$version/main" \
+ -X "https://dl-cdn.alpinelinux.org/alpine/$version/community" \
-U $__ApkSignatureArg --root "$__RootfsDir" --arch "$__AlpineArch" --initdb add
if [[ "$__AlpineLlvmLibsLookup" == 1 ]]; then
# shellcheck disable=SC2086
__AlpinePackages+=" $("$__ApkToolsDir/apk.static" \
- -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \
- -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \
+ -X "https://dl-cdn.alpinelinux.org/alpine/$version/main" \
+ -X "https://dl-cdn.alpinelinux.org/alpine/$version/community" \
-U $__ApkSignatureArg --root "$__RootfsDir" --arch "$__AlpineArch" \
search 'llvm*-libs' | grep -E '^llvm' | sort | tail -1 | sed 's/-[^-]*//2g')"
fi
@@ -560,8 +546,8 @@ if [[ "$__CodeName" == "alpine" ]]; then
# install all packages in one go
# shellcheck disable=SC2086
"$__ApkToolsDir/apk.static" \
- -X "http://dl-cdn.alpinelinux.org/alpine/$version/main" \
- -X "http://dl-cdn.alpinelinux.org/alpine/$version/community" \
+ -X "https://dl-cdn.alpinelinux.org/alpine/$version/main" \
+ -X "https://dl-cdn.alpinelinux.org/alpine/$version/community" \
-U $__ApkSignatureArg --root "$__RootfsDir" --arch "$__AlpineArch" $__NoEmulationArg \
add $__AlpinePackages
@@ -578,7 +564,7 @@ elif [[ "$__CodeName" == "freebsd" ]]; then
curl -SL "https://download.freebsd.org/ftp/releases/${__FreeBSDArch}/${__FreeBSDMachineArch}/${__FreeBSDBase}/base.txz" | tar -C "$__RootfsDir" -Jxf - ./lib ./usr/lib ./usr/libdata ./usr/include ./usr/share/keys ./etc ./bin/freebsd-version
fi
echo "ABI = \"FreeBSD:${__FreeBSDABI}:${__FreeBSDMachineArch}\"; FINGERPRINTS = \"${__RootfsDir}/usr/share/keys\"; REPOS_DIR = [\"${__RootfsDir}/etc/pkg\"]; REPO_AUTOUPDATE = NO; RUN_SCRIPTS = NO;" > "${__RootfsDir}"/usr/local/etc/pkg.conf
- echo "FreeBSD: { url: \"pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly\", mirror_type: \"srv\", signature_type: \"fingerprints\", fingerprints: \"/usr/share/keys/pkg\", enabled: yes }" > "${__RootfsDir}"/etc/pkg/FreeBSD.conf
+ echo "FreeBSD: { url: \"pkg+https://pkg.FreeBSD.org/\${ABI}/quarterly\", mirror_type: \"srv\", signature_type: \"fingerprints\", fingerprints: \"/usr/share/keys/pkg\", enabled: yes }" > "${__RootfsDir}"/etc/pkg/FreeBSD.conf
mkdir -p "$__RootfsDir"/tmp
# get and build package manager
if [[ "$__hasWget" == 1 ]]; then
@@ -592,9 +578,69 @@ elif [[ "$__CodeName" == "freebsd" ]]; then
./autogen.sh && ./configure --prefix="$__RootfsDir"/host && make -j "$JOBS" && make install
rm -rf "$__RootfsDir/tmp/pkg-${__FreeBSDPkg}"
# install packages we need.
- INSTALL_AS_USER=$(whoami) "$__RootfsDir"/host/sbin/pkg -r "$__RootfsDir" -C "$__RootfsDir"/usr/local/etc/pkg.conf update
+ INSTALL_AS_USER=$(whoami) IGNORE_OSVERSION=yes "$__RootfsDir"/host/sbin/pkg -r "$__RootfsDir" -C "$__RootfsDir"/usr/local/etc/pkg.conf update
# shellcheck disable=SC2086
INSTALL_AS_USER=$(whoami) "$__RootfsDir"/host/sbin/pkg -r "$__RootfsDir" -C "$__RootfsDir"/usr/local/etc/pkg.conf install --yes $__FreeBSDPackages
+elif [[ "$__CodeName" == "openbsd" ]]; then
+ # determine mirrors
+ OPENBSD_MIRROR="https://cdn.openbsd.org/pub/OpenBSD/$__OpenBSDVersion/$__OpenBSDMachineArch"
+
+ # download base system sets
+ ensureDownloadTool
+
+ BASE_SETS=(base comp)
+ for set in "${BASE_SETS[@]}"; do
+ FILE="${set}${__OpenBSDVersion//./}.tgz"
+ echo "Downloading $FILE..."
+ if [[ "$__hasWget" == 1 ]]; then
+ wget -O- "$OPENBSD_MIRROR/$FILE" | tar -C "$__RootfsDir" -xzpf -
+ else
+ curl -SL "$OPENBSD_MIRROR/$FILE" | tar -C "$__RootfsDir" -xzpf -
+ fi
+ done
+
+ PKG_MIRROR="https://cdn.openbsd.org/pub/OpenBSD/${__OpenBSDVersion}/packages/${__OpenBSDMachineArch}"
+
+ echo "Installing packages into sysroot..."
+
+ # Fetch package index once
+ if [[ "$__hasWget" == 1 ]]; then
+ PKG_INDEX=$(wget -qO- "$PKG_MIRROR/")
+ else
+ PKG_INDEX=$(curl -s "$PKG_MIRROR/")
+ fi
+
+ for pkg in $__OpenBSDPackages; do
+ PKG_FILE=$(echo "$PKG_INDEX" | grep -Po ">\K${pkg}-[0-9][^\" ]*\.tgz" \
+ | sort -V | tail -n1)
+
+ echo "Resolved package filename for $pkg: $PKG_FILE"
+
+ [[ -z "$PKG_FILE" ]] && { echo "ERROR: Package $pkg not found"; exit 1; }
+
+ if [[ "$__hasWget" == 1 ]]; then
+ wget -O- "$PKG_MIRROR/$PKG_FILE" | tar -C "$__RootfsDir/usr/local" -xzpf -
+ else
+ curl -SL "$PKG_MIRROR/$PKG_FILE" | tar -C "$__RootfsDir/usr/local" -xzpf -
+ fi
+ done
+
+ echo "Creating versionless symlinks for shared libraries..."
+ # Find all versioned .so files and create the base .so symlink
+ for lib in "$__RootfsDir"/usr/lib/lib*.so.*; do
+ if [ -f "$lib" ]; then
+ # Extract the filename (e.g., libc++.so.12.0)
+ VERSIONED_NAME=$(basename "$lib")
+ # Remove the trailing version numbers (e.g., libc++.so)
+ BASE_NAME=${VERSIONED_NAME%.so.*}.so
+ # Create the symlink in the same directory
+ ln -sf "$VERSIONED_NAME" "$__RootfsDir/usr/lib/$BASE_NAME"
+ fi
+ done
+
+ echo "Cleaning up unnecessary paths"
+ # we don't use executables and kernel in rootfs (as we use host's compiler with -sysroot)
+ rm -rf "$__RootfsDir/usr/share" "$__RootfsDir/usr/bin"
elif [[ "$__CodeName" == "illumos" ]]; then
mkdir "$__RootfsDir/tmp"
pushd "$__RootfsDir/tmp"
@@ -759,6 +805,14 @@ elif [[ "$__CodeName" == "haiku" ]]; then
elif [[ -n "$__CodeName" ]]; then
__Suites="$__CodeName $(for suite in $__UbuntuSuites; do echo -n "$__CodeName-$suite "; done)"
+ __SigCheckArgs=
+ if [[ "$__SkipSigCheck" == "0" ]]; then
+ if [[ -e "$__KeyringFile" ]]; then
+ __SigCheckArgs="--keyring $__KeyringFile"
+ fi
+ __SigCheckArgs="$__SigCheckArgs --force-check-gpg"
+ fi
+
if [[ "$__SkipEmulation" == "1" ]]; then
if [[ -z "$AR" ]]; then
if command -v ar &>/dev/null; then
@@ -774,31 +828,23 @@ elif [[ -n "$__CodeName" ]]; then
PYTHON=${PYTHON_EXECUTABLE:-python3}
# shellcheck disable=SC2086,SC2046
- echo running "$PYTHON" "$__CrossDir/install-debs.py" --arch "$__UbuntuArch" --mirror "$__UbuntuRepo" --rootfsdir "$__RootfsDir" --artool "$AR" \
+ echo running "$PYTHON" "$__CrossDir/install-debs.py" $__SigCheckArgs --arch "$__UbuntuArch" --mirror "$__UbuntuRepo" --rootfsdir "$__RootfsDir" --artool "$AR" \
$(for suite in $__Suites; do echo -n "--suite $suite "; done) \
$__UbuntuPackages
# shellcheck disable=SC2086,SC2046
- "$PYTHON" "$__CrossDir/install-debs.py" --arch "$__UbuntuArch" --mirror "$__UbuntuRepo" --rootfsdir "$__RootfsDir" --artool "$AR" \
+ "$PYTHON" "$__CrossDir/install-debs.py" $__SigCheckArgs --arch "$__UbuntuArch" --mirror "$__UbuntuRepo" --rootfsdir "$__RootfsDir" --artool "$AR" \
$(for suite in $__Suites; do echo -n "--suite $suite "; done) \
$__UbuntuPackages
exit 0
fi
- __UpdateOptions=
- if [[ "$__SkipSigCheck" == "0" ]]; then
- __Keyring="$__Keyring --force-check-gpg"
- else
- __Keyring=
- __UpdateOptions="--allow-unauthenticated --allow-insecure-repositories"
- fi
-
# shellcheck disable=SC2086
- echo running debootstrap "--variant=minbase" $__Keyring --arch "$__UbuntuArch" "$__CodeName" "$__RootfsDir" "$__UbuntuRepo"
+ echo running debootstrap "--variant=minbase" $__SigCheckArgs --arch "$__UbuntuArch" "$__CodeName" "$__RootfsDir" "$__UbuntuRepo"
# shellcheck disable=SC2086
- if ! debootstrap "--variant=minbase" $__Keyring --arch "$__UbuntuArch" "$__CodeName" "$__RootfsDir" "$__UbuntuRepo"; then
+ if ! debootstrap "--variant=minbase" $__SigCheckArgs --arch "$__UbuntuArch" "$__CodeName" "$__RootfsDir" "$__UbuntuRepo"; then
echo "debootstrap failed! dumping debootstrap.log"
cat "$__RootfsDir/debootstrap/debootstrap.log"
exit 1
@@ -816,6 +862,11 @@ Components: main universe
Signed-By: $__KeyringFile
EOF
+ __UpdateOptions=
+ if [[ "$__SkipSigCheck" == "1" ]]; then
+ __UpdateOptions="--allow-unauthenticated --allow-insecure-repositories"
+ fi
+
# shellcheck disable=SC2086
chroot "$__RootfsDir" apt-get update $__UpdateOptions
chroot "$__RootfsDir" apt-get -f -y install
diff --git a/eng/common/cross/install-debs.py b/eng/common/cross/install-debs.py
index c81eb37e522e..1d1dfabf7dc9 100644
--- a/eng/common/cross/install-debs.py
+++ b/eng/common/cross/install-debs.py
@@ -4,6 +4,7 @@
import asyncio
import aiohttp
import gzip
+import hashlib
import os
import re
import shutil
@@ -16,7 +17,7 @@
from collections import deque
from functools import cmp_to_key
-async def download_file(session, url, dest_path, max_retries=3, retry_delay=2, timeout=60):
+async def download_file(session, url, dest_path, max_retries=3, retry_delay=2, timeout=60, checksum=None):
"""Asynchronous file download with retries."""
attempt = 0
while attempt < max_retries:
@@ -25,19 +26,25 @@ async def download_file(session, url, dest_path, max_retries=3, retry_delay=2, t
if response.status == 200:
with open(dest_path, "wb") as f:
content = await response.read()
+
+ # verify checksum if provided
+ if checksum:
+ sha256 = hashlib.sha256(content).hexdigest()
+ if sha256 != checksum:
+ raise Exception(f"SHA256 mismatch for {url}: expected {checksum}, got {sha256}")
+
f.write(content)
print(f"Downloaded {url} at {dest_path}")
return
else:
- print(f"Failed to download {url}, Status Code: {response.status}")
- break
+ raise Exception(f"Failed to download {url}, Status Code: {response.status}")
except (asyncio.CancelledError, asyncio.TimeoutError, aiohttp.ClientError) as e:
print(f"Error downloading {url}: {type(e).__name__} - {e}. Retrying...")
attempt += 1
await asyncio.sleep(retry_delay)
- print(f"Failed to download {url} after {max_retries} attempts.")
+ raise Exception(f"Failed to download {url} after {max_retries} attempts.")
async def download_deb_files_parallel(mirror, packages, tmp_dir):
"""Download .deb files in parallel."""
@@ -51,11 +58,11 @@ async def download_deb_files_parallel(mirror, packages, tmp_dir):
if filename:
url = f"{mirror}/{filename}"
dest_path = os.path.join(tmp_dir, os.path.basename(filename))
- tasks.append(asyncio.create_task(download_file(session, url, dest_path)))
+ tasks.append(asyncio.create_task(download_file(session, url, dest_path, checksum=info.get("SHA256"))))
await asyncio.gather(*tasks)
-async def download_package_index_parallel(mirror, arch, suites):
+async def download_package_index_parallel(mirror, arch, suites, check_sig, keyring):
"""Download package index files for specified suites and components entirely in memory."""
tasks = []
timeout = aiohttp.ClientTimeout(total=60)
@@ -63,10 +70,9 @@ async def download_package_index_parallel(mirror, arch, suites):
async with aiohttp.ClientSession(timeout=timeout) as session:
for suite in suites:
for component in ["main", "universe"]:
- url = f"{mirror}/dists/{suite}/{component}/binary-{arch}/Packages.gz"
- tasks.append(fetch_and_decompress(session, url))
+ tasks.append(fetch_and_decompress(session, mirror, arch, suite, component, check_sig, keyring))
- results = await asyncio.gather(*tasks, return_exceptions=True)
+ results = await asyncio.gather(*tasks)
merged_content = ""
for result in results:
@@ -77,20 +83,75 @@ async def download_package_index_parallel(mirror, arch, suites):
return merged_content
-async def fetch_and_decompress(session, url):
+async def fetch_and_decompress(session, mirror, arch, suite, component, check_sig, keyring):
"""Fetch and decompress the Packages.gz file."""
- try:
- async with session.get(url) as response:
- if response.status == 200:
- compressed_data = await response.read()
- decompressed_data = gzip.decompress(compressed_data).decode('utf-8')
- print(f"Downloaded index: {url}")
- return decompressed_data
- else:
- print(f"Skipped index: {url} (doesn't exist)")
- return None
- except Exception as e:
- print(f"Error fetching {url}: {e}")
+
+ path = f"{component}/binary-{arch}/Packages.gz"
+ url = f"{mirror}/dists/{suite}/{path}"
+
+ async with session.get(url) as response:
+ if response.status == 200:
+ compressed_data = await response.read()
+ decompressed_data = gzip.decompress(compressed_data).decode('utf-8')
+ print(f"Downloaded index: {url}")
+
+ if check_sig:
+ # Verify the package index against the sha256 recorded in the Release file
+ release_file_content = await fetch_release_file(session, mirror, suite, keyring)
+ packages_sha = parse_release_file(release_file_content, path)
+
+ sha256 = hashlib.sha256(compressed_data).hexdigest()
+ if sha256 != packages_sha:
+ raise Exception(f"SHA256 mismatch for {path}: expected {packages_sha}, got {sha256}")
+ print(f"Checksum verified for {path}")
+
+ return decompressed_data
+ else:
+ print(f"Skipped index: {url} (doesn't exist)")
+ return None
+
+async def fetch_release_file(session, mirror, suite, keyring):
+ """Fetch Release and Release.gpg files and verify the signature."""
+
+ release_url = f"{mirror}/dists/{suite}/Release"
+ release_gpg_url = f"{mirror}/dists/{suite}/Release.gpg"
+
+ with tempfile.NamedTemporaryFile() as release_file, tempfile.NamedTemporaryFile() as release_gpg_file:
+ await download_file(session, release_url, release_file.name)
+ await download_file(session, release_gpg_url, release_gpg_file.name)
+
+ print("Verifying signature of Release with Release.gpg.")
+ # Use gpgv rather than gpg for verification. gpgv verifies a detached
+ # signature against a fixed keyring without involving gpg-agent or
+ # keyboxd, which makes it robust on hosts running GnuPG 2.4+ (e.g. Azure
+ # Linux) where "gpg --keyring" routes through keyboxd and can fail.
+ verify_command = ["gpgv"]
+ if keyring:
+ verify_command += ["--keyring", keyring]
+ verify_command += [release_gpg_file.name, release_file.name]
+ result = subprocess.run(verify_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ if result.returncode != 0:
+ raise Exception(f"Signature verification failed: {result.stderr.decode('utf-8')}")
+
+ print("Signature verified successfully.")
+
+ with open(release_file.name) as f:
+ return f.read()
+
+def parse_release_file(content, path):
+ """Parses the Release file and returns sha256 checksum of the specified path."""
+
+ # data looks like this:
+ #
+ matches = re.findall(r'^ (\S*) +(\S*) +(\S*)$', content, re.MULTILINE)
+
+ for entry in matches:
+ # the file has both md5 and sha256 checksums, we want sha256 which has a length of 64
+ if entry[2] == path and len(entry[0]) == 64:
+ return entry[0]
+
+ raise Exception(f"Could not find checksum for {path} in Release file.")
def parse_debian_version(version):
"""Parse a Debian package version into epoch, upstream version, and revision."""
@@ -171,13 +232,15 @@ def parse_package_index(content):
filename = fields.get("Filename")
depends = fields.get("Depends")
provides = fields.get("Provides", None)
+ sha256 = fields.get("SHA256")
# Only update if package_name is not in packages or if the new version is higher
if package_name not in packages or compare_debian_versions(version, packages[package_name]["Version"]) > 0:
packages[package_name] = {
"Version": version,
"Filename": filename,
- "Depends": depends
+ "Depends": depends,
+ "SHA256": sha256
}
# Update aliases if package provides any alternatives
@@ -233,7 +296,7 @@ def extract_deb_file(deb_file, tmp_dir, extract_dir, ar_tool):
os.makedirs(extract_dir, exist_ok=True)
with tempfile.TemporaryDirectory(dir=tmp_dir) as tmp_subdir:
- result = subprocess.run(f"{ar_tool} t {os.path.abspath(deb_file)}", cwd=tmp_subdir, check=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ result = subprocess.run([ar_tool, "t", os.path.abspath(deb_file)], cwd=tmp_subdir, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
tar_filename = None
for line in result.stdout.decode().splitlines():
@@ -247,7 +310,8 @@ def extract_deb_file(deb_file, tmp_dir, extract_dir, ar_tool):
tar_file_path = os.path.join(tmp_subdir, tar_filename)
print(f"Extracting {tar_filename} from {deb_file}..")
- subprocess.run(f"{ar_tool} p {os.path.abspath(deb_file)} {tar_filename} > {tar_file_path}", check=True, shell=True)
+ with open(tar_file_path, "wb") as outfile:
+ subprocess.run([ar_tool, "p", os.path.abspath(deb_file), tar_filename], check=True, stdout=outfile, stderr=subprocess.PIPE)
file_extension = os.path.splitext(tar_file_path)[1].lower()
@@ -268,7 +332,18 @@ def extract_deb_file(deb_file, tmp_dir, extract_dir, ar_tool):
raise ValueError(f"Unsupported compression format: {file_extension}")
with tarfile.open(tar_file_path, mode) as tar:
- tar.extractall(path=extract_dir, filter='fully_trusted')
+ tar.extractall(path=extract_dir, filter=_rootfs_extraction_filter)
+
+def _rootfs_extraction_filter(member, dest_path):
+ """Tarfile extraction filter based on the 'data' filter that additionally
+ rewrites absolute-target symlinks/hardlinks into rootfs-relative paths.
+ """
+ if (member.issym() or member.islnk()) and os.path.isabs(member.linkname):
+ link_dir = os.path.dirname(member.name)
+ new_linkname = os.path.relpath(member.linkname.lstrip('/'),
+ start=link_dir or '.')
+ member = member.replace(linkname=new_linkname, deep=False)
+ return tarfile.data_filter(member, dest_path)
def finalize_setup(rootfsdir):
lib_dir = os.path.join(rootfsdir, 'lib')
@@ -295,24 +370,17 @@ def finalize_setup(rootfsdir):
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generate rootfs for .NET runtime on Debian-like OS")
- parser.add_argument("--distro", required=False, help="Distro name (e.g., debian, ubuntu, etc.)")
parser.add_argument("--arch", required=True, help="Architecture (e.g., amd64, loong64, etc.)")
parser.add_argument("--rootfsdir", required=True, help="Destination directory.")
parser.add_argument('--suite', required=True, action='append', help='Specify one or more repository suites to collect index data.')
- parser.add_argument("--mirror", required=False, help="Mirror (e.g., http://ftp.debian.org/debian-ports etc.)")
+ parser.add_argument("--mirror", required=True, help="Mirror (e.g., http://ftp.debian.org/debian-ports etc.)")
parser.add_argument("--artool", required=False, default="ar", help="ar tool to extract debs (e.g., ar, llvm-ar etc.)")
+ parser.add_argument("--force-check-gpg", required=False, action='store_true', help="Verify the packages against signatures in Release file.")
+ parser.add_argument("--keyring", required=False, default='', help="Keyring file to check signature of Release file.")
parser.add_argument("packages", nargs="+", help="List of package names to be installed.")
args = parser.parse_args()
- if args.mirror is None:
- if args.distro == "ubuntu":
- args.mirror = "http://archive.ubuntu.com/ubuntu" if args.arch in ["amd64", "i386"] else "http://ports.ubuntu.com/ubuntu-ports"
- elif args.distro == "debian":
- args.mirror = "http://ftp.debian.org/debian-ports"
- else:
- raise Exception("Unsupported distro")
-
DESIRED_PACKAGES = args.packages + [ # base packages
"dpkg",
"busybox",
@@ -322,9 +390,16 @@ def finalize_setup(rootfsdir):
"debianutils"
]
- print(f"Creating rootfs. rootfsdir: {args.rootfsdir}, distro: {args.distro}, arch: {args.arch}, suites: {args.suite}, mirror: {args.mirror}")
+ print(f"Creating rootfs. rootfsdir: {args.rootfsdir}, arch: {args.arch}, suites: {args.suite}, mirror: {args.mirror}")
+
+ check_sig = args.force_check_gpg
+ if check_sig and not args.keyring:
+ print("ERROR: --force-check-gpg requires --keyring to specify a keyring file for signature verification.")
+ print("Install the appropriate keyring package (e.g., debian-ports-archive-keyring, ubuntu-archive-keyring)")
+ print("or pass --skipsigcheck to build-rootfs.sh to disable signature checking.")
+ sys.exit(1)
- package_index_content = asyncio.run(download_package_index_parallel(args.mirror, args.arch, args.suite))
+ package_index_content = asyncio.run(download_package_index_parallel(args.mirror, args.arch, args.suite, check_sig, args.keyring))
packages_info, aliases = parse_package_index(package_index_content)
diff --git a/eng/common/cross/toolchain.cmake b/eng/common/cross/toolchain.cmake
index 0ff85cf0367e..f65c689f6954 100644
--- a/eng/common/cross/toolchain.cmake
+++ b/eng/common/cross/toolchain.cmake
@@ -3,15 +3,22 @@ set(CROSS_ROOTFS $ENV{ROOTFS_DIR})
# reset platform variables (e.g. cmake 3.25 sets LINUX=1)
unset(LINUX)
unset(FREEBSD)
+unset(OPENBSD)
unset(ILLUMOS)
unset(ANDROID)
unset(TIZEN)
unset(HAIKU)
set(TARGET_ARCH_NAME $ENV{TARGET_BUILD_ARCH})
+
+file(GLOB OPENBSD_PROBE "${CROSS_ROOTFS}/etc/signify/openbsd-*.pub")
+
if(EXISTS ${CROSS_ROOTFS}/bin/freebsd-version)
set(CMAKE_SYSTEM_NAME FreeBSD)
set(FREEBSD 1)
+elseif(OPENBSD_PROBE)
+ set(CMAKE_SYSTEM_NAME OpenBSD)
+ set(OPENBSD 1)
elseif(EXISTS ${CROSS_ROOTFS}/usr/platform/i86pc)
set(CMAKE_SYSTEM_NAME SunOS)
set(ILLUMOS 1)
@@ -52,7 +59,9 @@ elseif(TARGET_ARCH_NAME STREQUAL "arm64")
set(TIZEN_TOOLCHAIN "aarch64-tizen-linux-gnu")
endif()
elseif(FREEBSD)
- set(triple "aarch64-unknown-freebsd12")
+ set(TOOLCHAIN "aarch64-unknown-freebsd14")
+ elseif(OPENBSD)
+ set(TOOLCHAIN "aarch64-unknown-openbsd")
endif()
elseif(TARGET_ARCH_NAME STREQUAL "armel")
set(CMAKE_SYSTEM_PROCESSOR armv7l)
@@ -108,7 +117,9 @@ elseif(TARGET_ARCH_NAME STREQUAL "x64")
set(TIZEN_TOOLCHAIN "x86_64-tizen-linux-gnu")
endif()
elseif(FREEBSD)
- set(triple "x86_64-unknown-freebsd12")
+ set(TOOLCHAIN "x86_64-unknown-freebsd14")
+ elseif(OPENBSD)
+ set(TOOLCHAIN "x86_64-unknown-openbsd")
elseif(ILLUMOS)
set(TOOLCHAIN "x86_64-illumos")
elseif(HAIKU)
@@ -148,9 +159,6 @@ if(TIZEN)
else()
find_toolchain_dir("${CROSS_ROOTFS}/usr/lib64/gcc/${TIZEN_TOOLCHAIN}")
endif()
-
- message(STATUS "TIZEN_TOOLCHAIN_PATH set to: ${TIZEN_TOOLCHAIN_PATH}")
-
include_directories(SYSTEM ${TIZEN_TOOLCHAIN_PATH}/include/c++)
include_directories(SYSTEM ${TIZEN_TOOLCHAIN_PATH}/include/c++/${TIZEN_TOOLCHAIN})
endif()
@@ -193,11 +201,11 @@ if(ANDROID)
# include official NDK toolchain script
include(${CROSS_ROOTFS}/../build/cmake/android.toolchain.cmake)
-elseif(FREEBSD)
+elseif(FREEBSD OR OPENBSD)
# we cross-compile by instructing clang
- set(CMAKE_C_COMPILER_TARGET ${triple})
- set(CMAKE_CXX_COMPILER_TARGET ${triple})
- set(CMAKE_ASM_COMPILER_TARGET ${triple})
+ set(CMAKE_C_COMPILER_TARGET ${TOOLCHAIN})
+ set(CMAKE_CXX_COMPILER_TARGET ${TOOLCHAIN})
+ set(CMAKE_ASM_COMPILER_TARGET ${TOOLCHAIN})
set(CMAKE_SYSROOT "${CROSS_ROOTFS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=lld")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=lld")
@@ -214,13 +222,19 @@ elseif(ILLUMOS)
locate_toolchain_exec(g++ CMAKE_CXX_COMPILER)
elseif(HAIKU)
set(CMAKE_SYSROOT "${CROSS_ROOTFS}")
- set(CMAKE_PROGRAM_PATH "${CMAKE_PROGRAM_PATH};${CROSS_ROOTFS}/cross-tools-x86_64/bin")
set(CMAKE_SYSTEM_PREFIX_PATH "${CROSS_ROOTFS}")
set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} -lssp")
set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_CXX_STANDARD_LIBRARIES} -lssp")
- locate_toolchain_exec(gcc CMAKE_C_COMPILER)
- locate_toolchain_exec(g++ CMAKE_CXX_COMPILER)
+ if ("$ENV{CCC_CC}" MATCHES ".*gcc.*")
+ set(CMAKE_PROGRAM_PATH "${CMAKE_PROGRAM_PATH};${CROSS_ROOTFS}/cross-tools-x86_64/bin")
+ locate_toolchain_exec(gcc CMAKE_C_COMPILER)
+ locate_toolchain_exec(g++ CMAKE_CXX_COMPILER)
+ else()
+ set(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/cross-tools-x86_64")
+ set(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/cross-tools-x86_64")
+ set(CMAKE_ASM_COMPILER_EXTERNAL_TOOLCHAIN "${CROSS_ROOTFS}/cross-tools-x86_64")
+ endif()
# let CMake set up the correct search paths
include(Platform/Haiku)
@@ -291,7 +305,7 @@ endif()
# Specify compile options
-if((TARGET_ARCH_NAME MATCHES "^(arm|arm64|armel|armv6|loongarch64|ppc64le|riscv64|s390x|x64|x86)$" AND NOT ANDROID AND NOT FREEBSD) OR ILLUMOS OR HAIKU)
+if((TARGET_ARCH_NAME MATCHES "^(arm|arm64|armel|armv6|loongarch64|ppc64le|riscv64|s390x|x64|x86)$" AND NOT ANDROID AND NOT FREEBSD AND NOT OPENBSD) OR ILLUMOS OR HAIKU)
set(CMAKE_C_COMPILER_TARGET ${TOOLCHAIN})
set(CMAKE_CXX_COMPILER_TARGET ${TOOLCHAIN})
set(CMAKE_ASM_COMPILER_TARGET ${TOOLCHAIN})
diff --git a/eng/common/darc-init.ps1 b/eng/common/darc-init.ps1
index e33743105635..a5be41db6906 100644
--- a/eng/common/darc-init.ps1
+++ b/eng/common/darc-init.ps1
@@ -29,11 +29,11 @@ function InstallDarcCli ($darcVersion, $toolpath) {
Write-Host "Installing Darc CLI version $darcVersion..."
Write-Host 'You may need to restart your command window if this is the first dotnet tool you have installed.'
if (-not $toolpath) {
- Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --add-source '$arcadeServicesSource' -v $verbosity -g"
- & "$dotnet" tool install $darcCliPackageName --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity -g
+ Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --source '$arcadeServicesSource' -v $verbosity -g"
+ & "$dotnet" tool install $darcCliPackageName --version $darcVersion --source "$arcadeServicesSource" -v $verbosity -g
}else {
- Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --add-source '$arcadeServicesSource' -v $verbosity --tool-path '$toolpath'"
- & "$dotnet" tool install $darcCliPackageName --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity --tool-path "$toolpath"
+ Write-Host "'$dotnet' tool install $darcCliPackageName --version $darcVersion --source '$arcadeServicesSource' -v $verbosity --tool-path '$toolpath'"
+ & "$dotnet" tool install $darcCliPackageName --version $darcVersion --source "$arcadeServicesSource" -v $verbosity --tool-path "$toolpath"
}
}
diff --git a/eng/common/darc-init.sh b/eng/common/darc-init.sh
index e889f439b8dc..e6ba4ee28c11 100644
--- a/eng/common/darc-init.sh
+++ b/eng/common/darc-init.sh
@@ -73,9 +73,9 @@ function InstallDarcCli {
echo "Installing Darc CLI version $darcVersion..."
echo "You may need to restart your command shell if this is the first dotnet tool you have installed."
if [ -z "$toolpath" ]; then
- echo $($dotnet_root/dotnet tool install $darc_cli_package_name --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity -g)
+ echo $($dotnet_root/dotnet tool install $darc_cli_package_name --version $darcVersion --source "$arcadeServicesSource" -v $verbosity -g)
else
- echo $($dotnet_root/dotnet tool install $darc_cli_package_name --version $darcVersion --add-source "$arcadeServicesSource" -v $verbosity --tool-path "$toolpath")
+ echo $($dotnet_root/dotnet tool install $darc_cli_package_name --version $darcVersion --source "$arcadeServicesSource" -v $verbosity --tool-path "$toolpath")
fi
}
diff --git a/eng/common/native/LocateNativeCompiler.targets b/eng/common/native/LocateNativeCompiler.targets
new file mode 100644
index 000000000000..028b33d94441
--- /dev/null
+++ b/eng/common/native/LocateNativeCompiler.targets
@@ -0,0 +1,27 @@
+
+
+
+
+ clang
+ $(ROOTFS_DIR)
+
+
+
+
+
+
+
+ $(_CC_LDFLAGS.SubString(0, $(_CC_LDFLAGS.IndexOf(';'))))
+ <_LDFLAGS>$(_CC_LDFLAGS.SubString($([MSBuild]::Add($(_CC_LDFLAGS.IndexOf(';')), 1))))
+ lld
+
+
+
diff --git a/eng/common/native/NativeAotSupported.props b/eng/common/native/NativeAotSupported.props
new file mode 100644
index 000000000000..559a6663929e
--- /dev/null
+++ b/eng/common/native/NativeAotSupported.props
@@ -0,0 +1,26 @@
+
+
+
+
+ <_NativeAotSupportedOS Condition="
+ '$(TargetOS)' != 'browser' and
+ '$(TargetOS)' != 'haiku' and
+ '$(TargetOS)' != 'illumos' and
+ '$(TargetOS)' != 'netbsd' and
+ '$(TargetOS)' != 'solaris'
+ ">true
+
+
+ <_NativeAotSupportedArch Condition="
+ '$(TargetArchitecture)' != 'wasm' and
+ ('$(TargetArchitecture)' != 'x86' or '$(TargetOS)' == 'windows')
+ ">true
+
+ true
+
+
+
diff --git a/eng/common/native/init-distro-rid.sh b/eng/common/native/init-distro-rid.sh
index 83ea7aab0e08..8fc6d2fec78d 100644
--- a/eng/common/native/init-distro-rid.sh
+++ b/eng/common/native/init-distro-rid.sh
@@ -39,6 +39,8 @@ getNonPortableDistroRid()
# $rootfsDir can be empty. freebsd-version is a shell script and should always work.
__freebsd_major_version=$("$rootfsDir"/bin/freebsd-version | cut -d'.' -f1)
nonPortableRid="freebsd.$__freebsd_major_version-${targetArch}"
+ elif [ "$targetOs" = "openbsd" ]; then
+ nonPortableRid="openbsd.$(uname -r)-${targetArch}"
elif command -v getprop >/dev/null && getprop ro.product.system.model | grep -qi android; then
__android_sdk_version=$(getprop ro.build.version.sdk)
nonPortableRid="android.$__android_sdk_version-${targetArch}"
diff --git a/eng/common/native/install-dependencies.sh b/eng/common/native/install-dependencies.sh
index 477a44f335be..aff839fa0974 100644
--- a/eng/common/native/install-dependencies.sh
+++ b/eng/common/native/install-dependencies.sh
@@ -24,14 +24,16 @@ case "$os" in
apt update
apt install -y build-essential gettext locales cmake llvm clang lld lldb liblldb-dev libunwind8-dev libicu-dev liblttng-ust-dev \
- libssl-dev libkrb5-dev pigz cpio
+ libssl-dev libkrb5-dev pigz cpio ninja-build file
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8
- elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ]; then
+ elif [ "$ID" = "fedora" ] || [ "$ID" = "rhel" ] || [ "$ID" = "azurelinux" ] || [ "$ID" = "centos" ]; then
pkg_mgr="$(command -v tdnf 2>/dev/null || command -v dnf)"
- $pkg_mgr install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio
+ $pkg_mgr install -y cmake llvm lld lldb clang python curl libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio ninja-build file
+ elif [ "$ID" = "amzn" ]; then
+ dnf install -y cmake llvm lld lldb clang python libicu-devel openssl-devel krb5-devel lttng-ust-devel pigz cpio ninja-build file
elif [ "$ID" = "alpine" ]; then
- apk add build-base cmake bash curl clang llvm-dev lld lldb krb5-dev lttng-ust-dev icu-dev openssl-dev pigz cpio
+ apk add build-base cmake bash curl clang llvm llvm-dev lld lldb-dev krb5-dev lttng-ust-dev icu-dev openssl-dev pigz cpio ninja file
else
echo "Unsupported distro. distro: $ID"
exit 1
@@ -52,6 +54,7 @@ brew "openssl@3"
brew "pkgconf"
brew "python3"
brew "pigz"
+brew "ninja"
EOF
;;
diff --git a/eng/common/post-build/redact-logs.ps1 b/eng/common/post-build/redact-logs.ps1
index 472d5bb562c9..c1e4104b79a4 100644
--- a/eng/common/post-build/redact-logs.ps1
+++ b/eng/common/post-build/redact-logs.ps1
@@ -48,8 +48,8 @@ try {
Write-Host "Installing Binlog redactor CLI..."
Write-Host "'$dotnet' new tool-manifest"
& "$dotnet" new tool-manifest
- Write-Host "'$dotnet' tool install $packageName --local --add-source '$PackageFeed' -v $verbosity --version $BinlogToolVersion"
- & "$dotnet" tool install $packageName --local --add-source "$PackageFeed" -v $verbosity --version $BinlogToolVersion
+ Write-Host "'$dotnet' tool install $packageName --local --source '$PackageFeed' -v $verbosity --version $BinlogToolVersion"
+ & "$dotnet" tool install $packageName --local --source "$PackageFeed" -v $verbosity --version $BinlogToolVersion
if (Test-Path $TokensFilePath) {
Write-Host "Adding additional sensitive data for redaction from file: " $TokensFilePath
diff --git a/eng/common/templates/steps/vmr-sync.yml b/eng/common/templates/steps/vmr-sync.yml
index eb619c502683..cdc6a28ff1f6 100644
--- a/eng/common/templates/steps/vmr-sync.yml
+++ b/eng/common/templates/steps/vmr-sync.yml
@@ -45,11 +45,11 @@ steps:
workingDirectory: ${{ parameters.vmrPath }}
- script: |
- ./eng/common/vmr-sync.sh \
- --vmr ${{ parameters.vmrPath }} \
- --tmp $(Agent.TempDirectory) \
- --azdev-pat '$(dn-bot-all-orgs-code-r)' \
- --ci \
+ ./eng/common/vmr-sync.sh \
+ --vmr ${{ parameters.vmrPath }} \
+ --tmp $(Agent.TempDirectory) \
+ --azdev-pat '$(AzdoToken)' \
+ --ci \
--debug
if [ "$?" -ne 0 ]; then
@@ -67,11 +67,11 @@ steps:
condition: eq(variables['Agent.OS'], 'Windows_NT')
- powershell: |
- ./eng/common/vmr-sync.ps1 `
- -vmr ${{ parameters.vmrPath }} `
- -tmp $(Agent.TempDirectory) `
- -azdevPat '$(dn-bot-all-orgs-code-r)' `
- -ci `
+ ./eng/common/vmr-sync.ps1 `
+ -vmr ${{ parameters.vmrPath }} `
+ -tmp $(Agent.TempDirectory) `
+ -azdevPat '$(AzdoToken)' `
+ -ci `
-debugOutput
if ($LASTEXITCODE -ne 0) {
diff --git a/eng/common/templates/vmr-build-pr.yml b/eng/common/templates/vmr-build-pr.yml
index 2f3694fa1323..d24de935248a 100644
--- a/eng/common/templates/vmr-build-pr.yml
+++ b/eng/common/templates/vmr-build-pr.yml
@@ -33,7 +33,7 @@ resources:
- repository: vmr
type: github
name: dotnet/dotnet
- endpoint: dotnet
+ endpoint: public
ref: refs/heads/main # Set to whatever VMR branch the PR build should insert into
stages:
diff --git a/global.json b/global.json
index 0b4ff331337e..32005f79bfa6 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "10.0.400-preview.0.26356.110",
+ "version": "10.0.400-preview.0.26357.110",
"paths": [
"builds/downloads/dotnet",
"$host$"
@@ -8,9 +8,9 @@
"errorMessage": "The .NET SDK could not be found, please run 'make dotnet -C builds'."
},
"tools": {
- "dotnet": "10.0.400-preview.0.26356.110"
+ "dotnet": "10.0.400-preview.0.26357.110"
},
"msbuild-sdks": {
- "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26356.110"
+ "Microsoft.DotNet.Arcade.Sdk": "10.0.0-beta.26357.110"
}
}
diff --git a/msbuild/Xamarin.MacDev.Tasks/Tasks/PrepareAssemblies.cs b/msbuild/Xamarin.MacDev.Tasks/Tasks/PrepareAssemblies.cs
index 9a94d2bb4cb8..fb9e61e6d893 100644
--- a/msbuild/Xamarin.MacDev.Tasks/Tasks/PrepareAssemblies.cs
+++ b/msbuild/Xamarin.MacDev.Tasks/Tasks/PrepareAssemblies.cs
@@ -75,6 +75,13 @@ public override bool Execute ()
rv = preparer.Prepare (out exceptions);
}
+ var totalDuration = TimeSpan.Zero;
+ foreach (var step in preparer.StepExecutions) {
+ totalDuration += step.Duration;
+ Log.LogMessage (MessageImportance.Low, $"{step.Duration.ToString (@"hh\:mm\:ss\.fffffff")} {step.Name}: {(step.ModifiedAssemblies ? " ✏️ modified one or more assemblies" : " ✅ did not modify any assemblies")}");
+ }
+ Log.LogMessage (MessageImportance.Low, $"{totalDuration.ToString (@"hh\:mm\:ss\.fffffff")} Total for all steps");
+
foreach (var pe in exceptions) {
if (pe.IsError (this)) {
((IToolLog) this).LogError (pe);
diff --git a/msbuild/Xamarin.Shared/Xamarin.Shared.targets b/msbuild/Xamarin.Shared/Xamarin.Shared.targets
index edb94b8b57b2..413442c5494a 100644
--- a/msbuild/Xamarin.Shared/Xamarin.Shared.targets
+++ b/msbuild/Xamarin.Shared/Xamarin.Shared.targets
@@ -3502,7 +3502,7 @@ Copyright (C) 2018 Microsoft. All rights reserved.
Name="_PrepareAssemblies"
Condition="'$(PrepareAssemblies)' == 'true'"
Inputs="@(_AssembliesToPrepare)"
- Outputs="@(_AssembliesToPrepare->'$(PreparedAssembliesDirectory)%(Filename)%(Extension)')"
+ Outputs="@(_AssembliesToPrepare->'$(PreparedAssembliesDirectory)%(Filename)%(Extension)');$(_AssemblyPreparerMSBuildOutputFile)"
DependsOnTargets="_PrepareAssembliesForPreparation;_ComputeAssembliesToPostprocessOnPublish;SelectRegistrar"
>
+
@@ -3565,6 +3566,7 @@ Copyright (C) 2018 Microsoft. All rights reserved.
+
diff --git a/msbuild/Xamarin.iOS.Tasks.Windows/Xamarin.Messaging.targets b/msbuild/Xamarin.iOS.Tasks.Windows/Xamarin.Messaging.targets
index 9a07dc40030d..0341c8a3cf17 100644
--- a/msbuild/Xamarin.iOS.Tasks.Windows/Xamarin.Messaging.targets
+++ b/msbuild/Xamarin.iOS.Tasks.Windows/Xamarin.Messaging.targets
@@ -71,7 +71,7 @@
-
+
{
+ if (e.Data is not null)
+ lock (writer)
+ writer.WriteLine (e.Data);
+ };
+ logStreamProcess.ErrorDataReceived += (_, e) => {
+ if (e.Data is not null)
+ lock (writer)
+ writer.WriteLine (e.Data);
+ };
+ logStreamProcess.Start ();
+ logStreamProcess.BeginOutputReadLine ();
+ logStreamProcess.BeginErrorReadLine ();
+
+ Console.WriteLine ($"Started 'log stream' (pid {logStreamProcess.Id}), writing to {logStreamFile}");
+}
+
var isAppleSilicon = RuntimeInformation.ProcessArchitecture == Architecture.Arm64 ||
Environment.GetEnvironmentVariable ("PROCESSOR_ARCHITECTURE")?.Contains ("ARM", StringComparison.OrdinalIgnoreCase) == true;
@@ -267,6 +302,34 @@
Console.WriteLine ($"HTML report written to {htmlReportPath}");
}
+// Stop 'log stream' and zip the output
+if (logStreamProcess is not null && logStreamFile is not null) {
+ try {
+ NativeMethods.kill (logStreamProcess.Id, 2 /* SIGINT */);
+ logStreamProcess.WaitForExit (10_000);
+ } catch {
+ // Process may have already exited
+ }
+
+ // Flush and close the log writer
+ logStreamWriter?.Dispose ();
+
+ try {
+ Console.WriteLine ($"Wrote {new FileInfo (logStreamFile).Length} bytes to {logStreamFile}");
+
+ // Zip the log file
+ var zipPath = logStreamFile + ".zip";
+ using (var zip = ZipFile.Open (zipPath, ZipArchiveMode.Create))
+ zip.CreateEntryFromFile (logStreamFile, Path.GetFileName (logStreamFile), CompressionLevel.Optimal);
+ File.Delete (logStreamFile);
+ Console.WriteLine ($"Compressed log stream to {zipPath}");
+ } catch (Exception ex) {
+ Console.Error.WriteLine ($"Warning: Failed to save log stream output: {ex.Message}");
+ }
+
+ logStreamProcess.Dispose ();
+}
+
return failedSuites > 0 ? 1 : 0;
// ===== Helper methods =====
diff --git a/src/Foundation/ModelAttribute.cs b/src/Foundation/ModelAttribute.cs
index f90e249bb2f0..78465d73b284 100644
--- a/src/Foundation/ModelAttribute.cs
+++ b/src/Foundation/ModelAttribute.cs
@@ -25,19 +25,23 @@
namespace Foundation {
- ///
+ /// Flags a class or interface as a model, indicating that only explicitly overridden methods are exposed to the Objective-C runtime.
+ ///
+ /// Objective-C protocols are like interfaces, but they support optional methods, that is, not all of the methods need to be implemented for the protocol to work.
+ /// The [Model] attribute is applied to a class that can have optional methods, and it is typically used for declaring Objective-C delegates or data models that have a number of optional methods. The runtime treats classes with the Model attribute applied specially: only when a user overrides methods in a class is the actual override exposed to the Objective-C world as existing.
+ /// This attribute is used in all of the models and delegate classes to allow the user to only implement the methods that they are interested in.
+ ///
[AttributeUsage (AttributeTargets.Class | AttributeTargets.Interface)]
public sealed class ModelAttribute : Attribute {
- /// Default constructor.
- /// To be added.
+ /// Initializes a new instance of the class.
public ModelAttribute () { }
- /// Specifies if the Objective-C type name for the model.
- /// The Objective-C type name for the model.
- ///
- /// This value is only taken into account when used in binding code / projects.
- ///
+ /// Gets or sets the Objective-C type name for the model.
+ /// The Objective-C type name for the model.
+ ///
+ /// This value is only taken into account when used in binding code / projects.
+ ///
public string? Name { get; set; }
}
}
diff --git a/src/ILLink.Substitutions.MacCatalyst.xml b/src/ILLink.Substitutions.MacCatalyst.xml
index da45aabaf46c..6cf57b701e0a 100644
--- a/src/ILLink.Substitutions.MacCatalyst.xml
+++ b/src/ILLink.Substitutions.MacCatalyst.xml
@@ -10,6 +10,8 @@
+
+
diff --git a/src/ILLink.Substitutions.iOS.xml b/src/ILLink.Substitutions.iOS.xml
index 3436035d8aff..e778b006756d 100644
--- a/src/ILLink.Substitutions.iOS.xml
+++ b/src/ILLink.Substitutions.iOS.xml
@@ -12,6 +12,8 @@
+
+
diff --git a/src/ILLink.Substitutions.macOS.xml b/src/ILLink.Substitutions.macOS.xml
index d93fac786837..3c1ddefda20d 100644
--- a/src/ILLink.Substitutions.macOS.xml
+++ b/src/ILLink.Substitutions.macOS.xml
@@ -9,6 +9,8 @@
+
+
diff --git a/src/ILLink.Substitutions.tvOS.xml b/src/ILLink.Substitutions.tvOS.xml
index 7131bcf39363..4ea0b88f2fe3 100644
--- a/src/ILLink.Substitutions.tvOS.xml
+++ b/src/ILLink.Substitutions.tvOS.xml
@@ -12,6 +12,8 @@
+
+
diff --git a/src/StoreKit/Enums.cs b/src/StoreKit/Enums.cs
index 9c1169aa7b45..eb5058aa6475 100644
--- a/src/StoreKit/Enums.cs
+++ b/src/StoreKit/Enums.cs
@@ -49,20 +49,20 @@ public enum SKError : long {
/// Indicates that the cloud service network connection failed.
CloudServiceNetworkConnectionFailed,
// iOS 10.3
- /// To be added.
+ /// Indicates that the cloud service has been revoked.
CloudServiceRevoked,
// iOS 12.2
- /// To be added.
+ /// Indicates that a privacy acknowledgement is required before proceeding.
PrivacyAcknowledgementRequired,
- /// To be added.
+ /// Indicates that the request data is unauthorized.
UnauthorizedRequestData,
- /// To be added.
+ /// Indicates that the offer identifier is invalid.
InvalidOfferIdentifier,
- /// To be added.
+ /// Indicates that the signature is invalid.
InvalidSignature,
- /// To be added.
+ /// Indicates that required offer parameters are missing.
MissingOfferParams,
- /// To be added.
+ /// Indicates that the offer price is invalid.
InvalidOfferPrice,
OverlayCancelled = 15,
diff --git a/tests/common/shared-dotnet.mk b/tests/common/shared-dotnet.mk
index 21f5f72ba6fe..c8448bdb0225 100644
--- a/tests/common/shared-dotnet.mk
+++ b/tests/common/shared-dotnet.mk
@@ -150,8 +150,12 @@ else ifeq ($(PLATFORM),tvOS)
EXECUTABLE="$(abspath .)/bin/$(CONFIG)/$(TEST_TFM)-tvos/$(PATH_RID)$(TESTNAME).app/$(TESTNAME)"
else ifeq ($(PLATFORM),MacCatalyst)
EXECUTABLE="$(abspath .)/bin/$(CONFIG)/$(TEST_TFM)-maccatalyst/$(PATH_RID)$(TESTNAME).app/Contents/MacOS/$(TESTNAME)"
+APPMANIFEST:=$(abspath .)/bin/$(CONFIG)/$(TEST_TFM)-maccatalyst/$(PATH_RID)$(TESTNAME).app/Contents/Info.plist
+BUNDLE_ID=$(shell defaults read "$(APPMANIFEST)" CFBundleIdentifier 2>/dev/null)
else ifeq ($(PLATFORM),macOS)
EXECUTABLE="$(abspath .)/bin/$(CONFIG)/$(TEST_TFM)-macos/$(PATH_RID)$(TESTNAME).app/Contents/MacOS/$(TESTNAME)"
+APPMANIFEST:=$(abspath .)/bin/$(CONFIG)/$(TEST_TFM)-macos/$(PATH_RID)$(TESTNAME).app/Contents/Info.plist
+BUNDLE_ID=$(shell defaults read "$(APPMANIFEST)" CFBundleIdentifier 2>/dev/null)
else
EXECUTABLE="unknown-executable-platform-$(PLATFORM)"
endif
@@ -187,8 +191,15 @@ run: prepare
@echo "Running $(wildcard *.?sproj)..."
$(Q) $(DOTNET) build "/bl:$(abspath $@-$(BINLOG_TIMESTAMP).binlog)" *.?sproj $(DOTNET_BUILD_VERBOSITY) $(BUILD_ARGUMENTS) $(CONFIG_ARGUMENT) $(UNIVERSAL_ARGUMENT) $(NATIVEAOT_ARGUMENTS) $(TEST_VARIATION_ARGUMENT) -t:Run $(RUN_ARGUMENTS) -tl:off
-run-bare:
+# Delete the saved application state for the app, to prevent the
+# "Do you want to try to reopen its windows again?" dialog from
+# blocking automated runs after a crash. See https://github.com/dotnet/macios/issues/25922
+delete-saved-state:
+ $(Q) if test -n "$(BUNDLE_ID)"; then rm -rf "$(HOME)/Library/Saved Application State/$(BUNDLE_ID).savedState" && echo "Deleted saved state for $(BUNDLE_ID)"; fi
+
+run-bare: delete-saved-state
$(Q) $(EXECUTABLE) --autostart --autoexit $(RUN_ARGUMENTS)
+ $(Q) $(MAKE) delete-saved-state
# Get the list of applicable simulators, and pick the first in the list.
# Make sure to have a matching simulator runtime installed, otherwise this won't work.
diff --git a/tests/dotnet/UnitTests/DynamicRegistrationSupportedTest.cs b/tests/dotnet/UnitTests/DynamicRegistrationSupportedTest.cs
new file mode 100644
index 000000000000..3bf5c7cfe633
--- /dev/null
+++ b/tests/dotnet/UnitTests/DynamicRegistrationSupportedTest.cs
@@ -0,0 +1,85 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.Build.Framework;
+using Microsoft.Build.Logging.StructuredLogger;
+
+#nullable enable
+
+namespace Xamarin.Tests {
+ [TestFixture]
+ public class DynamicRegistrationSupportedTest : TestBaseClass {
+ [Test]
+ [TestCase (ApplePlatform.iOS, "iossimulator-arm64", "true")]
+ [TestCase (ApplePlatform.iOS, "iossimulator-arm64", "false")]
+ public void UserSpecifiedValue (ApplePlatform platform, string runtimeIdentifiers, string dynamicRegistrationSupported)
+ {
+ // When the user sets $(DynamicRegistrationSupported), the value must be passed straight through to
+ // the 'ObjCRuntime.Runtime.DynamicRegistrationSupported' trimmer feature switch (the assembly-preparer
+ // doesn't need to compute it, so RegistrarRemovalTrackingStep is skipped).
+ var project = "MySimpleApp";
+ Configuration.IgnoreIfIgnoredPlatform (platform);
+ Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers);
+
+ var project_path = GetProjectPath (project, platform: platform);
+ Clean (project_path);
+ var properties = GetDefaultProperties (runtimeIdentifiers);
+ // DynamicRegistrationSupported maps to the 'remove-dynamic-registrar' optimization, which is only
+ // valid with a static registrar, so use one (managed-static) and enable trimming.
+ properties ["MtouchLink"] = "SdkOnly";
+ properties ["Registrar"] = "managed-static";
+ properties ["PrepareAssemblies"] = "true";
+ properties ["PostProcessAssemblies"] = "true";
+ properties ["DynamicRegistrationSupported"] = dynamicRegistrationSupported;
+
+ var rv = DotNet.AssertBuild (project_path, properties);
+
+ var featureSwitch = GetRuntimeHostConfigurationOption (rv.BinLogPath, "ObjCRuntime.Runtime.DynamicRegistrationSupported");
+ Assert.That (featureSwitch, Is.Not.Null, "The DynamicRegistrationSupported feature switch must be set.");
+ Assert.That (featureSwitch?.GetMetadata ("Value"), Is.EqualTo (dynamicRegistrationSupported), "The feature switch value must match the user-specified value.");
+ }
+
+ [Test]
+ [TestCase (ApplePlatform.iOS, "iossimulator-arm64")]
+ public void SkippedWhenPlatformAssemblyNotTrimmed (ApplePlatform platform, string runtimeIdentifiers)
+ {
+ // When the platform assembly isn't being trimmed (link mode None), the dynamic registrar can't be
+ // removed, so RegistrarRemovalTrackingStep is skipped and no DynamicRegistrationSupported feature
+ // switch is emitted (the dynamic registrar is kept, which is the default).
+ var project = "MySimpleApp";
+ Configuration.IgnoreIfIgnoredPlatform (platform);
+ Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers);
+
+ var project_path = GetProjectPath (project, platform: platform);
+ Clean (project_path);
+ var properties = GetDefaultProperties (runtimeIdentifiers);
+ properties ["MtouchLink"] = "None";
+ properties ["PrepareAssemblies"] = "true";
+ properties ["PostProcessAssemblies"] = "true";
+
+ var rv = DotNet.AssertBuild (project_path, properties);
+
+ var featureSwitch = GetRuntimeHostConfigurationOption (rv.BinLogPath, "ObjCRuntime.Runtime.DynamicRegistrationSupported");
+ Assert.That (featureSwitch, Is.Null, "No DynamicRegistrationSupported feature switch should be set when the platform assembly isn't trimmed.");
+ }
+
+ // Returns the last RuntimeHostConfigurationOption item with the given name (ItemSpec) added during the build.
+ static ITaskItem? GetRuntimeHostConfigurationOption (string binLogPath, string name)
+ {
+ ITaskItem? rv = null;
+ foreach (var args in BinLog.ReadBuildEvents (binLogPath)) {
+ if (args is not TaskParameterEventArgs tpea)
+ continue;
+ if (tpea.Kind != TaskParameterMessageKind.AddItem)
+ continue;
+ if (tpea.ItemType != "RuntimeHostConfigurationOption")
+ continue;
+ foreach (var item in tpea.Items) {
+ if (item is ITaskItem taskItem && taskItem.ItemSpec == name)
+ rv = taskItem;
+ }
+ }
+ return rv;
+ }
+ }
+}
diff --git a/tests/dotnet/UnitTests/ProjectTest.cs b/tests/dotnet/UnitTests/ProjectTest.cs
index 193984cb94ac..1bce8afd73cf 100644
--- a/tests/dotnet/UnitTests/ProjectTest.cs
+++ b/tests/dotnet/UnitTests/ProjectTest.cs
@@ -2245,15 +2245,16 @@ public void PluralRuntimeIdentifiers (ApplePlatform platform, string runtimeIden
PluralRuntimeIdentifiersImpl (platform, runtimeIdentifiers);
}
- internal static void PluralRuntimeIdentifiersImpl (ApplePlatform platform, string runtimeIdentifiers, Dictionary? extraProperties = null)
+ internal static void PluralRuntimeIdentifiersImpl (ApplePlatform platform, string runtimeIdentifiers, Dictionary? extraProperties = null, string configuration = "Debug")
{
var project = "MySimpleApp";
Configuration.IgnoreIfIgnoredPlatform (platform);
Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers);
- var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath);
+ var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath, configuration: configuration);
Clean (project_path);
var properties = GetDefaultProperties (extraProperties: extraProperties);
+ properties ["Configuration"] = configuration;
properties ["RuntimeIdentifiers"] = runtimeIdentifiers;
DotNet.AssertBuild (project_path, properties);
diff --git a/tests/dotnet/UnitTests/TestBaseClass.cs b/tests/dotnet/UnitTests/TestBaseClass.cs
index fbe234b535a6..f04e046c1e2c 100644
--- a/tests/dotnet/UnitTests/TestBaseClass.cs
+++ b/tests/dotnet/UnitTests/TestBaseClass.cs
@@ -480,6 +480,8 @@ protected Execution Execute (string executable, out string output, out string ma
if (!File.Exists (executable))
throw new FileNotFoundException ($"The executable '{executable}' does not exists.");
+ DeleteSavedState (executable);
+
magicWord = Guid.NewGuid ().ToString ();
var env = new Dictionary {
{ "MAGIC_WORD", magicWord },
@@ -492,9 +494,55 @@ protected Execution Execute (string executable, out string output, out string ma
var rv = Execution.RunAsync (executable, Array.Empty (), environment: env, timeout: TimeSpan.FromSeconds (30)).Result;
output = rv.Output.MergedOutput;
+
+ DeleteSavedState (executable);
+
return rv;
}
+ // Delete the saved application state for the app being launched, to prevent
+ // the "Do you want to try to reopen its windows again?" dialog from showing
+ // if the app crashed during a previous test run. See https://github.com/dotnet/macios/issues/25922
+ static void DeleteSavedState (string executable)
+ {
+ // Find the .app bundle directory from the executable path
+ var dir = Path.GetDirectoryName (executable);
+ while (!string.IsNullOrEmpty (dir) && !dir.EndsWith (".app", StringComparison.OrdinalIgnoreCase))
+ dir = Path.GetDirectoryName (dir);
+
+ if (string.IsNullOrEmpty (dir))
+ return;
+
+ // Read the bundle identifier from Info.plist
+ string? bundleIdentifier = null;
+ var infoPlistPath = Path.Combine (dir, "Contents", "Info.plist");
+ if (!File.Exists (infoPlistPath))
+ infoPlistPath = Path.Combine (dir, "Info.plist");
+ if (!File.Exists (infoPlistPath))
+ return;
+
+ try {
+ var infoPlist = PDictionary.OpenFile (infoPlistPath);
+ bundleIdentifier = infoPlist.GetString ("CFBundleIdentifier")?.Value;
+ } catch (Exception e) {
+ Console.WriteLine ($"Could not read bundle identifier from '{infoPlistPath}': {e.Message}");
+ return;
+ }
+
+ if (string.IsNullOrEmpty (bundleIdentifier))
+ return;
+
+ var savedStateDir = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.UserProfile), "Library", "Saved Application State", $"{bundleIdentifier}.savedState");
+ try {
+ if (Directory.Exists (savedStateDir)) {
+ Directory.Delete (savedStateDir, true);
+ Console.WriteLine ($"Deleted saved application state: {savedStateDir}");
+ }
+ } catch (Exception e) {
+ Console.WriteLine ($"Could not delete saved application state '{savedStateDir}': {e.Message}");
+ }
+ }
+
public static StringBuilder AssertExecute (string executable, params string [] arguments)
{
return AssertExecute (executable, arguments, out _);
diff --git a/tests/dotnet/UnitTests/WindowsTest.cs b/tests/dotnet/UnitTests/WindowsTest.cs
index e156da865385..c6e887dfe56b 100644
--- a/tests/dotnet/UnitTests/WindowsTest.cs
+++ b/tests/dotnet/UnitTests/WindowsTest.cs
@@ -261,12 +261,13 @@ public void BundleStructureWithRemoteMac (ApplePlatform platform, string runtime
}
[Category ("RemoteWindows")]
- [TestCase (ApplePlatform.iOS, "ios-arm64")]
- [TestCase (ApplePlatform.iOS, "iossimulator-arm64;iossimulator-x64")]
- public void PluralRuntimeIdentifiersWithRemoteMac (ApplePlatform platform, string runtimeIdentifiers)
+ [TestCase (ApplePlatform.iOS, "ios-arm64", "Debug")]
+ [TestCase (ApplePlatform.iOS, "ios-arm64", "Release")]
+ [TestCase (ApplePlatform.iOS, "iossimulator-arm64;iossimulator-x64", "Debug")]
+ public void PluralRuntimeIdentifiersWithRemoteMac (ApplePlatform platform, string runtimeIdentifiers, string configuration)
{
var properties = AddRemoteProperties ();
- DotNetProjectTest.PluralRuntimeIdentifiersImpl (platform, runtimeIdentifiers, properties);
+ DotNetProjectTest.PluralRuntimeIdentifiersImpl (platform, runtimeIdentifiers, properties, configuration);
}
[Category ("RemoteWindows")]
diff --git a/tools/assembly-preparer/AssemblyPreparer.cs b/tools/assembly-preparer/AssemblyPreparer.cs
index 53caadb1dd1d..d03b0cf31e4c 100644
--- a/tools/assembly-preparer/AssemblyPreparer.cs
+++ b/tools/assembly-preparer/AssemblyPreparer.cs
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using System.Diagnostics;
using System.IO;
using System.Runtime.Serialization;
using Mono.Cecil;
@@ -40,6 +41,12 @@ public string IntermediateOutputPath {
public List Assemblies { get; set; } = new List ();
+ // The list of steps that were executed, along with how long each step took and whether it modified any assemblies.
+ public List StepExecutions { get; } = new List ();
+
+ // Set to true (via the AppBundleRewriter.AssemblySaved callback) whenever the currently executing step modifies an assembly.
+ bool currentStepModifiedAssemblies;
+
public IList<(string Path, AssemblyDefinition Assembly, string? OriginatingAssembly)> AddedAssemblies => configuration.AddedAssemblies;
LinkerConfiguration.Configurator GetConfigurator (string? reproPath = null, Func? assemblyPreparerInfoFactory = null)
@@ -116,8 +123,8 @@ public static AssemblyPreparer LoadFromReproPath (string reproPath)
public bool Prepare (out List exceptions)
{
- var steps = new ConfigurationAwareStep [] {
- // All the same steps as the custom trimmer steps that are run before MarkStep in Xamarin.Shared.Sdk.targets (and in the same order).
+ // All the same steps as the custom trimmer steps that are run before MarkStep in Xamarin.Shared.Sdk.targets (and in the same order).
+ var steps = new List {
// CollectAssembliesStep
new LoadAssembliesStep (),
new ComputeMethodOverridesStep (),
@@ -131,14 +138,23 @@ public bool Prepare (out List exceptions)
new MarkForStaticRegistrarStep (),
new MarkNSObjectsStep (),
new InlineDlfcnMethodsStep (),
- new RegistrarRemovalTrackingStep (),
- // PreMarkDispatcher: I don't think we need this one
- new ManagedRegistrarStep (),
- new TrimmableRegistrarStep (),
- new ManagedRegistrarLookupTablesStep (),
- new InlineClassGetHandleStep (),
- new SaveAssembliesStep (),
};
+
+ // Only add RegistrarRemovalTrackingStep if it's needed:
+ // * If the user explicitly set $(DynamicRegistrationSupported), we don't need to compute the value (it's
+ // passed straight through to the trimmer feature switch).
+ // * If nothing is being trimmed, the dynamic registrar (which lives in the platform assembly, an SDK
+ // assembly that's only trimmed when trimming is enabled) can't be removed, so there's nothing to compute.
+ if (!configuration.DynamicRegistrationSupported.HasValue && configuration.Application.AreAnyAssembliesTrimmed)
+ steps.Add (new RegistrarRemovalTrackingStep ());
+
+ // PreMarkDispatcher: I don't think we need this one
+ steps.Add (new ManagedRegistrarStep ());
+ steps.Add (new TrimmableRegistrarStep ());
+ steps.Add (new ManagedRegistrarLookupTablesStep ());
+ steps.Add (new InlineClassGetHandleStep ());
+ steps.Add (new SaveAssembliesStep ());
+
return RunSteps (steps, out exceptions);
}
@@ -203,10 +219,35 @@ bool RunSteps (IList steps, out List e
var linkContext = configuration.DerivedLinkContext;
- foreach (var step in steps) {
- step.Process (linkContext);
+ // We detect whether a step modified any assemblies by subscribing to the AppBundleRewriter's
+ // AssemblySaved callback, which is called whenever an assembly is modified. All the steps that
+ // modify assemblies go through the AppBundleRewriter to do so.
+ Action? assemblySavedHandler = null;
+ try {
+ foreach (var step in steps) {
+ // Subscribe once the assemblies have been loaded: accessing the AppBundleRewriter before
+ // that point would create it without finding the corlib and platform assemblies.
+ if (assemblySavedHandler is null && configuration.Assemblies.Count > 0) {
+ assemblySavedHandler = (_) => currentStepModifiedAssemblies = true;
+ configuration.AppBundleRewriter.AssemblySaved += assemblySavedHandler;
+ }
+
+ currentStepModifiedAssemblies = false;
+ var watch = Stopwatch.StartNew ();
+ step.Process (linkContext);
+ watch.Stop ();
+ StepExecutions.Add (new StepExecution (step.GetType ().Name, watch.Elapsed, currentStepModifiedAssemblies));
+ }
+ } finally {
+ if (assemblySavedHandler is not null)
+ configuration.AppBundleRewriter.AssemblySaved -= assemblySavedHandler;
}
+ // The post-processing pass flushes its MSBuild output as its last step (DoneStep). The preparation
+ // pass has no DoneStep, so flush here so that its MSBuild output properties are written.
+ if (!configuration.Application.IsPostProcessingAssemblies)
+ configuration.FlushOutputForMSBuild ();
+
return exceptions.Count == 0;
}
@@ -219,6 +260,9 @@ public void Dispose ()
}
}
+// The result of executing a single step: its name, how long it took, and whether it modified any assemblies.
+public record struct StepExecution (string Name, TimeSpan Duration, bool ModifiedAssemblies);
+
public class AssemblyPreparerInfo {
internal AssemblyDefinition? Assembly { get; set; }
internal bool IsCILAssembly { get; set; }
diff --git a/tools/devops/automation/templates/build/build.yml b/tools/devops/automation/templates/build/build.yml
index 1eff352e19b6..234079f64821 100644
--- a/tools/devops/automation/templates/build/build.yml
+++ b/tools/devops/automation/templates/build/build.yml
@@ -238,18 +238,17 @@ steps:
# report the result of the build.
- pwsh: |
+ # Only report failures, not successes.
+ if (("$Env:AGENT_JOBSTATUS" -eq "Succeeded") -or ("$Env:AGENT_JOBSTATUS" -eq "SucceededWithIssues")) {
+ Write-Host "Build passed, skipping comment."
+ return
+ }
Import-Module $Env:SYSTEM_DEFAULTWORKINGDIRECTORY\$Env:BUILD_REPOSITORY_TITLE\tools\devops\automation\scripts\MaciosCI.psd1
$githubComments = New-GitHubCommentsObjectFromUrl -Url "$(Build.Repository.Uri)" -Token $(GitHub.Token) -Hash $Env:COMMENT_HASH
$commentMessage = [System.Text.StringBuilder]::new()
- if (("$Env:AGENT_JOBSTATUS" -eq "Succeeded") -or ("$Env:AGENT_JOBSTATUS" -eq "SucceededWithIssues")) {
- $commentTitle = "Build passed ($(System.JobDisplayName))"
- $commentIcon = ":white_check_mark:"
- $commentMessage.AppendLine("")
- } else {
- $commentTitle = "Build failed ($(System.JobDisplayName))"
- $commentIcon = ":fire:"
- $commentMessage.AppendLine("Build failed for the job '$(System.JobDisplayName)' (with job status '$Env:AGENT_JOBSTATUS')")
- }
+ $commentTitle = "Build failed ($(System.JobDisplayName))"
+ $commentIcon = ":fire:"
+ $commentMessage.AppendLine("Build failed for the job '$(System.JobDisplayName)' (with job status '$Env:AGENT_JOBSTATUS')")
$githubComments.NewCommentFromMessage($commentTitle, $commentIcon, $commentMessage.ToString(), "build $(System.JobDisplayName)")
condition: always()
displayName: 'Report build result'
diff --git a/tools/dotnet-linker/AppBundleRewriter.cs b/tools/dotnet-linker/AppBundleRewriter.cs
index e9257f6727a5..db82fb797034 100644
--- a/tools/dotnet-linker/AppBundleRewriter.cs
+++ b/tools/dotnet-linker/AppBundleRewriter.cs
@@ -29,6 +29,9 @@ class AppBundleRewriter {
AssemblyDefinition? corlib_assembly;
AssemblyDefinition? platform_assembly;
+ // Invoked whenever an assembly is modified (i.e. whenever SaveAssembly is called).
+ public Action? AssemblySaved { get; set; }
+
public AssemblyDefinition CurrentAssembly {
get {
if (current_assembly is null)
@@ -1455,6 +1458,7 @@ void SaveAssembly (AssemblyDefinition assembly)
{
if (assembly != CurrentAssembly && assembly != PlatformAssembly)
throw new InvalidOperationException ($"Can't save assembly {assembly.Name} because it's not the current assembly ({CurrentAssembly.Name}) or the platform assembly ({PlatformAssembly.Name}).");
+ AssemblySaved?.Invoke (assembly);
var annotations = configuration.Context.Annotations;
var action = annotations.GetAction (assembly);
if (action == AssemblyAction.Copy) {
diff --git a/tools/dotnet-linker/LinkerConfiguration.cs b/tools/dotnet-linker/LinkerConfiguration.cs
index a5f94cceb61d..ca103ddf6557 100644
--- a/tools/dotnet-linker/LinkerConfiguration.cs
+++ b/tools/dotnet-linker/LinkerConfiguration.cs
@@ -30,6 +30,9 @@ public class LinkerConfiguration {
public string DedupAssembly = string.Empty;
public string CacheDirectory { get; private set; } = string.Empty;
public Version? DeploymentTarget { get; private set; }
+ // The user-provided value of the $(DynamicRegistrationSupported) MSBuild property (null if not set).
+ // When set, RegistrarRemovalTrackingStep doesn't need to run in the assembly-preparer.
+ public bool? DynamicRegistrationSupported { get; private set; }
public HashSet FrameworkAssemblies { get; private set; } = new HashSet ();
public string IntermediateLinkDir { get; private set; } = string.Empty;
public bool InvariantGlobalization { get; private set; }
@@ -43,6 +46,9 @@ public class LinkerConfiguration {
public HashSet FieldSymbols { get; } = new HashSet ();
public string IntermediateOutputPath { get; private set; } = string.Empty;
public string ItemsDirectory { get; private set; } = string.Empty;
+ // The files the assembly-preparer writes its MSBuild output properties to (one per pass).
+ public string MSBuildOutputFile { get; private set; } = string.Empty;
+ public string MSBuildPostProcessOutputFile { get; private set; } = string.Empty;
public bool IsSimulatorBuild { get; private set; }
public string PartialStaticRegistrarLibrary { get; set; } = string.Empty;
public ApplePlatform Platform { get; private set; }
@@ -100,6 +106,10 @@ public List Exceptions {
Dictionary> msbuild_items = new Dictionary> ();
+ // MSBuild output properties (property name -> value), written (alphabetically sorted) to the
+ // MSBuild output file by FlushOutputForMSBuild.
+ SortedDictionary msbuild_properties = new SortedDictionary (StringComparer.Ordinal);
+
AppBundleRewriter? abr;
internal AppBundleRewriter AppBundleRewriter {
get {
@@ -286,6 +296,24 @@ Configurator GetConfigurator (string linker_file)
}
})
)},
+ { "DynamicRegistrationSupported", (
+ // This is the user-overridable $(DynamicRegistrationSupported) MSBuild property. It maps to
+ // the RemoveDynamicRegistrar optimization (inverted): if dynamic registration is supported,
+ // then we're not removing the dynamic registrar. When set, RegistrarRemovalTrackingStep doesn't
+ // need to run in the assembly-preparer (the value is passed straight through to the trimmer
+ // feature switch), and it won't recompute the value in the real linker either.
+ new LoadValue ((key, value) => {
+ if (string.IsNullOrEmpty (value))
+ return; // Not set: RegistrarRemovalTrackingStep will compute a default value.
+ if (!TryParseOptionalBoolean (value, out var dynamicRegistrationSupported))
+ throw new InvalidOperationException ($"Unable to parse the {key} value: {value} in {linker_file}");
+ if (dynamicRegistrationSupported.HasValue) {
+ DynamicRegistrationSupported = dynamicRegistrationSupported.Value;
+ Application.Optimizations.RemoveDynamicRegistrar = !dynamicRegistrationSupported.Value;
+ }
+ }),
+ new SaveValue ((key, storage) => saveNullableBool (key, DynamicRegistrationSupported, storage))
+ )},
{ "EnableSGenConc", (
new LoadValue ((key, value) => Application.EnableSGenConc = string.Equals ("true", value, StringComparison.OrdinalIgnoreCase)),
new SaveValue ((key, storage) => storage.Add ($"{key}={(Application.EnableSGenConc ? "true" : "false")}"))
@@ -404,6 +432,16 @@ Configurator GetConfigurator (string linker_file)
new LoadValue ((key, value) => Application.MonoLibraries.Add (value)),
new SaveValue ((key, storage) => storage.AddRange (Application.MonoLibraries.OrderBy (v => v).Select (v => $"{key}={v}")))
)},
+ { "MSBuildOutputFile", (
+ // The file the assembly-preparer's preparation pass writes its MSBuild output properties to.
+ new LoadValue ((key, value) => MSBuildOutputFile = value),
+ new SaveValue ((key, storage) => saveNonEmpty (key, MSBuildOutputFile, storage))
+ )},
+ { "MSBuildPostProcessOutputFile", (
+ // The file the assembly-preparer's post-processing pass writes its MSBuild output properties to.
+ new LoadValue ((key, value) => MSBuildPostProcessOutputFile = value),
+ new SaveValue ((key, storage) => saveNonEmpty (key, MSBuildPostProcessOutputFile, storage))
+ )},
{ "MtouchFloat32", (
new LoadValue ((key, value) => loadNullableBool (key, value, out Application.AotFloat32)),
new SaveValue ((key, storage) => saveNullableBool (key, Application.AotFloat32, storage))
@@ -866,25 +904,48 @@ public void WriteOutputForMSBuild (string itemName, List items)
}
}
+ // Register an MSBuild output property. The collected properties are written to the MSBuild
+ // output file (see FlushOutputForMSBuild) so that MSBuild can read them back.
+ public void SetOutputForMSBuild (string propertyName, string value)
+ {
+ msbuild_properties [propertyName] = value;
+ }
+
public void FlushOutputForMSBuild ()
{
- Directory.CreateDirectory (ItemsDirectory);
- foreach (var kvp in msbuild_items) {
- var itemName = kvp.Key;
- var items = kvp.Value;
-
- var xmlNs = XNamespace.Get ("http://schemas.microsoft.com/developer/msbuild/2003");
- var elements = items.Select (item =>
- new XElement (xmlNs + itemName,
- new XAttribute ("Include", item.Include),
- item.Metadata.Select (metadata => new XElement (xmlNs + metadata.Key, metadata.Value))));
-
- var document = new XDocument (
- new XElement (xmlNs + "Project",
- new XElement (xmlNs + "ItemGroup",
- elements)));
-
- document.Save (Path.Combine (ItemsDirectory, itemName + ".items"));
+ // ItemsDirectory isn't set when running in the assembly-preparer, so only write
+ // the item files when we have a directory to write them to.
+ if (!string.IsNullOrEmpty (ItemsDirectory)) {
+ Directory.CreateDirectory (ItemsDirectory);
+ foreach (var kvp in msbuild_items) {
+ var itemName = kvp.Key;
+ var items = kvp.Value;
+
+ var xmlNs = XNamespace.Get ("http://schemas.microsoft.com/developer/msbuild/2003");
+ var elements = items.Select (item =>
+ new XElement (xmlNs + itemName,
+ new XAttribute ("Include", item.Include),
+ item.Metadata.Select (metadata => new XElement (xmlNs + metadata.Key, metadata.Value))));
+
+ var document = new XDocument (
+ new XElement (xmlNs + "Project",
+ new XElement (xmlNs + "ItemGroup",
+ elements)));
+
+ document.Save (Path.Combine (ItemsDirectory, itemName + ".items"));
+ }
+ }
+
+ // Write the collected MSBuild output properties (alphabetically sorted, one 'Name=Value' per line)
+ // to the output file for the current pass, so that MSBuild can read them back. We always write the
+ // file (even when there are no properties), so it's a consistent, persistent artifact of the pass
+ // (it's added to FileWrites by the _PrepareAssemblies/_PostprocessAssemblies targets).
+ var outputFile = Application.IsPostProcessingAssemblies ? MSBuildPostProcessOutputFile : MSBuildOutputFile;
+ if (!string.IsNullOrEmpty (outputFile)) {
+ var directory = Path.GetDirectoryName (outputFile);
+ if (!string.IsNullOrEmpty (directory))
+ Directory.CreateDirectory (directory);
+ File.WriteAllLines (outputFile, msbuild_properties.Select (kvp => $"{kvp.Key}={kvp.Value}"));
}
}
diff --git a/tools/linker/RegistrarRemovalTrackingStep.cs b/tools/linker/RegistrarRemovalTrackingStep.cs
index c295752c86cc..36f9e17306fb 100644
--- a/tools/linker/RegistrarRemovalTrackingStep.cs
+++ b/tools/linker/RegistrarRemovalTrackingStep.cs
@@ -28,7 +28,9 @@ protected override void TryProcessAssembly (AssemblyDefinition assembly)
Process (assembly);
}
+#if !ASSEMBLY_PREPARER
AssemblyDefinition? PlatformAssembly;
+#endif
bool dynamic_registration_support_required;
@@ -49,7 +51,9 @@ bool RequiresDynamicRegistrar (AssemblyDefinition assembly, bool warnIfRequired)
if (Profile.IsProductAssembly (assembly)) {
if (Annotations.GetAction (assembly) != AssemblyAction.Link)
return false;
+#if !ASSEMBLY_PREPARER
PlatformAssembly = assembly;
+#endif
}
// Can't touch the forbidden fruit in the product assembly unless there's a reference to it
@@ -166,20 +170,27 @@ protected override void TryEndProcess ()
App.Log (4, "Optimization dynamic registrar removal: {0}", Optimizations.RemoveDynamicRegistrar.Value ? "enabled" : "disabled");
- if (Optimizations.RemoveDynamicRegistrar.Value) {
+#if ASSEMBLY_PREPARER
+ // In the assembly-preparer we don't rewrite the platform assembly. Instead we surface the computed
+ // value to MSBuild (as the DynamicRegistrationSupported output property), which enables the
+ // 'ObjCRuntime.Runtime.DynamicRegistrationSupported' trimmer feature switch so that ILLink hardcodes
+ // Runtime.DynamicRegistrationSupported. This way the assembly-preparer doesn't have to modify (and
+ // thus re-save) any assembly.
+ Configuration.SetOutputForMSBuild ("DynamicRegistrationSupported", App.DynamicRegistrationSupported ? "true" : "false");
+#else
+ if (Optimizations.RemoveDynamicRegistrar.Value && PlatformAssembly is not null) {
// ILLink will optimize `Runtime.Initialize` based on `DynamicRegistrationSupported` returning a constant (`true`)
// and this will runs before we have the chance to set it to `false` in `CoreOptimizedGeneratedCode` so we instead
// do the change here so the linker can do this without further ado
// note: it does not matter for _legacy_ so we apply the change (to earlier) to minimize the difference between them
- if (PlatformAssembly is not null) {
- var method = PlatformAssembly.MainModule.GetType ("ObjCRuntime.Runtime").Methods.First ((n) => n.Name == "get_DynamicRegistrationSupported");
- // Rewrite to return 'false'
- var instr = method.Body.Instructions;
- instr.Clear ();
- instr.Add (Instruction.Create (OpCodes.Ldc_I4_0));
- instr.Add (Instruction.Create (OpCodes.Ret));
- }
+ var method = PlatformAssembly.MainModule.GetType ("ObjCRuntime.Runtime").Methods.First ((n) => n.Name == "get_DynamicRegistrationSupported");
+ // Rewrite to return 'false'
+ var instr = method.Body.Instructions;
+ instr.Clear ();
+ instr.Add (Instruction.Create (OpCodes.Ldc_I4_0));
+ instr.Add (Instruction.Create (OpCodes.Ret));
}
+#endif
}
}
}