Skip to content

Commit

Permalink
Merge pull request #21986 from dsplaisted/selfcontained-flow
Browse files Browse the repository at this point in the history
Update handling of SelfContained and RuntimeIdentifier properties when specified on the command line
  • Loading branch information
dsplaisted authored Aug 22, 2022
2 parents cef9d95 + 4d634e9 commit 5d20158
Show file tree
Hide file tree
Showing 10 changed files with 505 additions and 19 deletions.
53 changes: 53 additions & 0 deletions documentation/general/SelfContainedBreakingChangeNotification.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# [Breaking change]: Handling of command-line RuntimeIdentifier and SelfContained properties across project references

## Description

The `RuntimeIdentifier` and `SelfContained` properties can be specified on the command line to commands such as `dotnet build` and `dotnet publish`.
They can be specified either via parameters such as `-r` or `--self-contained`, or via the generic `-p:Key=Value` parameter, such as `-p:SelfContained=true`.

If these properties are specified on the command line, we've updated how they are applied (or not applied) to projects referenced by the initial project that is being built.

## Version

???

## Previous behavior

If `SelfContained` was specified on the command line, it would always flow to referenced projects.

`RuntimeIdentifier` would flow to referenced projects where either the `RuntimeIdentifier` or `RuntimeIdentifiers` properties were non-empty.

## New Behavior

Both `SelfContained` and `RuntimeIdentifier` will flow to a referenced project if any of the following are true for the referenced project:

- The `IsRidAgnostic` property is set to `false`
- The `OutputType` is `Exe` or `WinExe`
- Either the `RuntimeIdentifer` or `RuntimeIdentifiers` property is non-empty

## Type of breaking change

Source incompatible

## Reason for change

