dotnet / dotnet/android

`DOTNET_STARTUP_HOOKS` from `@(AndroidEnvironment)` files is silently overwritten

Open
#11,186 0 comments 1 reaction 2 assignees Claimed by @simonrozsival View on GitHub
copilot
Dominant language
C#
Stars
2.1k
Forks
579
Avg merge
1d 20h
Merged PRs (30d)
257

Description

### Description

Both the HotReload targets (`Microsoft.Android.Sdk.HotReload.targets`) and the trimmable typemap targets (`Microsoft.Android.Sdk.TypeMap.Trimmable.targets`) compose `DOTNET_STARTUP_HOOKS` only from `@(RuntimeEnvironmentVariable)` items. However, users can also declare environment variables via `@(AndroidEnvironment)` text files:

```
DOTNET_STARTUP_HOOKS=MyStartupHook.dll
```

When the build targets run, they do:
```xml

```

This replaces the `@(RuntimeEnvironmentVariable)` value but never reads or merges values from `@(AndroidEnvironment)` files. The user's startup hook is silently dropped.

### Affected targets

1. **`_AndroidConfigureHotReloadEnvironment`** in `Microsoft.Android.Sdk.HotReload.targets` ([L48-49](https://github.com/dotnet/android/blob/610601670e841a466cc22c17b344c328aa2d4163/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.HotReload.targets#L48-L49)) — replaces `DOTNET_STARTUP_HOOKS` without composing.
2. **`_ConfigureTrimmableTypeMapStartupHook`** in `Microsoft.Android.Sdk.TypeMap.Trimmable.targets` (PR #11181) — composes `@(RuntimeEnvironmentVariable)` entries but ignores `@(AndroidEnvironment)` files.

### Expected behavior

All `DOTNET_STARTUP_HOOKS` values — regardless of source — should be merged (colon-separated) into the final environment written to the app.

### Suggested approach

Introduce a new `@(_AndroidDotnetStartupHooks)` item group as the single collection point. A target running before `_GenerateEnvironmentFiles` merges **all three sources**:

1. **`@(_AndroidDotnetStartupHooks)`** — internal contributors (HotReload, trimmable typemap) append their assembly names here instead of directly manipulating `@(RuntimeEnvironmentVariable)`
2. **`@(RuntimeEnvironmentVariable)`** (filtered for `DOTNET_STARTUP_HOOKS`) — values passed in externally, e.g. by `dotnet watch`
3. **`@(AndroidEnvironment)`** files — parsed for `DOTNET_STARTUP_HOOKS=...` lines

The target then:
- Removes any `DOTNET_STARTUP_HOOKS` from `@(RuntimeEnvironmentVariable)`
- Strips `DOTNET_STARTUP_HOOKS=...` lines from `@(AndroidEnvironment)` file content (or leaves them and deduplicates — TBD)
- Joins all collected values with `:` into a single `@(RuntimeEnvironmentVariable)` entry

This approach:
- Eliminates the fragile remove-then-add pattern each contributor currently does
- Makes composition order-independent — no target needs to know about other contributors
- Avoids duplicates naturally (items are distinct assembly names)
- Starts as private (`_` prefix) with the option to make it public and documented later

### Context

Discussed in https://github.com/dotnet/android/pull/11181#discussion_r3126593242

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.