Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7bb213c
[tools/msbuild] Add InlineDlfcnMethodsStep as an opt-in custom linker…
rolfbjarne Mar 4, 2026
2e48a39
Auto-format source code
Mar 12, 2026
583b796
sqinlinedlfcn
rolfbjarne Mar 13, 2026
96e1eb1
Change InlineDlfcnMethods from string to InlineDlfcnMethodsMode enum
rolfbjarne Mar 16, 2026
65a47a5
Merge remote-tracking branch 'origin/main' into dev/rolf/inline-dlfcn
rolfbjarne Mar 16, 2026
59e6156
[main] Update dependencies from dotnet/dotnet (#24881)
dotnet-maestro[bot] Mar 16, 2026
c568db9
[runtime] Make 'is_class_finalization_aware' work before the managed …
rolfbjarne Mar 16, 2026
95470ee
[sharpie] Add --deepsplit option to split bindings into one file per …
dalexsoto Mar 16, 2026
bf6009e
[main] Update dependencies from dotnet/xharness (#24880)
dotnet-maestro[bot] Mar 16, 2026
44c872c
[Foundation] Improve nullability in NSArray.FromArrayNative. (#24884)
rolfbjarne Mar 16, 2026
cd5bbb0
[GitHub Actions] Allow manual triggering of Linux Build Verification …
rolfbjarne Mar 16, 2026
809b19e
[src] Transient strings must always be disposed. (#24894)
rolfbjarne Mar 16, 2026
f60e676
Only compile when connected.
rolfbjarne Mar 17, 2026
0a572b5
Localized file check-in by OneLocBuild Task: Build definition ID 1441…
vs-mobiletools-engineering-service2 Mar 17, 2026
1f573e7
[main] Update dependencies from dotnet/xharness (#24904)
dotnet-maestro[bot] Mar 17, 2026
5390722
[main] Update dependencies from dotnet/dotnet (#24905)
dotnet-maestro[bot] Mar 17, 2026
ce4906b
Update tools/mtouch/Errors.resx
rolfbjarne Mar 17, 2026
4925f35
Update tests/xharness/Jenkins/TestVariationsFactory.cs
rolfbjarne Mar 17, 2026
90a9f11
Update msbuild/Xamarin.MacDev.Tasks/Tasks/CompileNativeCode.cs
rolfbjarne Mar 17, 2026
75de7f3
[Foundation] Improve nullability in NSArray.ArrayFromHandleFunc. (#24…
rolfbjarne Mar 17, 2026
08716c7
[tools] There's no need to set the DOTNET variable before creating ma…
rolfbjarne Mar 17, 2026
45549a4
[build] Fix parallel make hang by disabling dotnet build servers (#24…
dalexsoto Mar 17, 2026
94e4aa7
[tests] Choose the right simulator RID on x64. (#24874)
rolfbjarne Mar 17, 2026
f74b4fd
IOSurface is now working in the simulator.
rolfbjarne Mar 17, 2026
d4762a7
Fix review comments in InlineDlfcnMethodsStep
rolfbjarne Mar 17, 2026
095d77f
[tests] Fix flaky RecordTest.DeskCase_83099_InmutableDictionary on ma…
rolfbjarne Mar 17, 2026
76cf49e
[msbuild] Rename the localization resources, so they don't match loca…
rolfbjarne Mar 17, 2026
6ed4eb3
[tests] Detect when we couldn't create a second background session. (…
rolfbjarne Mar 17, 2026
03b4918
[sharpie] Fix platform type mapping for types inside generic type arg…
rolfbjarne Mar 17, 2026
6b9cfc9
[msbuild] Filter static frameworks from post-processing items. Fixes …
rolfbjarne Mar 17, 2026
d8dcb86
[Foundation] Improve nullability in NSArray.GetItem. (#24907)
rolfbjarne Mar 17, 2026
310712a
LEGO: Pull request from lego/hb_5df43909-4a19-4f55-bc3f-9ea8fccf3c82_…
csigs Mar 18, 2026
b427ce7
[sharpie] Prefer standard protocol interfaces over [Model] classes in…
rolfbjarne Mar 18, 2026
4a99c2e
[main] Update dependencies from dotnet/dotnet (#24917)
dotnet-maestro[bot] Mar 18, 2026
5dc7df4
[dotnet] Make 'dotnet watch' work. (#24922)
rolfbjarne Mar 18, 2026
7a40438
Merge remote-tracking branch 'origin/main' into dev/rolf/inline-dlfcn
rolfbjarne Mar 18, 2026
8bf1c02
Merge remote-tracking branch 'origin/net11.0' into dev/rolf/inline-dl…
rolfbjarne Mar 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ The build system supports multiple platforms simultaneously:

The `configure` script is used to select which platforms to build. This script must be run before `make`.

## MSBuild code

Non-standard patterns in .targets files:

- Always use `$(DeviceSpecificIntermediateOutputPath)` instead of `$(IntermediateOutputPath)`.

## Binding System

### bgen (Binding Generator)
Expand Down Expand Up @@ -331,4 +337,4 @@ try {
} catch (Exception e) {
// Code here
}
```
```
1 change: 1 addition & 0 deletions .github/workflows/linux-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name: Linux Build Verification
on:
pull_request:
workflow_dispatch:

permissions:
contents: read
Expand Down
9 changes: 9 additions & 0 deletions Make.config
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,15 @@ DOTNET_DIR=$(abspath $(TOP)/builds/downloads/$(DOTNET_INSTALL_NAME))
export DOTNET_ROOT=$(DOTNET_DIR)
# dotnet now is being looked up in the PATH
export PATH := $(DOTNET_DIR):$(PATH)

# Disable build servers to prevent parallel make from hanging.
# Build servers (MSBuild server, Roslyn/VBCSCompiler) inherit jobserver file
# descriptors from make, and don't close them when daemonizing. This prevents
# make from detecting that all jobs have finished, causing it to hang
# indefinitely at the end of the build.
export DOTNET_CLI_USE_MSBUILD_SERVER=0
export UseSharedCompilation=false
export MSBUILDDISABLENODEREUSE=1
DOTNET=$(DOTNET_DIR)/dotnet
DOTNET_BCL_DIR:=$(abspath $(TOP)/packages/microsoft.netcore.app.ref/$(DOTNET_BCL_VERSION)/ref/$(DOTNET_TFM))
# when bumping to a new .NET version, there may be a period when some parts of .NET is still on the old .NET version, so handle that here for DOTNET_BCL_DIR
Expand Down
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ world: check-system
@$(MAKE) reset-versions
@$(MAKE) all -j8
@$(MAKE) install -j8
@echo "Build is done, the following workloads were built:"
@$(DOTNET) workload list

.PHONY: check-system
check-system:
Expand Down Expand Up @@ -68,9 +70,6 @@ install-hook::
exit 1; \
fi

all-hook install-hook::
$(Q) $(MAKE) -C dotnet shutdown-build-server

dotnet-install-system:
$(Q) $(MAKE) -C dotnet install-system

Expand Down
22 changes: 22 additions & 0 deletions docs/building-apps/build-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,28 @@ See also:
* The [AlternateAppIcon](build-items.md#alternateappicon) item group.
* The [AppIcon](#appicon) property.

## InlineDlfcnMethods

Controls whether the build system replaces runtime calls to `ObjCRuntime.Dlfcn` methods with direct native symbol lookups at build time, eliminating the overhead of `dlsym` at runtime.

The valid options are:

* `compatibility`: Inlines dlfcn method calls but only creates native references for symbols used in `[Field]` attributes. This is more conservative and avoids link errors for symbols that don't exist at build time.
* `strict`: Inlines dlfcn method calls and creates native references for all symbols. This is more aggressive and may cause link errors if referenced native symbols don't exist.
* (empty): Disables inlining of dlfcn method calls.

Default value:
* .NET 11+: `strict` when using NativeAOT (`PublishAot=true`), `compatibility` otherwise.
* .NET 10 and earlier: not set (disabled).

Example:

```xml
<PropertyGroup>
<InlineDlfcnMethods>compatibility</InlineDlfcnMethods>
</PropertyGroup>
```

## iOSMinimumVersion

Specifies the minimum iOS version the app can run on.
Expand Down
68 changes: 68 additions & 0 deletions docs/code/native-symbols.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Native symbols

Native symbols can be referenced from managed code in several ways:

* P/Invokes (DllImports)
* Calls to `dlsym`, which can happen through:
* The various APIs in `ObjCRuntime.Dlfcn`
* The various APIs in `System.Runtime.InteropServices.NativeLibrary`
* A P/Invoke directly into `dlsym`

It's highly desirable to use a direct native reference to native symbols when building a mobile app, for a few reasons:

* It's faster at runtime, and the app is smaller.
* If the referenced native symbol comes from a third-party static library, the
native linker can remove it if it's configured to remove unused code
(because the native linker can't see that the native symbol is in fact used
at runtime) unless there's a direct native reference to the symbol.

On the other hand there's one scenario when a direct native reference is not desirable: when the native symbol does not exist.

In order to create a direct native reference to native symbols, we need to know the names of those native symbols.

## The `InlineDlfcnMethods` property

This behavior is controlled by the `InlineDlfcnMethods` MSBuild property, which
has two modes:

* `strict`: all calls to `ObjCRuntime.Dlfcn` APIs are inlined.
* `compatibility`: only calls that reference symbols from `[Field]` attributes are inlined.

See the [build properties documentation](../building-apps/build-properties.md) for default values.

## How it works

During the build we try to collect the following:

* Any property or field with the `[Foundation.Field]` attribute: we collect the symbol name.
* Any calls to the `ObjCRuntime.Dlfcn` APIs: we try to collect the symbol name (this might not always succeed, if the symbol name is not a constant).
* We don't process calls to `System.Runtime.InteropServices.NativeLibrary` at the moment (this may change in the future, if there's need).

This is further complicated by the fact that we only want to create native
references for symbols that survive trimming.

So we do the following:

1. During trimming, two custom linker steps execute:

* `InlineDlfcnMethodsStep`: for every symbol we've collected, this step
creates a P/Invoke to a native method that will return the address for
that symbol (using a direct native reference), and modifies the code
that fetches that symbol to call said P/Invoke.
* `GenerateInlinedDlfcnNativeCodeStep`: writes the complete list of
inlined symbols to a file (`inlined-dlfcn-symbols.txt`) for later
MSBuild targets to consume.

2. After trimming, we figure out which of those symbols survived:

* For ILTrim: the `_CollectPostILTrimInformation` MSBuild target inspects
the trimmed assemblies and collects all the inlined dlfcn P/Invokes that
survived. Per-assembly results are cached to speed up incremental builds.
* For NativeAOT: the `_CollectPostNativeAOTTrimInformation` MSBuild target
inspects the native object file (or static library) produced by NativeAOT,
collects all unresolved native references, and filters them against the
inlined dlfcn symbols to determine which survived.

3. The `_PostTrimmingProcessing` MSBuild target takes the surviving symbols
from either path, generates the corresponding native C code, and adds it to
the list of files to compile and link into the final executable.
20 changes: 0 additions & 20 deletions dotnet/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -537,23 +537,3 @@ clean-local::
$(Q) $(DOTNET) restore package/workaround-for-maccore-issue-2427/restore.csproj /bl:package/workaround-for-maccore-issue-2427/restore.binlog $(MSBUILD_VERBOSITY)
$(Q) touch $@

# We need to shut down the builder server, because:
# We're using parallel make, and parallel make will start a jobserver, managed by file descriptors, where these file descriptors must be closed in all subprocesses for make to realize it's done.
# 'dotnet pack' might have started a build server
# The build server does not close any file descriptors it may have inherited when daemonizing itself.
# Thus the build server (which will still be alive after we're done building here) might have a file descriptor open which make is waiting for.
# The proper fix is to fix the build server to close its file descriptors.
# The intermediate working is to shut down the build server instead. An alternative solution would be to pass /p:UseSharedCompilation=false to 'dotnet pack' to disable the usage of the build server.
#
# The 'shutdown-build-server' is executed in a sub-make (and not as a dependency to the all-hook target),
# to make sure it's executed after everything else is built in this file.
all-hook::
$(Q) $(MAKE) shutdown-build-server

shutdown-build-server:
$(Q) echo "Shutting down build servers:"
$(Q) $(DOTNET) build-server shutdown | sed 's/^/ /' || true
$(Q) echo "Listing .NET processes still alive:"
$(Q) pgrep -lf "^$(DOTNET)" | sed 's/^/ /' || true
$(Q) echo "Killing the above mentioned processes."
$(Q) pkill -9 -f "^$(DOTNET)" | sed 's/^/ /' || true
30 changes: 30 additions & 0 deletions dotnet/targets/Microsoft.Sdk.Desktop.targets
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,38 @@
<Target
Name="_PrepareRunDesktop"
BeforeTargets="ComputeRunArguments"
DependsOnTargets="_ValidateHotReloadConfiguration"
Condition="'$(_PlatformName)' == 'macOS' Or '$(_PlatformName)' == 'MacCatalyst'">

<!--
If we're running under 'dotnet watch', change a few defaults to:
* Open in a new instance: enabled
* Wait for exit: enabled
* stdout/stderr: send to the current terminal
-->
<ItemGroup>
<_DotNetWatchVariable Include="@(RuntimeEnvironmentVariable)" Condition="'%(Identity)' == 'DOTNET_WATCH' And '%(Value)' == '1'" />
</ItemGroup>
<PropertyGroup Condition="'$(RunWithOpen)' != 'false' And '@(_DotNetWatchVariable->Count())' == '1'">
<_IsDotNetWatch>true</_IsDotNetWatch>
<OpenNewInstance Condition="'$(OpenNewInstance)' == ''">true</OpenNewInstance>
<OpenWaitForExit Condition="'$(OpenWaitForExit)' == ''">true</OpenWaitForExit>
</PropertyGroup>
<Exec
Command="tty"
Condition="'$(StandardOutputPath)' == '' Or '$(StandardErrorPath)' == ''"
ConsoleToMSBuild="true"
IgnoreStandardErrorWarningFormat="true"
IgnoreExitCode="true"
>
<Output TaskParameter="ConsoleOutput" ItemName="_TtyOutput" />
</Exec>
<PropertyGroup Condition="'$(_IsDotNetWatch)' == 'true'">
<_TtyPath>@(_TtyOutput)</_TtyPath>
<StandardOutputPath Condition="'$(StandardOutputPath)' == '' And Exists('$(_TtyPath)')">$(_TtyPath)</StandardOutputPath>
<StandardErrorPath Condition="'$(StandardErrorPath)' == '' And Exists('$(_TtyPath)')">$(_TtyPath)</StandardErrorPath>
</PropertyGroup>

<PropertyGroup Condition="'$(RunWithOpen)' != 'false'">
<_OpenArguments Condition="'$(XamarinDebugMode)' != ''">$(_OpenArguments) --env __XAMARIN_DEBUG_MODE__=$(XamarinDebugMode)</_OpenArguments>
<_OpenArguments Condition="'$(XamarinDebugPort)' != ''">$(_OpenArguments) --env __XAMARIN_DEBUG_PORT__=$(XamarinDebugPort)</_OpenArguments>
Expand Down
2 changes: 1 addition & 1 deletion dotnet/targets/Microsoft.Sdk.Mobile.targets
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
<Target
Name="_PrepareRunMobile"
BeforeTargets="ComputeRunArguments"
DependsOnTargets="_InstallMobile;ComputeMlaunchRunArguments"
DependsOnTargets="_ValidateHotReloadConfiguration;_InstallMobile;ComputeMlaunchRunArguments"
Condition="'$(_PlatformName)' != 'macOS' And '$(_PlatformName)' != 'MacCatalyst'">
</Target>

Expand Down
Loading
Loading