From aa7b9c88712a767660e9d17a122623af0b330d36 Mon Sep 17 00:00:00 2001 From: Jan Krivanek Date: Thu, 23 Feb 2023 18:10:14 +0100 Subject: [PATCH 1/7] Document references peculiarities --- documentation/README.md | 5 +- .../wiki/Controlling-Dependencies-Behavior.md | 239 ++++++++++++++++++ 2 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 documentation/wiki/Controlling-Dependencies-Behavior.md diff --git a/documentation/README.md b/documentation/README.md index 082fab41bb3..e4effe6ca4e 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -39,6 +39,7 @@ The folder contains collection of docs and references for MSBuild, detailed info ### Problems? * [Rebuilding when nothing changed](wiki/Rebuilding-when-nothing-changed.md) +* [Controling Dependencies Behavior](wiki/Controlling-Dependencies-Behavior.md) * [Something's wrong in my build](wiki/Something's-wrong-in-my-build.md) * [Some gotchas around the Microsoft.Build.Framework project/assembly](wiki/Microsoft.Build.Framework.md) * [GAC and MSBuild](wiki/UnGAC.md) @@ -50,16 +51,16 @@ The folder contains collection of docs and references for MSBuild, detailed info * [`ProjectReference`](ProjectReference-Protocol.md) * [MSBuild Server](MSBuild-Server.md) * [Low priority nodes](specs/low-priority-switch.md) +* [Threading in MSBuild worker nodes](specs/threading.md) +* [Nodes orchestration](wiki/Nodes-Orchestration.md) * [Project cache plugin](specs/project-cache.md) * [Support for remote host objects](specs/remote-host-object.md) * [Static graph](specs/static-graph.md) * [Single project isolated builds: implementation details](specs/single-project-isolated-builds.md) * [Task isolation](specs/task-isolation-and-dependencies.md) -* [Threading in MSBuild worker nodes](specs/threading.md) * [Target maps](wiki/Target-Maps.md) * [Managing parallelism in MSBuild](specs/resource-management.md) * [SDK resolution](specs/sdk-resolvers-algorithm.md) -* [Nodes orchestration](wiki/Nodes-Orchestration.md) ### Tasks diff --git a/documentation/wiki/Controlling-Dependencies-Behavior.md b/documentation/wiki/Controlling-Dependencies-Behavior.md new file mode 100644 index 00000000000..a1afd679279 --- /dev/null +++ b/documentation/wiki/Controlling-Dependencies-Behavior.md @@ -0,0 +1,239 @@ +# Controlling dependencies behavior + +MSBuild recognizes [few types of dependencies](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/msbuild/common-msbuild-project-items) (here we are mainly interested in `ProjectReference`, `PackageReference`, `Reference` aka assembly reference) and offers optional mechanisms to tailor some aspects of the dependencies workings - transitive dependencies resolution, multitargeted references resolution, copying dependencies to output directory. + +## Access to transitive project references + +In [SDK-style projects](https://learn.microsoft.com/en-us/dotnet/core/project-sdk/overview) MSBuild by default makes all transitive `ProjectReference`s accessible as if they were direct dependencies. + +This can lead to easy unintentional breaking out of layering architecture separation. + +This behavior can be opted-out via `DisableTransitiveProjectReferences` property on the referencing project. + +*Example*: + +Let's imagine an `Onion Architecture` design: + +``` + --------------- ------------------ -------------- +| Service Layer | --> | Repository Layer | --> | Domain Model | + --------------- ------------------ -------------- +``` + +Service Layer definition: + +```xml + + + + + + + net48 + 10 + + true + + +``` + +```csharp +namespace Service; + +public class PersonsAccessor +{ + private Repository.Persona _persona; + // This is allowed unless DisableTransitiveProjectReferences=true is passed into build. + // private Domain.PersonTable _tbl; +} +``` + +## Access to transitive package references + +The transitive access to dependencies works by default for package dependencies as well. This can be opted out via `PrivateAssets=compile` on the `PackageReference` of the concern. (More details on [Controlling package dependency assets](https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets)) + +*Example*: + +In our previous example let's have `Repository Layer` reference `newtonsoft.json`: + +```xml + + + + compile + + +``` + +Then our `Service Layer` would have access to `newtonsoft.json` (unless opted out via `PrivateAssets=compile`): + +```csharp +namespace Service; +//This is allowed unless PrivateAssets=compile is set on the PackageDependency in Repository. +//using Newtonsoft.Json; + +public class PersonsAccessor +{ + private Repository.Persona _persona; +} +``` + +## Not copying dependencies to output + +By default the above mentioned dependency types are being copied to build output directory during the build (provided the target failed [up-to-date check](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/msbuild/incremental-builds?view=vs-2015&redirectedfrom=MSDN#output-inference) and run). There can be various scenarios where this behavior is not desired (examples: dependency is compile time only or contains a logic for build; component is plugin to a main app and there is a desire not to duplicate common dependencies in output). + +Overriding this logic depends on a type of dependency. + +### Not copying Assembly Reference + +Copying can be opted out via [Private metadata on the Reference item](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/msbuild/common-msbuild-project-items?view=vs-2015#reference) (which corresponds to the `Copy Local` property of the reference in the Visual Studio properties dialog for the reference): + +```xml + + + ..\somepath\mydll.dll + + false + + +``` + +### Not copying PackageReference + +Detailed options description can be found in [Controlling package dependency assets](https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets). Here we'll offer three artifical examples: + +**Not copying package dependency to the immediate output folder:** + +```xml + + + + compile + + +``` + +**Not copying package dependency to the downstream dependants output folder:** + +```xml + + + + all + + +``` + +**Not copying package dependency from the upstream dependencies:** + +```xml + + + + all + + +``` + +### Not copying ProjectReference + +The opt-out mechanism is analogous to [Assembly Reference copy opt-out](#not-copying-assembly-reference): + +```xml + + + + false + + +``` + +**Note:** There is possible need to explicitly specify `_GetChildProjectCopyToPublishDirectoryItems=false` to opt-out copying of project dependencies when builiding through [`MSBuilt` task](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-task) ([source](https://github.com/dotnet/msbuild/issues/4795#issuecomment-669885298)) + +## ProjectReference without accessibility and copying to output + +In a specific scenarios we might want to indicate that specific project should be built prior our project but said project should not be reference accessible nor its output copied to current project output. This can be helpful for build time only dependencies - projects defining behavior that is going to be used as build step of a current project. + +Such a behavior can be achived with [`ReferenceOutputAssembly` metadata](https://learn.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-items?view=vs-2022#projectreference): + +```xml + + + + false + + +``` + +**Note:** This technique doesn't fully work when referencing project with executable output type (`Exe`) - in such case the types defined within the project still cannot be referenced, however output is copied to the current project output folder. In that case we need to combine (or replace) the `ReferenceOutputAssembly` metadata with `Private` metadata - [as described above](#not-copying-projectreference). + +## Forcing TargetFramework of a referenced multitargeted project + +Consider agaoin our previous [Onion architecture example](#OnionArchSample), but now the individual projects will be [multitargeted](https://learn.microsoft.com/en-us/nuget/create-packages/multiple-target-frameworks-project-file). + +Repository Layer: + +```xml + + + netstandard2.0;net48 + + + + + + + + + + + + +``` + +And it's going to be referenced by Service Layer: + + +```xml + + + Exe + net48;netstandard2.0 + + + + + + +``` + +Building the Service Layer will create output folders for `net7` and `net48`: + +``` +net48 + |---- Repository.dll (targeted for net48) + |---- Domain-net48.dll + |---- System.Text.Json.dll + +net7 + |---- Repository.dll (targeted for netstandard2.0) + |---- Domain-netstd20.dll + |---- Newtonsoft.Json.dll +``` + +Would we want to reference the netstandard version of the Repository Layer in our Service Layer - we can force the reference chain via `SetTargetFramework` metadata on `ProjectReference` item: + +```xml + + + +``` + +**Notes:** + +This will properly enforce the framework for the dependency chain. The output folder will contain proper version of the direct dependency - Repository Layer. The transitive dependencies might overbuild, and output folder of current project (Service Layer) might contain both versions of the transitive project dependency (Domain-net48.dll and Domain-netstd20.dll). This limitation can be workarounded by switching of the transitive project dependencies via `DisableTransitiveProjectReferences` (same as shown in [Access to transitive project references](#access-to-transitive-project-references)) + +`SetTargetFramework` is currently not honored by the nuget client([nuget issue #12436](https://github.com/NuGet/Home/issues/12436)), so the output folder will contain binaries from nuget packages as if this metadata was not used. To workaround this the apropriate nuget needs to be directly referenced from the project enforcing reference framework via `SetTargetFramework`, or copied to output/publish folder via different means. \ No newline at end of file From 84e0f6ac567ad88cb58d0fdd0a1f68c1e1dc6772 Mon Sep 17 00:00:00 2001 From: Jan Krivanek Date: Thu, 2 Mar 2023 15:07:48 +0100 Subject: [PATCH 2/7] Update documentation/wiki/Controlling-Dependencies-Behavior.md Co-authored-by: Rainer Sigwald --- documentation/wiki/Controlling-Dependencies-Behavior.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/documentation/wiki/Controlling-Dependencies-Behavior.md b/documentation/wiki/Controlling-Dependencies-Behavior.md index a1afd679279..879bac7668c 100644 --- a/documentation/wiki/Controlling-Dependencies-Behavior.md +++ b/documentation/wiki/Controlling-Dependencies-Behavior.md @@ -14,10 +14,10 @@ This behavior can be opted-out via `DisableTransitiveProjectReferences` property Let's imagine an `Onion Architecture` design: -``` - --------------- ------------------ -------------- -| Service Layer | --> | Repository Layer | --> | Domain Model | - --------------- ------------------ -------------- +```mermaid +flowchart LR + Service[Service Layer] --> Repository + Repository[Repository Layer] --> Domain[Domain Layer] ``` Service Layer definition: From 4edf77f777dda952740c02a556275762c9c881b8 Mon Sep 17 00:00:00 2001 From: Jan Krivanek Date: Thu, 2 Mar 2023 16:09:35 +0100 Subject: [PATCH 3/7] Incorporated PR review feedback --- documentation/README.md | 2 +- .../wiki/Controlling-Dependencies-Behavior.md | 44 +++++++++++-------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/documentation/README.md b/documentation/README.md index e4effe6ca4e..2de997ba44e 100644 --- a/documentation/README.md +++ b/documentation/README.md @@ -39,7 +39,7 @@ The folder contains collection of docs and references for MSBuild, detailed info ### Problems? * [Rebuilding when nothing changed](wiki/Rebuilding-when-nothing-changed.md) -* [Controling Dependencies Behavior](wiki/Controlling-Dependencies-Behavior.md) +* [Controling References Behavior](wiki/Controlling-Dependencies-Behavior.md) * [Something's wrong in my build](wiki/Something's-wrong-in-my-build.md) * [Some gotchas around the Microsoft.Build.Framework project/assembly](wiki/Microsoft.Build.Framework.md) * [GAC and MSBuild](wiki/UnGAC.md) diff --git a/documentation/wiki/Controlling-Dependencies-Behavior.md b/documentation/wiki/Controlling-Dependencies-Behavior.md index 879bac7668c..f5d25bce3ce 100644 --- a/documentation/wiki/Controlling-Dependencies-Behavior.md +++ b/documentation/wiki/Controlling-Dependencies-Behavior.md @@ -1,14 +1,22 @@ -# Controlling dependencies behavior +# Controlling references behavior -MSBuild recognizes [few types of dependencies](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/msbuild/common-msbuild-project-items) (here we are mainly interested in `ProjectReference`, `PackageReference`, `Reference` aka assembly reference) and offers optional mechanisms to tailor some aspects of the dependencies workings - transitive dependencies resolution, multitargeted references resolution, copying dependencies to output directory. +MSBuild recognizes a [few types of references](https://learn.microsoft.com/previous-versions/visualstudio/visual-studio-2015/msbuild/common-msbuild-project-items) (here we are mainly interested in `ProjectReference`, `PackageReference`, `Reference` aka assembly reference) and offers optional mechanisms to tailor some aspects of the references workings - transitive references resolution, multitargeted references resolution, copying references to output directory. -## Access to transitive project references +## .NET SDK projects and access to transitive references + +For [.NET SDK projects](https://learn.microsoft.com/dotnet/core/project-sdk/overview) restore operation by default makes all transitive references accessible as if they were direct references. + +This is required by the compiler and analyzers to be able to properly inspect the whole dependency or/and inheritance chain of types when deciding about particular checks. -In [SDK-style projects](https://learn.microsoft.com/en-us/dotnet/core/project-sdk/overview) MSBuild by default makes all transitive `ProjectReference`s accessible as if they were direct dependencies. +It is facilitated via `project.assets.json` file created by NuGet client during the restore operation. This file captures the whole transitive closure of the project dependency tree. -This can lead to easy unintentional breaking out of layering architecture separation. +SDK build tasks require existence of this file (hence the infamous `Assets file \project.assets.json not found` if the MSBuild.exe is run without prior restore operation). It is used to reconstruct the `ProjectReference`s and `PackageReference`s for the project and pass them to MSBuild. For this reason MSBuild and compiler by default sees those transitive references as if they were direct references. + +## Access to transitive project references -This behavior can be opted-out via `DisableTransitiveProjectReferences` property on the referencing project. +Above described behavior can lead to easy unintentional breaking out of layering architecture separation. + +This behavior can be opted-out for `ProjectReference`s via `DisableTransitiveProjectReferences` property on the referencing project. *Example*: @@ -50,7 +58,7 @@ public class PersonsAccessor ## Access to transitive package references -The transitive access to dependencies works by default for package dependencies as well. This can be opted out via `PrivateAssets=compile` on the `PackageReference` of the concern. (More details on [Controlling package dependency assets](https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets)) +The transitive access to references works by default for package references as well. This can be opted out via `PrivateAssets=compile` on the `PackageReference` of the concern. (More details on [Controlling package dependency assets](https://learn.microsoft.com/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets)) *Example*: @@ -80,13 +88,13 @@ public class PersonsAccessor ## Not copying dependencies to output -By default the above mentioned dependency types are being copied to build output directory during the build (provided the target failed [up-to-date check](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/msbuild/incremental-builds?view=vs-2015&redirectedfrom=MSDN#output-inference) and run). There can be various scenarios where this behavior is not desired (examples: dependency is compile time only or contains a logic for build; component is plugin to a main app and there is a desire not to duplicate common dependencies in output). +By default the above mentioned dependency types are copied to the build output directory during the build. There can be various scenarios where this behavior is not desired (examples: dependency is compile time only or contains a logic for build; component is plugin to a main app and there is a desire not to duplicate common dependencies in output). -Overriding this logic depends on a type of dependency. +Overriding this logic depends on the type of the dependency. ### Not copying Assembly Reference -Copying can be opted out via [Private metadata on the Reference item](https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2015/msbuild/common-msbuild-project-items?view=vs-2015#reference) (which corresponds to the `Copy Local` property of the reference in the Visual Studio properties dialog for the reference): +Copying can be opted out via [Private metadata on the Reference item](https://learn.microsoft.com/previous-versions/visualstudio/visual-studio-2015/msbuild/common-msbuild-project-items?view=vs-2015#reference) (which corresponds to the `Copy Local` property of the reference in the Visual Studio properties dialog for the reference): ```xml @@ -100,7 +108,7 @@ Copying can be opted out via [Private metadata on the Reference item](https://le ### Not copying PackageReference -Detailed options description can be found in [Controlling package dependency assets](https://learn.microsoft.com/en-us/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets). Here we'll offer three artifical examples: +Detailed options description can be found in [Controlling package dependency assets](https://learn.microsoft.com/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets). Here we'll offer three artifical examples: **Not copying package dependency to the immediate output folder:** @@ -149,13 +157,11 @@ The opt-out mechanism is analogous to [Assembly Reference copy opt-out](#not-cop ``` -**Note:** There is possible need to explicitly specify `_GetChildProjectCopyToPublishDirectoryItems=false` to opt-out copying of project dependencies when builiding through [`MSBuilt` task](https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild-task) ([source](https://github.com/dotnet/msbuild/issues/4795#issuecomment-669885298)) - ## ProjectReference without accessibility and copying to output In a specific scenarios we might want to indicate that specific project should be built prior our project but said project should not be reference accessible nor its output copied to current project output. This can be helpful for build time only dependencies - projects defining behavior that is going to be used as build step of a current project. -Such a behavior can be achived with [`ReferenceOutputAssembly` metadata](https://learn.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-items?view=vs-2022#projectreference): +Such a behavior can be achived with [`ReferenceOutputAssembly` metadata](https://learn.microsoft.com/visualstudio/msbuild/common-msbuild-project-items?view=vs-2022#projectreference): ```xml @@ -168,11 +174,11 @@ Such a behavior can be achived with [`ReferenceOutputAssembly` metadata](https:/ ``` -**Note:** This technique doesn't fully work when referencing project with executable output type (`Exe`) - in such case the types defined within the project still cannot be referenced, however output is copied to the current project output folder. In that case we need to combine (or replace) the `ReferenceOutputAssembly` metadata with `Private` metadata - [as described above](#not-copying-projectreference). +**Note:** This technique has possibly unexpected behavior when referencing project with executable output type (`Exe`) - in such case the output assembly (`.dll`) is still not copied and referenced (as the metadatum name implies) and hence the types defined within the project cannot be referenced, however other supplementary output (added as `content` or `none`) is copied to the current project output folder (for .NET Core this includes `deps.json`, `runtimeconfig.json` and mainly `.exe`). In that case we can combine (or replace) the `ReferenceOutputAssembly` metadata with `Private` metadata - [as described above](#not-copying-projectreference). More details on this case [here](https://github.com/dotnet/msbuild/issues/4795#issuecomment-1442390297) ## Forcing TargetFramework of a referenced multitargeted project -Consider agaoin our previous [Onion architecture example](#OnionArchSample), but now the individual projects will be [multitargeted](https://learn.microsoft.com/en-us/nuget/create-packages/multiple-target-frameworks-project-file). +Consider agaoin our previous [Onion architecture example](#OnionArchSample), but now the individual projects will be [multitargeted](https://learn.microsoft.com/nuget/create-packages/multiple-target-frameworks-project-file). Repository Layer: @@ -234,6 +240,8 @@ Would we want to reference the netstandard version of the Repository Layer in ou **Notes:** -This will properly enforce the framework for the dependency chain. The output folder will contain proper version of the direct dependency - Repository Layer. The transitive dependencies might overbuild, and output folder of current project (Service Layer) might contain both versions of the transitive project dependency (Domain-net48.dll and Domain-netstd20.dll). This limitation can be workarounded by switching of the transitive project dependencies via `DisableTransitiveProjectReferences` (same as shown in [Access to transitive project references](#access-to-transitive-project-references)) +`SetTargetFramework` is currently not honored by the NuGet client([nuget issue #12436](https://github.com/NuGet/Home/issues/12436)), so the output folder will contain binaries from nuget packages as if this metadata was not used. To workaround this the apropriate nuget needs to be directly referenced from the project enforcing reference framework via `SetTargetFramework`, or copied to output/publish folder via different means. + + +`SetTargetFramework` will properly enforce the framework for the `ProjectReference` chain. Once the `TargetFramework` overriding is encountered it is passed down the reference chain and the `ProjectReference`s respect it during the `TargetFramework` resolution. Due to the nature of handling of [transitive references in .NET-SDK style projects](#net-sdk-projects-and-access-to-transitive-references) and the fact that NuGet client doesn't honor `SetTargetFramework`, the transitive references can get resolved and built for multiple `TargetFramework`s. This means the output folder will contain proper version of the direct dependency - Repository Layer. The transitive references might overbuild, and output folder of current project (Service Layer) might contain both versions of the transitive project dependency (Domain-net48.dll and Domain-netstd20.dll). This limitation can be workarounded by switching of the transitive project references via `DisableTransitiveProjectReferences` (same as shown in [Access to transitive project references](#access-to-transitive-project-references)) -`SetTargetFramework` is currently not honored by the nuget client([nuget issue #12436](https://github.com/NuGet/Home/issues/12436)), so the output folder will contain binaries from nuget packages as if this metadata was not used. To workaround this the apropriate nuget needs to be directly referenced from the project enforcing reference framework via `SetTargetFramework`, or copied to output/publish folder via different means. \ No newline at end of file From 59e729009166da646359be165d16c72f8fdaee6e Mon Sep 17 00:00:00 2001 From: Jan Krivanek Date: Fri, 24 Mar 2023 17:17:41 +0100 Subject: [PATCH 4/7] Fix samples formatting --- .../wiki/Controlling-Dependencies-Behavior.md | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/documentation/wiki/Controlling-Dependencies-Behavior.md b/documentation/wiki/Controlling-Dependencies-Behavior.md index f5d25bce3ce..c122946d774 100644 --- a/documentation/wiki/Controlling-Dependencies-Behavior.md +++ b/documentation/wiki/Controlling-Dependencies-Behavior.md @@ -38,9 +38,9 @@ Service Layer definition: net48 - 10 + 10 - true + true ``` @@ -50,9 +50,9 @@ namespace Service; public class PersonsAccessor { - private Repository.Persona _persona; - // This is allowed unless DisableTransitiveProjectReferences=true is passed into build. - // private Domain.PersonTable _tbl; + private Repository.Persona _persona; + // This is allowed unless DisableTransitiveProjectReferences=true is passed into build. + // private Domain.PersonTable _tbl; } ``` @@ -68,8 +68,8 @@ In our previous example let's have `Repository Layer` reference `newtonsoft.json - compile - + compile + ``` @@ -82,7 +82,7 @@ namespace Service; public class PersonsAccessor { - private Repository.Persona _persona; + private Repository.Persona _persona; } ``` @@ -116,8 +116,8 @@ Detailed options description can be found in [Controlling package dependency ass - compile - + compile + ``` @@ -128,8 +128,8 @@ Detailed options description can be found in [Controlling package dependency ass - all - + all + ``` From 5ff294222c8d2e0f6fd5688e5b08a6f7243292cd Mon Sep 17 00:00:00 2001 From: Jan Krivanek Date: Tue, 28 Mar 2023 09:36:21 +0200 Subject: [PATCH 5/7] Apply suggestions from code review Co-authored-by: Rainer Sigwald --- documentation/wiki/Controlling-Dependencies-Behavior.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/wiki/Controlling-Dependencies-Behavior.md b/documentation/wiki/Controlling-Dependencies-Behavior.md index c122946d774..462a861269c 100644 --- a/documentation/wiki/Controlling-Dependencies-Behavior.md +++ b/documentation/wiki/Controlling-Dependencies-Behavior.md @@ -4,13 +4,13 @@ MSBuild recognizes a [few types of references](https://learn.microsoft.com/previ ## .NET SDK projects and access to transitive references -For [.NET SDK projects](https://learn.microsoft.com/dotnet/core/project-sdk/overview) restore operation by default makes all transitive references accessible as if they were direct references. +[.NET SDK projects](https://learn.microsoft.com/dotnet/core/project-sdk/overview) by default make all transitive references accessible as if they were direct references. This is required by the compiler and analyzers to be able to properly inspect the whole dependency or/and inheritance chain of types when deciding about particular checks. It is facilitated via `project.assets.json` file created by NuGet client during the restore operation. This file captures the whole transitive closure of the project dependency tree. -SDK build tasks require existence of this file (hence the infamous `Assets file \project.assets.json not found` if the MSBuild.exe is run without prior restore operation). It is used to reconstruct the `ProjectReference`s and `PackageReference`s for the project and pass them to MSBuild. For this reason MSBuild and compiler by default sees those transitive references as if they were direct references. +SDK build tasks require existence of this file (hence the infamous `Assets file \project.assets.json not found` if the MSBuild.exe is run without prior restore operation). It is used to reconstruct the `ProjectReference`s and create `Reference` items for the content of `PackageReference`s for the project and make them available to the rest of the build. For this reason MSBuild and compiler by default sees those transitive references as if they were direct references. ## Access to transitive project references From 2431f9d3c15dc7179e045b8da8a70d9745648f1e Mon Sep 17 00:00:00 2001 From: Jan Krivanek Date: Tue, 28 Mar 2023 13:56:10 +0200 Subject: [PATCH 6/7] Clarify the documentation based on feedback --- .../wiki/Controlling-Dependencies-Behavior.md | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/documentation/wiki/Controlling-Dependencies-Behavior.md b/documentation/wiki/Controlling-Dependencies-Behavior.md index 462a861269c..2f91144656f 100644 --- a/documentation/wiki/Controlling-Dependencies-Behavior.md +++ b/documentation/wiki/Controlling-Dependencies-Behavior.md @@ -6,7 +6,7 @@ MSBuild recognizes a [few types of references](https://learn.microsoft.com/previ [.NET SDK projects](https://learn.microsoft.com/dotnet/core/project-sdk/overview) by default make all transitive references accessible as if they were direct references. -This is required by the compiler and analyzers to be able to properly inspect the whole dependency or/and inheritance chain of types when deciding about particular checks. +This is provided for the compiler and analyzers to be able to properly inspect the whole dependency or/and inheritance chain of types when deciding about particular checks. It is facilitated via `project.assets.json` file created by NuGet client during the restore operation. This file captures the whole transitive closure of the project dependency tree. @@ -58,12 +58,24 @@ public class PersonsAccessor ## Access to transitive package references -The transitive access to references works by default for package references as well. This can be opted out via `PrivateAssets=compile` on the `PackageReference` of the concern. (More details on [Controlling package dependency assets](https://learn.microsoft.com/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets)) +The transitive access to references works by default for package references as well. This can be opted out for referencing projects via `PrivateAssets=compile` on the `PackageReference` of the concern. (More details on [Controlling package dependency assets](https://learn.microsoft.com/nuget/consume-packages/package-references-in-project-files#controlling-dependency-assets)). + +When using this metadatum - the access to the package, its dirrect and transitive dependencies is **not** restricted for the project declaring the refenerence on the package in its `Project` element. It is restricted for the projects referencing the project (or package) that specified the `PackageRegerence` with the `PrivateAssets` metadatum. *Example*: In our previous example let's have `Repository Layer` reference `newtonsoft.json`: +```mermaid +flowchart LR + Service[Service Layer] --> Repository + Repository[Repository Layer] --> newtonsoft.json[newtonsoft.json] +``` + +We are not able to influence access to `newtonsoft.json` and its dependencies (would there be any) in the `Repository Layer`, but we can prevent it from propagating to `Service Layer`. + +`Repository Layer`: + ```xml @@ -73,7 +85,7 @@ In our previous example let's have `Repository Layer` reference `newtonsoft.json ``` -Then our `Service Layer` would have access to `newtonsoft.json` (unless opted out via `PrivateAssets=compile`): +Unless opted out via `PrivateAssets=compile`, our `Service Layer` would have access to `newtonsoft.json`: ```csharp namespace Service; @@ -86,6 +98,9 @@ public class PersonsAccessor } ``` +**Notes:** + `PrivateAssets` metadatum (and it's counterparts `IncludeAssets` and `ExcludeAssets`) is applicable to `PackageReference` and controls exposure of dependencies to the consuming projects, not the current project. It is currently not possible to prevent access to package references from within directly referencing project - this is purely decision of the package itself (as it can define it's dependencies as `PrivateAssets`). + ## Not copying dependencies to output By default the above mentioned dependency types are copied to the build output directory during the build. There can be various scenarios where this behavior is not desired (examples: dependency is compile time only or contains a logic for build; component is plugin to a main app and there is a desire not to duplicate common dependencies in output). From 6455a760a2760e96d93fb3f8384df8b669952b09 Mon Sep 17 00:00:00 2001 From: Jan Krivanek Date: Fri, 21 Apr 2023 17:41:38 +0200 Subject: [PATCH 7/7] Reflect PR suggestions --- documentation/wiki/Controlling-Dependencies-Behavior.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/documentation/wiki/Controlling-Dependencies-Behavior.md b/documentation/wiki/Controlling-Dependencies-Behavior.md index 2f91144656f..ceafb75a2d1 100644 --- a/documentation/wiki/Controlling-Dependencies-Behavior.md +++ b/documentation/wiki/Controlling-Dependencies-Behavior.md @@ -172,6 +172,8 @@ The opt-out mechanism is analogous to [Assembly Reference copy opt-out](#not-cop ``` +Same metadata and logic applies here as it is being inherited from the `Reference` Item definition and the logic treats it identicaly. + ## ProjectReference without accessibility and copying to output In a specific scenarios we might want to indicate that specific project should be built prior our project but said project should not be reference accessible nor its output copied to current project output. This can be helpful for build time only dependencies - projects defining behavior that is going to be used as build step of a current project. @@ -245,7 +247,7 @@ net7 |---- Newtonsoft.Json.dll ``` -Would we want to reference the netstandard version of the Repository Layer in our Service Layer - we can force the reference chain via `SetTargetFramework` metadata on `ProjectReference` item: +Should we want to reference the netstandard version of the Repository Layer in our Service Layer - we can force the reference chain via `SetTargetFramework` metadata on `ProjectReference` item: ```xml