Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions INDEX.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ Use update-index to regenerate it:
| | [Readonly references in C# and IL verification.](proposed/verifiable-ref-readonly.md) | |
| | [Ref returns in C# and IL verification.](proposed/verifiable-ref-returns.md) | |
| | [SDK Analysis Level Property and Usage](proposed/sdk-analysis-level.md) | (PM) [Chet Husk](https://github.com/baronfel), (Engineering) [Daniel Plaisted](https://github.com/dsplaisted) |
| | [Shared dependencies across applications](proposed/shared-dependencies-across-applications.md) | |
| | [Swift Interop](proposed/swift-interop.md) | [Andy Gocke](https://github.com/agocke), [Jeremy Koritzinsky](https://github.com/jkoritzinsky) |
| | [Target AVX2 in R2R images](proposed/vector-instruction-set-default.md) | [Richard Lander](https://github.com/richlander) |

95 changes: 95 additions & 0 deletions proposed/shared-dependencies-across-applications.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Shared dependencies across applications

The .NET deployment model optionally shares the runtime across apps, but offers no sharing model for other libraries. When multiple applications share many of the same dependencies, each carries its own copies, inflating the overall deployment and requiring every app to be updated to pick up a dependency change. If that common set of libraries is owned and versioned together, it is a natural set to deploy once and share.

**Goals**
- Apps can reference the shared set at build time without deploying it into each app's output
- The shared set lives in a shared location on disk and is resolved at run time by apps that referenced it at build time
- A single deployment of the shared set is used by all the applications
- The shared set can be updated independently of the apps

**Non-goals**
- Extension of the shared framework concept (global install, name-based framework references, framework versioning) to third parties
- Machine-wide assembly store cache
- Deployment of the applications and their shared set(s)

## Proposed approach

### Runtime resolution

The host reads a `sharedDependencies` array in `runtimeconfig.json`. Each entry points at a shared set's `.deps.json`.

`App.runtimeconfig.json`:

```json
{
"runtimeOptions": {
"tfm": "net11.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "11.0.0"
},
"sharedDependencies": [
"/opt/AppSuite.Shared/AppSuite.Shared.deps.json"
]
}
}
```

- Each `sharedDependencies` entry is either an absolute path or relative path (based on the app's directory) to the shared set's `.deps.json`. Environment variables are not supported.
- The host merges each shared set's `deps.json` into the app's dependencies at startup. The shared set is treated as part of the app, so it resolves with the same priority as assets directly in the app's `deps.json` (that is, before frameworks). This is similar to an [additional `deps.json` via `--additional-deps`/`DOTNET_ADDITIONAL_DEPS`](https://github.com/dotnet/runtime/blob/main/docs/design/features/additional-deps.md).
- The assets in a shared set's `deps.json` are resolved relative to that `deps.json`'s folder (`/opt/AppSuite.Shared/` in the above example). The presence of `sharedDependencies` does not trigger file existence checks at startup. This differs from `--additional-deps`/`DOTNET_ADDITIONAL_DEPS`, which resolves an additional deps file's assets relative to the app directory and enables those file existence checks.

### Developer experience

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just kind of put something that somewhat made sense to me here. Happy to pull in whatever experience others folks think makes more sense.


An app can reference a shared set so it compiles against the libraries without copying them into its output, and the build records the deployed `.deps.json` in the app's `sharedDependencies`. The shared set can be a project that aggregates the libraries to be shared.

`App.csproj` (app):

```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net11.0</TargetFramework>
</PropertyGroup>

<!-- SharedDependencyLocation metadata (no copy-local + emits the entry) -->
<ItemGroup>
<ProjectReference Include="../AppSuite.Shared/AppSuite.Shared.csproj"
SharedDependencyLocation="/opt/AppSuite.Shared/" />

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we had External=True from nuget, would I assume you wouldn't use SharedDependencyLocation on any packagereference?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That seems like it would fit. What is the mechanism for run time resolution for External=True? Is there a doc around that feature we can link to?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I asked @nkolev92 offline if he could add more details and share any specs they might have with you. We should update this design to include an example using that new feature once we have the details.

</ItemGroup>

<!-- SharedDependency item (emits the entry) + ProjectReference with Private=false (no copy-local) -->
<ItemGroup>
<ProjectReference Include="../AppSuite.Utilities/AppSuite.Utilities.csproj" Private="false" />
<SharedDependency Include="/opt/AppSuite.Utilities/AppSuite.Utilities.deps.json" />
</ItemGroup>
</Project>
```

`AppSuite.Shared.csproj` (shared set):

```xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net11.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Serilog" Version="4.0.0" />
<ProjectReference Include="../AppSuite.Core/AppSuite.Core.csproj" />
<!-- Other libraries to be shared -->
</ItemGroup>
</Project>
```

- `SharedDependencyLocation` metadata on the reference turns copy-local off and emits the `sharedDependencies` entry (`<SharedDependencyLocation>/<depsFileName>`).
- `SharedDependency` item points at a deployed `.deps.json` and emits the entry.
- Publishing the shared set produces a layout with the dependency assemblies and a `deps.json`. That layout is deployed to the shared location and the `deps.json` is pointed at by `sharedDependencies`.

## Related

- [dotnet/runtime#53834](https://github.com/dotnet/runtime/issues/53834) — Support deploying multiple exes as a single self-contained set
- [`--additional-deps`/`DOTNET_ADDITIONAL_DEPS`](https://github.com/dotnet/runtime/blob/main/docs/design/features/additional-deps.md) — existing support for additional deps files via command line or environment variable
- Not directly related, but in the space of configuring how dependencies can be laid out and found
- [Configure .NET install search behavior](https://learn.microsoft.com/dotnet/core/deploying#configure-net-install-search-behavior) — existing configuration for how the apphost finds the .NET install
- [dotnet/sdk#48406](https://github.com/dotnet/sdk/issues/48406) — `localPath` in `deps.json` is used for asset resolution and can be customized via `DestinationSubDirectory`: [dotnet/runtime#117682](https://github.com/dotnet/runtime/issues/117682), [dotnet/runtime#118297](https://github.com/dotnet/runtime/pull/118297), [dotnet/sdk#50120](https://github.com/dotnet/sdk/pull/50120)
Loading