As of .NET SDK 6.0.100, we recommend specifying the value for self-contained on the command line if you specify the RuntimeIdentifier.
(This is because in the future we are considering [changing the logic](https://github.com/dotnet/designs/blob/main/accepted/2021/architecture-targeting.md)
so that specifying the RuntimeIdentifier on the command line doesn't automatically set the app to self-contained.) We also added a warning message
to guide you to do so.

However, if you followed the warning and switched to a command specifying both the RuntimeIdentifier and the value for self-contained (for example
`dotnet build -r win-x64 --self-contained`), the command could fail if you referenced an Exe project, because the `RuntimeIdentifier` you specified
would not apply to the referenced project, but the `SelfContained` value would, and it's an error for an Exe project to have `SelfContained` set to
true without having a `RuntimeIdentifier` set.

## Recommended action

If you were relying on the `SelfContained` property to apply to all projects when it was specified on the command line, then you can get similar behavior
by setting `IsRidAgnostic` to false either in a file ([such as Directory.Build.props](https://docs.microsoft.com/visualstudio/msbuild/customize-your-build#directorybuildprops-and-directorybuildtargets)),
or as a command-line parameter such as `-p:IsRidAgnostic=false`.

## Open Questions

TODO: How does this apply to solutions? Could a solution build set IsRidAgnostic to false for all projects, and would that fix other issues we have when specifying the RuntimeIdentifier for a solution build?
TODO: What happens if there's an Exe1 -> Library -> Exe2 reference, especially if there's also a direct reference from Exe1 -> Exe2
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,38 @@ protected override void ExecuteCore()
bool shouldBeValidatedAsExecutableReference = MSBuildUtilities.ConvertStringToBool(projectAdditionalProperties["ShouldBeValidatedAsExecutableReference"], true);
bool referencedProjectIsExecutable = MSBuildUtilities.ConvertStringToBool(projectAdditionalProperties["_IsExecutable"]);
bool referencedProjectIsSelfContained = MSBuildUtilities.ConvertStringToBool(projectAdditionalProperties["SelfContained"]);
bool referencedProjectHadSelfContainedSpecified = MSBuildUtilities.ConvertStringToBool(projectAdditionalProperties["_SelfContainedWasSpecified"]);

var globalProperties = BuildEngine6.GetGlobalProperties();

bool selfContainedIsGlobalProperty = globalProperties.ContainsKey("SelfContained");
bool runtimeIdentifierIsGlobalProperty = globalProperties.ContainsKey("RuntimeIdentifier");

bool projectIsRidAgnostic = true;
if (projectAdditionalProperties.TryGetValue("IsRidAgnostic", out string isRidAgnostic) &&
bool.TryParse(isRidAgnostic, out bool isRidAgnosticParseResult))
{
projectIsRidAgnostic = isRidAgnosticParseResult;
}

if (!projectIsRidAgnostic)
{
// If the project is NOT RID agnostic, and SelfContained was set as a global property,
// then SelfContained will flow across the project reference when we go to build it,
// despite the fact that we ignored it when doing the GetTargetFrameworks negotiation
if (selfContainedIsGlobalProperty && SelfContained)
{
referencedProjectIsSelfContained = true;
}

// If the project is NOT RID agnostic, then a global RuntimeIdentifier will flow to it.
// If the project didn't explicitly specify a value for SelfContained, then this will
// set SelfContained to true
if (runtimeIdentifierIsGlobalProperty && !referencedProjectHadSelfContainedSpecified)
{
referencedProjectIsSelfContained = true;
}
}

if (referencedProjectIsExecutable && shouldBeValidatedAsExecutableReference)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ Copyright (c) .NET Foundation. All rights reserved.

<PropertyGroup>
<_IsExecutable Condition="'$(OutputType)' == 'Exe' or '$(OutputType)'=='WinExe'">true</_IsExecutable>
</PropertyGroup>

</PropertyGroup>

<PropertyGroup Condition="'$(HasRuntimeOutput)' == ''">
<HasRuntimeOutput>$(_IsExecutable)</HasRuntimeOutput>
<_UsingDefaultForHasRuntimeOutput>true</_UsingDefaultForHasRuntimeOutput>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ Copyright (c) .NET Foundation. All rights reserved.
<PredefinedCulturesOnly Condition="'$(PredefinedCulturesOnly)' == '' and '$(InvariantGlobalization)' == 'true'">true</PredefinedCulturesOnly>
</PropertyGroup>

<!-- Set the IsRidAgnostic property if this project should NOT accept global RuntimeIdentifier and SelfContained
property values from referencing projects. -->
<PropertyGroup Condition="'$(IsRidAgnostic)' == ''">
<IsRidAgnostic Condition="('$(_IsExecutable)' == 'true' And '$(IsTestProject)' != 'true') Or
'$(RuntimeIdentifier)' != '' Or
'$(RuntimeIdentifiers)' != ''">false</IsRidAgnostic>
<IsRidAgnostic Condition="'$(IsRidAgnostic)' == ''">true</IsRidAgnostic>
</PropertyGroup>

<!-- Opt into .NET Core resource-serialization strategy by default when targeting frameworks
that support it by default.
-->
Expand Down Expand Up @@ -1073,9 +1082,11 @@ Copyright (c) .NET Foundation. All rights reserved.
<ItemGroup>
<AdditionalTargetFrameworkInfoProperty Include="SelfContained"/>
<AdditionalTargetFrameworkInfoProperty Include="_IsExecutable"/>
<AdditionalTargetFrameworkInfoProperty Include="IsRidAgnostic"/>
<AdditionalTargetFrameworkInfoProperty Include="ShouldBeValidatedAsExecutableReference"/>
<AdditionalTargetFrameworkInfoProperty Include="_SelfContainedWasSpecified"/>
</ItemGroup>

<PropertyGroup Condition="'$(RuntimeIdentifier)' == 'browser-wasm'">
<!-- Don't generate a NETSDK1151 error if a non self-contained Exe references a Blazor wasm Exe -->
<ShouldBeValidatedAsExecutableReference>false</ShouldBeValidatedAsExecutableReference>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -488,8 +488,8 @@ public void It_can_use_implicitly_defined_compilation_constants(string targetFra
testProj.AdditionalProperties["TargetPlatformIdentifier"] = targetPlatformIdentifier;
testProj.AdditionalProperties["TargetPlatformVersion"] = targetPlatformVersion;
}
var testAsset = _testAssetsManager.CreateTestProject(testProj, targetFramework);
File.WriteAllText(Path.Combine(testAsset.Path, testProj.Name, $"{testProj.Name}.cs"), @"

testProj.SourceFiles[$"{testProj.Name}.cs"] = @"
using System;
class Program
{
Expand Down Expand Up @@ -529,7 +529,8 @@ static void Main(string[] args)
Console.WriteLine(""IOS"");
#endif
}
}");
}";
var testAsset = _testAssetsManager.CreateTestProject(testProj, targetFramework);

var buildCommand = new BuildCommand(Log, Path.Combine(testAsset.Path, testProj.Name));
buildCommand
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,7 @@ public void It_does_not_include_items_in_any_group_if_group_specific_default_inc
XElement itemGroup = new XElement(ns + "ItemGroup");
project.Root.Add(itemGroup);
itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Include", testProject.Name + ".cs")));
itemGroup.Add(new XElement(ns + "Compile", new XAttribute("Include", testProject.Name + "Program.cs")));
});

var projectFolder = Path.Combine(testAsset.TestRoot, testProject.Name);
Expand All @@ -409,7 +410,7 @@ public void It_does_not_include_items_in_any_group_if_group_specific_default_inc

var compileItems = getCompileItemsCommand.GetValues();
RemoveGeneratedCompileItems(compileItems);
compileItems.ShouldBeEquivalentTo(new[] { testProject.Name + ".cs" });
compileItems.ShouldBeEquivalentTo(new[] { testProject.Name + ".cs", testProject.Name + "Program.cs" });

// Validate None items.
var getNoneItemsCommand = new GetValuesCommand(Log, projectFolder, testProject.TargetFrameworks, "None", GetValuesCommand.ValueType.Item);
Expand Down
Loading

0 comments on commit 5d20158

Please sign in to comment.