diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml
index 2c658e58f0b4..5e8189ad7e95 100644
--- a/eng/Version.Details.xml
+++ b/eng/Version.Details.xml
@@ -105,9 +105,9 @@
https://github.com/dotnet/aspnetcore
d50065c4a4fe31a66a1cc2e1a31896d30464da13
-
+
https://github.com/nuget/nuget.client
- 1a082949ae5b6da7ca2cce047396c53ae1afdde7
+ 125f673fd1cdb3cc012f62aa3ce764d2460b89eb
https://github.com/microsoft/vstest
@@ -246,13 +246,13 @@
9a1c3e1b7f0c8763d4c96e593961a61a72679a7b
-
+
https://github.com/dotnet/roslyn-analyzers
- 846a766f73caa82608db6fee9f2860004298449f
+ ea9fb45000311153bfc91690f306cca2b80e6b83
-
+
https://github.com/dotnet/roslyn-analyzers
- 846a766f73caa82608db6fee9f2860004298449f
+ ea9fb45000311153bfc91690f306cca2b80e6b83
diff --git a/eng/Versions.props b/eng/Versions.props
index 1460f902048c..d770b5f19ff3 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -16,7 +16,7 @@
- false
+ true
release
@@ -63,7 +63,7 @@
- 6.4.0-preview.3.107
+ 6.4.0-rc.117
$(NuGetBuildTasksPackageVersion)
6.0.0-rc.278
$(NuGetBuildTasksPackageVersion)
@@ -102,7 +102,7 @@
- 7.0.0-preview1.22504.1
+ 7.0.0-preview1.22513.1
@@ -112,7 +112,7 @@
so target one that matches the version in minimumMSBuildVersion.
This avoids the need to juggle references to packages that have been updated in newer MSBuild. -->
- $([System.IO.File]::ReadAllText('$(RepoRoot)\src\Layout\redist\minimumMSBuildVersion').Trim())
+ $([System.IO.File]::ReadAllText('$(RepoRoot)\src\Layout\redist\minimumMSBuildVersion').Trim())
$(MicrosoftBuildPackageVersion)
$(MicrosoftBuildPackageVersion)
17.5.0-preview-22513-03
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/DiagnosticIds.cs b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/DiagnosticIds.cs
index cd86e7d20eb9..ef25359022e4 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/DiagnosticIds.cs
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/DiagnosticIds.cs
@@ -26,6 +26,8 @@ public static class DiagnosticIds
public const string CannotAddAttribute = "CP0016";
public const string CannotChangeParameterName = "CP0017";
public const string CannotAddSealedToInterfaceMember = "CP0018";
+ public const string CannotReduceVisibility = "CP0019";
+ public const string CannotExpandVisibility = "CP0020";
// Assembly loading ids
public const string AssemblyNotFound = "CP1001";
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx
index 7473dcfa8549..9d810ecb933a 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx
@@ -240,4 +240,10 @@
Cannot add sealed keyword to default interface member '{0}'.
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
\ No newline at end of file
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/AttributesMustMatch.cs b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/AttributesMustMatch.cs
index bd078b4874d4..d4e8ff594acd 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/AttributesMustMatch.cs
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/AttributesMustMatch.cs
@@ -62,6 +62,11 @@ private void AddDifference(IList differences, DifferenceType d
return;
}
+ if (!_settings.StrictMode && dt == DifferenceType.Added)
+ {
+ return;
+ }
+
CompatDifference difference = dt switch
{
DifferenceType.Changed => new CompatDifference(
@@ -167,10 +172,17 @@ private void ReportAttributeDifferences(ISymbol containing,
for (int i = 0; i < rightGroup.Attributes.Count; i++)
{
- if (!rightGroup.Seen[i])
+ if (!rightGroup.Seen[i] && _settings.StrictMode)
{
// Attribute arguments exist on right but not left.
- // Issue "changed" diagnostic.
+ // Left
+ // [Foo("a")]
+ // void F()
+ // Right
+ // [Foo("a")]
+ // [Foo("b")]
+ // void F()
+ // Issue "changed" diagnostic when in strict mode.
AddDifference(differences, DifferenceType.Changed, leftMetadata, rightMetadata, containing, itemRef, rightGroup.Attributes[i]);
}
}
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs
index a2be104545fe..1bb7d5ad2198 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs
@@ -4,6 +4,7 @@
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.DotNet.ApiCompatibility.Abstractions;
+using Microsoft.DotNet.ApiCompatibility.Extensions;
namespace Microsoft.DotNet.ApiCompatibility.Rules
{
@@ -50,6 +51,12 @@ private void RunOnMemberSymbol(ISymbol? left, ISymbol? right, ITypeSymbol leftCo
if (left.IsVirtual)
{
+ // Removing the virtual keyword from a member in a sealed type won't be a breaking change.
+ if (leftContainingType.IsEffectivelySealed(_settings.IncludeInternalSymbols))
+ {
+ return;
+ }
+
// If left is virtual and right is not, then emit a diagnostic
// specifying that the virtual modifier cannot be removed.
if (!right.IsVirtual)
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotChangeVisibility.cs b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotChangeVisibility.cs
new file mode 100644
index 000000000000..5a5ffcd950e2
--- /dev/null
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotChangeVisibility.cs
@@ -0,0 +1,115 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Runtime;
+using Microsoft.CodeAnalysis;
+using Microsoft.DotNet.ApiCompatibility.Abstractions;
+using Microsoft.DotNet.ApiCompatibility.Extensions;
+
+namespace Microsoft.DotNet.ApiCompatibility.Rules
+{
+ ///
+ /// This class implements a rule to check that the visibility of symbols is not reduced.
+ /// In strict mode, it also checks that the visibility isn't expanded.
+ ///
+ public class CannotChangeVisibility : IRule
+ {
+ private readonly RuleSettings _settings;
+
+ public CannotChangeVisibility(RuleSettings settings, IRuleRegistrationContext context)
+ {
+ _settings = settings;
+ context.RegisterOnMemberSymbolAction(RunOnMemberSymbol);
+ context.RegisterOnTypeSymbolAction(RunOnTypeSymbol);
+ }
+
+ private static Accessibility NormalizeInternals(Accessibility a) => a switch
+ {
+ Accessibility.ProtectedOrInternal => Accessibility.Protected,
+ Accessibility.ProtectedAndInternal or Accessibility.Internal => Accessibility.Private,
+ _ => a,
+ };
+
+ private int CompareAccessibility(Accessibility a, Accessibility b)
+ {
+ if (!_settings.IncludeInternalSymbols)
+ {
+ a = NormalizeInternals(a);
+ b = NormalizeInternals(b);
+ }
+
+ if (a == b)
+ {
+ return 0;
+ }
+
+ return (a, b) switch
+ {
+ (Accessibility.Public, _) => 1,
+ (_, Accessibility.Public) => -1,
+ (Accessibility.ProtectedOrInternal, _) => 1,
+ (_, Accessibility.ProtectedOrInternal) => -1,
+ (Accessibility.Protected or Accessibility.Internal, _) => 1,
+ (_, Accessibility.Protected or Accessibility.Internal) => -1,
+ (Accessibility.ProtectedAndInternal, _) => 1,
+ (_, Accessibility.ProtectedAndInternal) => -1,
+ _ => throw new NotImplementedException(),
+ };
+ }
+
+ private void RunOnSymbol(
+ ISymbol? left,
+ ISymbol? right,
+ MetadataInformation leftMetadata,
+ MetadataInformation rightMetadata,
+ IList differences)
+ {
+ // The MemberMustExist rule handles missing symbols and therefore this rule only runs when left and right is not null.
+ if (left is null || right is null)
+ {
+ return;
+ }
+
+ Accessibility leftAccess = left.DeclaredAccessibility;
+ Accessibility rightAccess = right.DeclaredAccessibility;
+ int accessComparison = CompareAccessibility(leftAccess, rightAccess);
+
+ if (accessComparison > 0)
+ {
+ differences.Add(new CompatDifference(leftMetadata,
+ rightMetadata,
+ DiagnosticIds.CannotReduceVisibility,
+ string.Format(Resources.CannotReduceVisibility, left, leftAccess, rightAccess),
+ DifferenceType.Changed,
+ left));
+ }
+ else if (_settings.StrictMode && accessComparison < 0)
+ {
+ differences.Add(new CompatDifference(leftMetadata,
+ rightMetadata,
+ DiagnosticIds.CannotExpandVisibility,
+ string.Format(Resources.CannotExpandVisibility, right, leftAccess, rightAccess),
+ DifferenceType.Changed,
+ right));
+ }
+ }
+
+ private void RunOnTypeSymbol(
+ ITypeSymbol? left,
+ ITypeSymbol? right,
+ MetadataInformation leftMetadata,
+ MetadataInformation rightMetadata,
+ IList differences) => RunOnSymbol(left, right, leftMetadata, rightMetadata, differences);
+
+ private void RunOnMemberSymbol(
+ ISymbol? left,
+ ISymbol? right,
+ ITypeSymbol leftContainingType,
+ ITypeSymbol rightContainingType,
+ MetadataInformation leftMetadata,
+ MetadataInformation rightMetadata,
+ IList differences) => RunOnSymbol(left, right, leftMetadata, rightMetadata, differences);
+ }
+}
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/RuleFactory.cs b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/RuleFactory.cs
index 341b875bd16e..747a644dca39 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/RuleFactory.cs
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/RuleFactory.cs
@@ -39,7 +39,8 @@ public IRule[] CreateRules(RuleSettings settings, IRuleRegistrationContext conte
new CannotRemoveBaseTypeOrInterface(settings, context),
new CannotSealType(settings, context),
new EnumsMustMatch(settings, context),
- new MembersMustExist(settings, context)
+ new MembersMustExist(settings, context),
+ new CannotChangeVisibility(settings, context)
};
if (_enableRuleAttributesMustMatch)
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf
index 27cc9270a65c..131100e6ca39 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf
@@ -87,6 +87,16 @@
Název parametru u člena {0} se změnil z {1} na {2}.
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
Atribut {0} nelze odebrat z {1}.
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf
index 148f10710e5b..d29904e874cf 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf
@@ -87,6 +87,16 @@
Der Parametername des Members "{0}" wurde von "{1}" in "{2}" geändert.
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
Das Attribut "{0}" kann nicht aus "{1}" entfernt werden.
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf
index fc456bf2c297..6721143f8649 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf
@@ -87,6 +87,16 @@
El nombre de parámetro del miembro '{0}' cambió de '{1}' a '{2}'.
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
No se puede quitar el atributo '{0}' de '{1}'.
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf
index 6655b69e4083..2ad359cfdc97 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf
@@ -87,6 +87,16 @@
Le nom du paramètre sur le membre « {0} » est passé de « {1} » à « {2} ».
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
Impossible de supprimer l’attribut « {0} » de « {1} ».
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf
index c36c61b78cff..289caf6c8a81 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf
@@ -87,6 +87,16 @@
Il nome del parametro nel membro '{0}' è stato modificato da '{1}' a '{2}'.
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
Non è possibile rimuovere l'attributo '{0}' da '{1}'.
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf
index f9df9120294d..6acabcccea94 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf
@@ -87,6 +87,16 @@
メンバー '{0}' のパラメーター名が '{1}' から '{2}' に変更されました。
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
属性 '{0}' を '{1}' から削除できません。
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf
index 2b3f366bad4e..60517def4b2b 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf
@@ -87,6 +87,16 @@
멤버 '{0}'의 매개 변수 이름이 '{1}'에서 '{2}'(으)로 변경되었습니다.
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
'{1}'에서 특성 '{0}'을(를) 제거할 수 없습니다.
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf
index e30706482074..490b7be942b1 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf
@@ -87,6 +87,16 @@
Nazwa parametru elementu członkowskiego „{0}” została zmieniona z „{1}” na „{2}”.
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
Nie można usunąć atrybutu „{0}” z elementu „{1}”.
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf
index a7bac27d9eb1..13b6c11d7070 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf
@@ -87,6 +87,16 @@
Nome do parâmetro no membro '{0}' alterado de '{1}' para '{2}'.
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
Não é possível remover o atributo '{0}' de '{1}'.
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf
index 128fe9f46c58..cc5d247c856f 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf
@@ -87,6 +87,16 @@
Имя параметра на члене "{0}" изменено с "{1}" на "{2}".
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
Не удается удалить атрибут "{0}" из "{1}".
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf
index da71a633b1ea..9e2b91f92e5d 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf
@@ -87,6 +87,16 @@
'{0}' üyesinin parametre adı '{1}' iken '{2}' olarak değiştirildi.
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
'{0}' özniteliği '{1}' sınıfından kaldırılamıyor.
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf
index d8ccd28402ec..1ff387a8d4aa 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf
@@ -87,6 +87,16 @@
成员 "{0}" 上的参数名称已从 "{1}" 更改为 "{2}"。
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
无法从 "{1}" 中删除属性 "{0}"。
diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf
index c6e3479330c8..caa004f4c38f 100644
--- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf
+++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf
@@ -87,6 +87,16 @@
成員 '{0}' 上的參數名稱從 '{1}' 變更為 '{2}'。
+
+
+ Visibility of '{0}' expanded from '{1}' to '{2}'.
+
+
+
+
+ Visibility of '{0}' reduced from '{1}' to '{2}'.
+
+
無法從 '{1}' 移除屬性 '{0}'。
diff --git a/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs b/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs
index 77e0a621cdce..454534779a5d 100644
--- a/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs
+++ b/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs
@@ -63,7 +63,7 @@ internal MsBuildFileSetFactory(
public async Task CreateAsync(CancellationToken cancellationToken)
{
- var watchList = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ var watchList = Path.GetTempFileName();
try
{
var projectDir = Path.GetDirectoryName(_projectFile);
diff --git a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
index 636f43d119b4..065f750f7200 100644
--- a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
+++ b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj
@@ -23,6 +23,7 @@
+
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj b/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj
index 03ea923734ef..b954e2c4659d 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj
@@ -21,8 +21,12 @@
+
$(PkgMicrosoft_Build_Runtime)\contentFiles\any\net6.0\MSBuild.dll
+ $(PkgMicrosoft_Build_Runtime)\contentFiles\any\net7.0\MSBuild.dll
diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs
index 312d6fecd7d4..b063b9fc4a08 100644
--- a/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs
+++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs
@@ -7,7 +7,7 @@
namespace Microsoft.Extensions.EnvironmentAbstractions
{
- internal class DirectoryWrapper: IDirectory
+ internal class DirectoryWrapper : IDirectory
{
public bool Exists(string path)
{
@@ -19,6 +19,11 @@ public ITemporaryDirectory CreateTemporaryDirectory()
return new TemporaryDirectory();
}
+ public string CreateTemporarySubdirectory()
+ {
+ return CreateTemporaryDirectory().DirectoryPath;
+ }
+
public IEnumerable EnumerateDirectories(string path)
{
return Directory.EnumerateDirectories(path);
diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs
index 06ed28ab76d7..cf69976a0844 100644
--- a/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs
+++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs
@@ -30,7 +30,7 @@ public string ToQuotedString()
public override string ToString()
{
- return ToQuotedString();
+ return Value;
}
public DirectoryPath GetDirectoryPath()
diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs
index 983bba34afc3..0a2e4e31e09f 100644
--- a/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs
+++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs
@@ -24,5 +24,9 @@ internal interface IDirectory
void Delete(string path, bool recursive);
void Move(string source, string destination);
+
+
+ /// Returns a new directory created under the temp folder. Can be on the mock under test or the real temp file folder.
+ string CreateTemporarySubdirectory();
}
}
diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj b/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj
index 041ba1315c78..6260c984866d 100644
--- a/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj
+++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj
@@ -12,4 +12,7 @@
true
+
+
+
diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs
index d43683e156f5..9ce8241c8967 100644
--- a/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs
+++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs
@@ -1,8 +1,9 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using Microsoft.Extensions.EnvironmentAbstractions;
using System.IO;
+using Microsoft.Extensions.EnvironmentAbstractions;
+using Microsoft.DotNet;
namespace Microsoft.DotNet.InternalAbstractions
{
@@ -12,8 +13,7 @@ internal class TemporaryDirectory : ITemporaryDirectory
public TemporaryDirectory()
{
- DirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
- Directory.CreateDirectory(DirectoryPath);
+ DirectoryPath = Path.Combine(PathUtilities.CreateTempSubdirectory());
}
public void Dispose()
diff --git a/src/Cli/dotnet/ShellShim/ShellShimRepository.cs b/src/Cli/dotnet/ShellShim/ShellShimRepository.cs
index 4dee9748d079..68390f1f2ce2 100644
--- a/src/Cli/dotnet/ShellShim/ShellShimRepository.cs
+++ b/src/Cli/dotnet/ShellShim/ShellShimRepository.cs
@@ -85,7 +85,8 @@ public void CreateShim(FilePath targetExecutablePath, ToolCommandName commandNam
ex);
}
},
- rollback: () => {
+ rollback: () =>
+ {
foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value)))
{
File.Delete(file.Value);
@@ -97,12 +98,13 @@ public void RemoveShim(ToolCommandName commandName)
{
var files = new Dictionary();
TransactionalAction.Run(
- action: () => {
+ action: () =>
+ {
try
{
foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value)))
{
- var tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ var tempPath = Path.Combine(_fileSystem.Directory.CreateTemporarySubdirectory(), Path.GetRandomFileName());
FileAccessRetrier.RetryOnMoveAccessFailure(() => _fileSystem.File.Move(file.Value, tempPath));
files[file.Value] = tempPath;
}
@@ -118,13 +120,15 @@ public void RemoveShim(ToolCommandName commandName)
ex);
}
},
- commit: () => {
+ commit: () =>
+ {
foreach (var value in files.Values)
{
_fileSystem.File.Delete(value);
}
},
- rollback: () => {
+ rollback: () =>
+ {
foreach (var kvp in files)
{
FileAccessRetrier.RetryOnMoveAccessFailure(() => _fileSystem.File.Move(kvp.Value, kvp.Key));
diff --git a/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs b/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs
index 799c99fbec39..8b04671a631b 100644
--- a/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs
+++ b/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs
@@ -3,11 +3,9 @@
using System;
using System.CommandLine;
-using System.CommandLine.Parsing;
using System.IO;
using System.Linq;
using Microsoft.DotNet.Cli.Utils;
-using Microsoft.DotNet.Tools.Common;
using NuGet.Common;
using NuGet.Configuration;
@@ -18,8 +16,6 @@ namespace Microsoft.DotNet.Cli
///
public static class SudoEnvironmentDirectoryOverride
{
- private const string SudoHomeDirectory = "/tmp/dotnet_sudo_home/";
-
///
/// Not for security use. Detect if command is running under sudo
/// via if SUDO_UID being set.
@@ -38,22 +34,9 @@ public static void OverrideEnvironmentVariableToTmp(ParseResult parseResult)
{
if (!OperatingSystem.IsWindows() && IsRunningUnderSudo() && IsRunningWorkloadCommand(parseResult))
{
- if (!TempHomeIsOnlyRootWritable(SudoHomeDirectory))
- {
- try
- {
- Directory.Delete(SudoHomeDirectory, recursive: true);
- }
- catch (DirectoryNotFoundException)
- {
- // Avoid read after write race condition
- }
- }
-
- Directory.CreateDirectory(SudoHomeDirectory);
-
+ string sudoHome = PathUtilities.CreateTempSubdirectory();
var homeBeforeOverride = Path.Combine(Environment.GetEnvironmentVariable("HOME"));
- Environment.SetEnvironmentVariable("HOME", SudoHomeDirectory);
+ Environment.SetEnvironmentVariable("HOME", sudoHome);
CopyUserNuGetConfigToOverriddenHome(homeBeforeOverride);
}
@@ -107,31 +90,5 @@ private static void CopyUserNuGetConfigToOverriddenHome(string homeBeforeOverrid
private static bool IsRunningWorkloadCommand(ParseResult parseResult) =>
parseResult.RootSubCommandResult() == (WorkloadCommandParser.GetCommand().Name);
-
- private static bool TempHomeIsOnlyRootWritable(string path)
- {
- if (StatInterop.LStat(path, out StatInterop.FileStatus fileStat) != 0)
- {
- return false;
- }
-
- return IsOwnedByRoot(fileStat) && GroupCannotWrite(fileStat) &&
- OtherUserCannotWrite(fileStat);
- }
-
- private static bool OtherUserCannotWrite(StatInterop.FileStatus fileStat)
- {
- return (fileStat.Mode & (int) StatInterop.Permissions.S_IWOTH) == 0;
- }
-
- private static bool GroupCannotWrite(StatInterop.FileStatus fileStat)
- {
- return (fileStat.Mode & (int) StatInterop.Permissions.S_IWGRP) == 0;
- }
-
- private static bool IsOwnedByRoot(StatInterop.FileStatus fileStat)
- {
- return fileStat.Uid == 0;
- }
}
}
diff --git a/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs b/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs
index 1f3a37d9ee0f..f9d20bf96648 100644
--- a/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs
+++ b/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs
@@ -11,7 +11,6 @@
using Microsoft.DotNet.Configurer;
using Microsoft.DotNet.Tools;
using Microsoft.Extensions.EnvironmentAbstractions;
-using NuGet.ProjectModel;
using NuGet.Versioning;
namespace Microsoft.DotNet.ToolPackage
@@ -46,17 +45,18 @@ public IToolPackage InstallPackage(
string rollbackDirectory = null;
return TransactionalAction.Run(
- action: () => {
+ action: () =>
+ {
try
{
var stageDirectory = _store.GetRandomStagingDirectory();
Directory.CreateDirectory(stageDirectory.Value);
rollbackDirectory = stageDirectory.Value;
- var tempProject = CreateTempProject(
+ string tempProject = CreateDirectoryWithTempProject(
packageId: packageId,
versionRange: versionRange,
- targetFramework: string.IsNullOrEmpty(targetFramework) ? BundledTargetFramework.GetTargetFrameworkMoniker() : targetFramework,
+ targetFramework: string.IsNullOrEmpty(targetFramework) ? BundledTargetFramework.GetTargetFrameworkMoniker() : targetFramework,
restoreDirectory: stageDirectory,
assetJsonOutputDirectory: stageDirectory,
rootConfigDirectory: packageLocation.RootConfigDirectory,
@@ -65,13 +65,13 @@ public IToolPackage InstallPackage(
try
{
_projectRestorer.Restore(
- tempProject,
+ new FilePath(tempProject),
packageLocation,
verbosity: verbosity);
}
finally
{
- File.Delete(tempProject.Value);
+ File.Delete(tempProject);
}
var version = _store.GetStagedPackageVersion(stageDirectory, packageId);
@@ -104,7 +104,8 @@ public IToolPackage InstallPackage(
ex);
}
},
- rollback: () => {
+ rollback: () =>
+ {
if (!string.IsNullOrEmpty(rollbackDirectory) && Directory.Exists(rollbackDirectory))
{
Directory.Delete(rollbackDirectory, true);
@@ -126,16 +127,13 @@ public IToolPackage InstallPackageToExternalManagedLocation(
string targetFramework = null,
string verbosity = null)
{
- var tempDirectoryForAssetJson = new DirectoryPath(Path.GetTempPath())
- .WithSubDirectories(Path.GetRandomFileName());
-
- Directory.CreateDirectory(tempDirectoryForAssetJson.Value);
+ var tempDirectoryForAssetJson = PathUtilities.CreateTempSubdirectory();
- var tempProject = CreateTempProject(
+ string tempProject = CreateDirectoryWithTempProject(
packageId: packageId,
versionRange: versionRange,
targetFramework: string.IsNullOrEmpty(targetFramework) ? BundledTargetFramework.GetTargetFrameworkMoniker() : targetFramework,
- assetJsonOutputDirectory: tempDirectoryForAssetJson,
+ assetJsonOutputDirectory: new DirectoryPath(tempDirectoryForAssetJson),
restoreDirectory: null,
rootConfigDirectory: packageLocation.RootConfigDirectory,
additionalFeeds: packageLocation.AdditionalFeeds);
@@ -143,19 +141,19 @@ public IToolPackage InstallPackageToExternalManagedLocation(
try
{
_projectRestorer.Restore(
- tempProject,
+ new FilePath(tempProject),
packageLocation,
verbosity: verbosity);
}
finally
{
- File.Delete(tempProject.Value);
+ File.Delete(tempProject);
}
- return ToolPackageInstance.CreateFromAssetFile(packageId, tempDirectoryForAssetJson);
+ return ToolPackageInstance.CreateFromAssetFile(packageId, new DirectoryPath(tempDirectoryForAssetJson));
}
- private FilePath CreateTempProject(
+ private string CreateDirectoryWithTempProject(
PackageId packageId,
VersionRange versionRange,
string targetFramework,
@@ -164,16 +162,14 @@ private FilePath CreateTempProject(
DirectoryPath? rootConfigDirectory,
string[] additionalFeeds)
{
- var tempProject = _tempProject ?? new DirectoryPath(Path.GetTempPath())
- .WithSubDirectories(Path.GetRandomFileName())
- .WithFile("restore.csproj");
-
- if (Path.GetExtension(tempProject.Value) != "csproj")
+ string tempProject;
+ if (_tempProject != null && _tempProject.HasValue)
{
- tempProject = new FilePath(Path.ChangeExtension(tempProject.Value, "csproj"));
+ tempProject = _tempProject.Value.Value;
+ Directory.CreateDirectory(Path.GetDirectoryName(tempProject));
}
-
- Directory.CreateDirectory(tempProject.GetDirectoryPath().Value);
+ else
+ tempProject = Path.Combine(PathUtilities.CreateTempSubdirectory(), "restore.csproj");
var tempProjectContent = new XDocument(
new XElement("Project",
@@ -203,7 +199,7 @@ private FilePath CreateTempProject(
new XAttribute("Project", "Sdk.targets"),
new XAttribute("Sdk", "Microsoft.NET.Sdk"))));
- File.WriteAllText(tempProject.Value, tempProjectContent.ToString());
+ File.WriteAllText(tempProject, tempProjectContent.ToString());
return tempProject;
}
diff --git a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
index d8df4fb955aa..52c7432fb321 100644
--- a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
+++ b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
@@ -5,10 +5,8 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
-using System.CommandLine.Parsing;
using System.IO;
using System.Linq;
-using System.Text;
using System.Threading.Tasks;
using Microsoft.Deployment.DotNet.Releases;
using Microsoft.DotNet.Cli;
@@ -17,7 +15,6 @@
using Microsoft.DotNet.Configurer;
using Microsoft.DotNet.ToolPackage;
using Microsoft.DotNet.Workloads.Workload.Install;
-using Microsoft.DotNet.Workloads.Workload.Install.InstallRecord;
using Microsoft.Extensions.EnvironmentAbstractions;
using Microsoft.NET.Sdk.WorkloadManifestReader;
using NuGet.Versioning;
@@ -79,7 +76,7 @@ public InstallingWorkloadCommand(
var sourceOption = parseResult.GetValueForOption(InstallingWorkloadCommandParser.SourceOption);
_packageSourceLocation = string.IsNullOrEmpty(configOption) && (sourceOption == null || !sourceOption.Any()) ? null :
new PackageSourceLocation(string.IsNullOrEmpty(configOption) ? null : new FilePath(configOption), sourceFeedOverrides: sourceOption);
-
+
var sdkWorkloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(_dotnetPath, _installedSdkVersion.ToString(), userProfileDir);
_workloadResolver = workloadResolver ?? WorkloadResolver.Create(sdkWorkloadManifestProvider, _dotnetPath, _installedSdkVersion.ToString(), _userProfileDir);
diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs
index 7e06aae182b1..0b4c199f1b3e 100644
--- a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs
+++ b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs
@@ -4,7 +4,6 @@
using System;
using System.Collections.Generic;
using System.CommandLine;
-using System.CommandLine.Parsing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
@@ -72,7 +71,7 @@ public ToolInstallGlobalOrToolPathCommand(
_environmentPathInstruction = environmentPathInstruction
?? EnvironmentPathFactory.CreateEnvironmentPathInstruction();
_createShellShimRepository = createShellShimRepository ?? ShellShimRepositoryFactory.CreateShellShimRepository;
- var tempDir = new DirectoryPath(Path.Combine(Path.GetTempPath(), "dotnet-tool-install"));
+ var tempDir = new DirectoryPath(PathUtilities.CreateTempSubdirectory());
var configOption = parseResult.GetValueForOption(ToolInstallCommandParser.ConfigOption);
var sourceOption = parseResult.GetValueForOption(ToolInstallCommandParser.AddSourceOption);
var packageSourceLocation = new PackageSourceLocation(string.IsNullOrEmpty(configOption) ? null : new FilePath(configOption), additionalSourceFeeds: sourceOption);
@@ -143,7 +142,7 @@ public override int Execute()
}
else
{
- framework = string.IsNullOrEmpty(_framework) ?
+ framework = string.IsNullOrEmpty(_framework) ?
null :
NuGetFramework.Parse(_framework);
}
diff --git a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs
index e0ca3d540524..dc4cd2d3f51d 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs
@@ -2,7 +2,6 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.CommandLine;
-using System.CommandLine.Parsing;
using System.IO;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
@@ -104,7 +103,7 @@ public WorkloadCommandBase(ParseResult parseResult,
? tempDirPath
: !string.IsNullOrWhiteSpace(parseResult.GetValueForOption(WorkloadInstallCommandParser.TempDirOption))
? parseResult.GetValueForOption(WorkloadInstallCommandParser.TempDirOption)
- : Path.GetTempPath();
+ : PathUtilities.CreateTempSubdirectory();
TempPackagesDirectory = new DirectoryPath(Path.Combine(TempDirectoryPath, "dotnet-sdk-advertising-temp"));
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs
index 956ed4bbec1f..245c526076f6 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs
@@ -19,6 +19,7 @@
using System.Text.Json;
using System.Threading.Tasks;
+
namespace Microsoft.DotNet.Workloads.Workload.Install
{
internal class FileBasedInstaller : IInstaller
@@ -51,7 +52,7 @@ public FileBasedInstaller(IReporter reporter,
{
_userProfileDir = userProfileDir;
_dotnetDir = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath);
- _tempPackagesDir = new DirectoryPath(tempDirPath ?? Path.GetTempPath());
+ _tempPackagesDir = new DirectoryPath(tempDirPath ?? PathUtilities.CreateTempSubdirectory());
ILogger logger = verbosity.IsDetailedOrDiagnostic() ? new NuGetConsoleLogger() : new NullLogger();
_restoreActionConfig = restoreActionConfig;
_nugetPackageDownloader = nugetPackageDownloader ??
@@ -188,7 +189,7 @@ public void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand
Directory.Delete(dir, true);
}
}
- });
+ });
}
}
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs
index ed04a4548648..368d4716b184 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
-using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
@@ -116,7 +115,7 @@ public void GarbageCollectInstalledWorkloadPacks(DirectoryPath? offlineCache = n
Log?.LogMessage("Starting garbage collection.");
IEnumerable installedFeatureBands = GetInstalledFeatureBands();
IEnumerable installedWorkloads = RecordRepository.GetInstalledWorkloads(_sdkFeatureBand);
- Dictionary<(WorkloadPackId id, string version),PackInfo> expectedWorkloadPacks = installedWorkloads
+ Dictionary<(WorkloadPackId id, string version), PackInfo> expectedWorkloadPacks = installedWorkloads
.SelectMany(workload => _workloadResolver.GetPacksInWorkload(workload))
.Distinct()
.Select(pack => _workloadResolver.TryGetPackInfo(pack))
@@ -415,9 +414,9 @@ public void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand
RollBackMsiInstall(msiToInstall);
}
});
-
+
}
-
+
}
void RollBackMsiInstall(WorkloadDownload msiToRollback, DirectoryPath? offlineCache = null)
@@ -492,17 +491,10 @@ public PackageId GetManifestPackageId(ManifestId manifestId, SdkFeatureBand feat
public async Task ExtractManifestAsync(string nupkgPath, string targetPath)
{
Log?.LogMessage($"ExtractManifestAsync: Extracting '{nupkgPath}' to '{targetPath}'");
-
- string extractionPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
- if (Directory.Exists(extractionPath))
- {
- Directory.Delete(extractionPath, true);
- }
+ string extractionPath = PathUtilities.CreateTempSubdirectory();
try
{
- Directory.CreateDirectory(extractionPath);
-
Log?.LogMessage($"ExtractManifestAsync: Temporary extraction path: '{extractionPath}'");
await _nugetPackageDownloader.ExtractPackageAsync(nupkgPath, new DirectoryPath(extractionPath));
if (Directory.Exists(targetPath))
@@ -959,7 +951,7 @@ public static NetSdkMsiInstallerClient Create(
if (nugetPackageDownloader == null)
{
- DirectoryPath tempPackagesDir = new(string.IsNullOrWhiteSpace(tempDirPath) ? Path.GetTempPath() : tempDirPath);
+ DirectoryPath tempPackagesDir = new(string.IsNullOrWhiteSpace(tempDirPath) ? PathUtilities.CreateTempSubdirectory() : tempDirPath);
nugetPackageDownloader = new NuGetPackageDownloader(tempPackagesDir,
filePermissionSetter: null, new FirstPartyNuGetPackageSigningVerifier(),
diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs
index 048d3dc22889..36c42b47cee4 100644
--- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs
+++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs
@@ -6,7 +6,6 @@
using System.IO;
using System.Linq;
using System.Net.Http;
-using System.Runtime.InteropServices;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.DotNet.Cli;
@@ -69,7 +68,7 @@ private static WorkloadManifestUpdater GetInstance(string userProfileDir)
var sdkVersion = Product.Version;
var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetPath, sdkVersion, userProfileDir);
var workloadResolver = WorkloadResolver.Create(workloadManifestProvider, dotnetPath, sdkVersion, userProfileDir);
- var tempPackagesDir = new DirectoryPath(Path.Combine(Path.GetTempPath(), "dotnet-sdk-advertising-temp"));
+ var tempPackagesDir = new DirectoryPath(PathUtilities.CreateTempSubdirectory());
var nugetPackageDownloader = new NuGetPackageDownloader(tempPackagesDir,
filePermissionSetter: null,
new FirstPartyNuGetPackageSigningVerifier(),
@@ -189,9 +188,9 @@ Dictionary Workloads
}
if (advertisingManifestVersionAndWorkloads != null &&
- ((advertisingManifestVersionAndWorkloads.Value.ManifestVersion.CompareTo(currentManifestVersion.manifestVersion) > 0
+ ((advertisingManifestVersionAndWorkloads.Value.ManifestVersion.CompareTo(currentManifestVersion.manifestVersion) > 0
&& advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.Equals(currentManifestVersion.sdkFeatureBand)) ||
- advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.CompareTo(currentManifestVersion.sdkFeatureBand) > 0))
+ advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.CompareTo(currentManifestVersion.sdkFeatureBand) > 0))
{
manifestUpdates.Add((new ManifestVersionUpdate(manifestId, currentManifestVersion.manifestVersion, currentManifestVersion.sdkFeatureBand.ToString(),
advertisingManifestVersionAndWorkloads.Value.ManifestVersion, advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.ToString()),
@@ -262,7 +261,7 @@ public async Task> GetManifestPackageDownloadsAsyn
var newPackageId = _workloadManifestInstaller.GetManifestPackageId(new ManifestId(manifest.Id), installedSdkFeatureBand);
(success, latestVersion) = await GetPackageVersion(newPackageId, packageSourceLocation: _packageSourceLocation, includePreview: includePreviews);
-
+
if (success)
{
downloads.Add(new WorkloadDownload(manifest.Id, newPackageId.ToString(), latestVersion.ToString()));
@@ -307,7 +306,7 @@ private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest,
try
{
var adManifestPath = GetAdvertisingManifestPath(_sdkFeatureBand, manifestId);
-
+
bool success;
(success, packagePath) = await GetManifestPackageUpdate(_sdkFeatureBand, manifestId, includePreviews, offlineCache);
if (!success)
@@ -323,7 +322,7 @@ private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest,
_reporter.WriteLine(string.Format(LocalizableStrings.AdManifestPackageDoesNotExist, manifestId));
return;
}
-
+
await _workloadManifestInstaller.ExtractManifestAsync(packagePath, adManifestPath);
// add file that contains the advertisted manifest feature band so GetAdvertisingManifestVersionAndWorkloads will use correct feature band, regardless of if rollback occurred or not
@@ -384,7 +383,7 @@ private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest,
{
adManifestFeatureBand = new SdkFeatureBand(File.ReadAllText(adManifestFeatureBandPath));
}
-
+
return (new ManifestVersion(manifest.Version), adManifestFeatureBand, manifest.Workloads.Values.OfType().ToDictionary(w => w.Id));
}
@@ -470,12 +469,12 @@ private async Task NewerManifestPackageExists(ManifestId manifest)
ManifestVersion manifestVersion;
SdkFeatureBand manifestFeatureBand;
var parts = manifest.Value.Split('/');
-
+
string manifestVersionString = (parts[0]);
if (!FXVersion.TryParse(manifestVersionString, out FXVersion version))
{
throw new FormatException(String.Format(LocalizableStrings.InvalidVersionForWorkload, manifest.Key, manifestVersionString));
- }
+ }
manifestVersion = new ManifestVersion(parts[0]);
if (parts.Length == 1)
@@ -499,15 +498,15 @@ private bool BackgroundUpdatesAreDisabled() =>
private static string GetAdvertisingWorkloadsFilePath(string userProfileDir, SdkFeatureBand featureBand) => Path.Combine(userProfileDir, $".workloadAdvertisingUpdates{featureBand}");
- private async Task GetOnlinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, bool includePreviews)
- {
- string packagePath = await _nugetPackageDownloader.DownloadPackageAsync(
- _workloadManifestInstaller.GetManifestPackageId(manifestId, sdkFeatureBand),
- packageSourceLocation: _packageSourceLocation,
- includePreview: includePreviews);
-
- return packagePath;
- }
+ private async Task GetOnlinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, bool includePreviews)
+ {
+ string packagePath = await _nugetPackageDownloader.DownloadPackageAsync(
+ _workloadManifestInstaller.GetManifestPackageId(manifestId, sdkFeatureBand),
+ packageSourceLocation: _packageSourceLocation,
+ includePreview: includePreviews);
+
+ return packagePath;
+ }
private string GetOfflinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, DirectoryPath? offlineCache = null)
{
@@ -527,7 +526,7 @@ private string GetOfflinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId m
{
if (offlineCache == null || !offlineCache.HasValue)
{
- try
+ try
{
string packagePath = await GetOnlinePackagePath(sdkFeatureBand, manifestId, includePreviews);
return (true, packagePath);
@@ -557,9 +556,9 @@ private string GetOfflinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId m
}
}
-
-private string GetAdvertisingManifestPath(SdkFeatureBand featureBand, ManifestId manifestId) =>
- Path.Combine(_userProfileDir, "sdk-advertising", featureBand.ToString(), manifestId.ToString());
+
+ private string GetAdvertisingManifestPath(SdkFeatureBand featureBand, ManifestId manifestId) =>
+ Path.Combine(_userProfileDir, "sdk-advertising", featureBand.ToString(), manifestId.ToString());
}
}
diff --git a/src/Cli/dotnet/dotnet.csproj b/src/Cli/dotnet/dotnet.csproj
index 3c207d9c173d..849ad24e8c65 100644
--- a/src/Cli/dotnet/dotnet.csproj
+++ b/src/Cli/dotnet/dotnet.csproj
@@ -95,8 +95,7 @@
-
+
diff --git a/src/Common/CompileOptions.cs b/src/Common/CompileOptions.cs
index 2ba77d957d5e..2df7fecc6221 100644
--- a/src/Common/CompileOptions.cs
+++ b/src/Common/CompileOptions.cs
@@ -1,5 +1,10 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System.Reflection;
+
+#if MICROSOFT_ENABLE_TELEMETRY
+ [assembly: AssemblyMetadata("TelemetryOptOutDefault", Microsoft.DotNet.Cli.CompileOptions.TelemetryOptOutDefaultString)]
+#endif
namespace Microsoft.DotNet.Cli
{
@@ -10,6 +15,12 @@ static class CompileOptions
false;
#else
true;
+#endif
+ public const string TelemetryOptOutDefaultString =
+#if MICROSOFT_ENABLE_TELEMETRY
+ "False";
+#else
+ "True";
#endif
}
}
diff --git a/src/Common/PathUtilities.cs b/src/Common/PathUtilities.cs
new file mode 100644
index 000000000000..dad9351b1ede
--- /dev/null
+++ b/src/Common/PathUtilities.cs
@@ -0,0 +1,49 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.IO;
+using System.Runtime.InteropServices;
+
+namespace Microsoft.DotNet;
+
+static class PathUtilities
+{
+ const int S_IRUSR = 256;
+ const int S_IWUSR = 128;
+ const int S_IXUSR = 64;
+ const int S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR; // 700 (octal) Permissions
+
+ const int MAX_NUM_DIRECTORY_CREATE_RETRIES = 2;
+
+ public static string CreateTempSubdirectory()
+ {
+ return CreateTempSubdirectoryRetry(0);
+ }
+
+ [DllImport("libc", SetLastError = true)]
+ private static extern int mkdir(string pathname, int mode);
+ private static string CreateTempSubdirectoryRetry(int attemptNo)
+ {
+ string path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
+ {
+ int mkdirStatusCode = mkdir(path, S_IRWXU);
+ if (mkdirStatusCode != 0)
+ {
+ int errno = Marshal.GetLastWin32Error();
+ if (Directory.Exists(path) && attemptNo < MAX_NUM_DIRECTORY_CREATE_RETRIES)
+ {
+ return CreateTempSubdirectoryRetry(attemptNo + 1);
+ }
+ else
+ throw new IOException($"Failed to create a temporary subdirectory {path} with mkdir, error code: {errno}");
+ }
+ }
+ else
+ {
+ Directory.CreateDirectory(path);
+ }
+ return path;
+ }
+}
diff --git a/src/Layout/redist/trustedroots/codesignctl.pem b/src/Layout/redist/trustedroots/codesignctl.pem
index efa36732f919..a973427758b9 100644
--- a/src/Layout/redist/trustedroots/codesignctl.pem
+++ b/src/Layout/redist/trustedroots/codesignctl.pem
@@ -8384,6 +8384,22 @@ CwqQAjBdGuxRidRk3PnlHji9Wy7j5UTkOxh61/CVQI/y68/0+dBlokHysOZ8wTYs
j1453Tc=
-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV
+BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk
+LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv
+b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ
+BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg
+THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v
+IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv
+xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H
+Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
+A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB
+eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo
+jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ
++efcMQ==
+-----END CERTIFICATE-----
+
-----BEGIN CERTIFICATE-----
MIIFtjCCA56gAwIBAgIQFcKuKk2ZmmOM07oTGXYI9TANBgkqhkiG9w0BAQsFADB1
MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
@@ -8468,3 +8484,37 @@ Ts3Xroud2xb9BMaSvdSI5qmjqrv3ZDg7X8wM0DW+dBkDpsWqTKJhNoI+HfMrvJdd
20t4Oy31O+9gI+j17AsjNpWvmGa/U9N7uGlKKpZmacSUxvRfbqyYeIiABlyisu2i
-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFojCCA4qgAwIBAgIUf/Go+fQ66IduLcb/XkM9su4wpkMwDQYJKoZIhvcNAQEN
+BQAwaTELMAkGA1UEBhMCVVMxFjAUBgNVBAoMDUVudHJ1c3QsIEluYy4xQjBABgNV
+BAMMOUVudHJ1c3QgQ29kZSBTaWduaW5nIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkgLSBDU0JSMTAeFw0yMTA1MDcxMzI2MzZaFw00MDEyMzAxMzI2MzZaMGkx
+CzAJBgNVBAYTAlVTMRYwFAYDVQQKDA1FbnRydXN0LCBJbmMuMUIwQAYDVQQDDDlF
+bnRydXN0IENvZGUgU2lnbmluZyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+IC0gQ1NCUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCngY/3FEW2
+YkPy2K7TJV5IT1G/xX2fUBw10dZ+YSqUGW0nRqSmGl33VFFqgCLGqGZ1TVSDyV5o
+G6v2W2Swra0gvVTvRmttAudFrnX2joq5Mi6LuHccUk15iF+lOhjJUCyXJy2/2gB9
+Y3/vMuxGh2Pbmp/DWiE2e/mb1cqgbnIs/OHxnnBNCFYVb5Cr+0i6udfBgniFZS5/
+tcnA4hS3NxFBBuKK4Kj25X62eAUBw2DtTwdBLgoTSeOQm3/dvfqsv2RR0VybtPVc
+51z/O5uloBrXfQmywrf/bhy8yH3m6Sv8crMU6UpVEoScRCV1HfYq8E+lID1oJeth
+l3wP5bY9867DwRG8G47M4EcwXkIAhnHjWKwGymUfe5SmS1dnDH5erXhnW1XjXuvH
+2OxMbobL89z4n4eqclgSD32m+PhCOTs8LOQyTUmM4OEAwjignPqEPkHcblauxhpb
+9GdoBQHNG7+uh7ydU/Yu6LZr5JnexU+HWKjSZR7IH9Vybu5ZHFc7CXKd18q3kMbN
+e0WSkUIDTH0/yvKquMIOhvMQn0YupGaGaFpoGHApOBGAYGuKQ6NzbOOzazf/5p1n
+AZKG3y9I0ftQYNVc/iHTAUJj/u9wtBfAj6ju08FLXxLq/f0uDodEYOOp9MIYo+P9
+zgyEIg3zp3jak/PbOM+5LzPG/wc8Xr5F0wIDAQABo0IwQDAdBgNVHQ4EFgQUgrrW
+PZfOn89x6JI3r/2ztWk1V88wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AYYwDQYJKoZIhvcNAQENBQADggIBABLvOKGI4aGj1mXcR5zzvNzPrEuMPBq+K/T3
+0GcXaIZNcKjyzdAxAld9qQyKO1c5nvBu9yQiBWfRwZbBvtHw+FZnC96614ibjddr
+CHb1WJHZtcNAUxqk8YXNPwBOP06TO3i50gdSZAyGaW3oVGVWF+gAU4SK89v7s84L
+VWKPzUxJBjh/UsPzHNc99zPKMq3Bqa9v6xHL7qxRv7AmmnpOI7RK9mm0QmnWoI22
+jEdKOyA3t0EH7y8g2GYcaZeobDB8d0Nea74mmIMPOtbHcCoWRi0lVIZjZVdC9yNB
+6VBqB0POTrXpH2jY2NYJSqjosvyQZ5LkkCbzR/rWIPuJgOJEczn3ioYzC/iqqedN
+7Nxv1c8xTauOH5BA1nxcgg+uF1Jx6aznTTjtKth2eYetF6NMq7dCV78GrOXQTTDp
+VU/jRcrz4GohNI3HnxyjY0iS0pYHvqVHPsIqmTinjtohfFFt3Ms9B+mpvUnUXTVf
+W4wEUeqaWJC6G69oeLEWD5QpO4+bKo/JIPBxQkxcTasxjKvpfyZoaaClFg2BxNEF
+DMOHZuUHY6obTv+yB0FPpSJGUKxmAIdSbDyyO5yXoUa0W97PwmpZVQeMo6TRdzVn
+RgQv2Ti5Rq+6jhtyJgIvdlTvg8IvLHdwzHcQkqoDrcrM4E/pg0blszwZb3p5h7Y4
+mr1CzqRi
+-----END CERTIFICATE-----
+
diff --git a/src/Layout/redist/trustedroots/timestampctl.pem b/src/Layout/redist/trustedroots/timestampctl.pem
index 6e6aeb668d77..c04ccb8e7cb1 100644
--- a/src/Layout/redist/trustedroots/timestampctl.pem
+++ b/src/Layout/redist/trustedroots/timestampctl.pem
@@ -8739,6 +8739,22 @@ CwqQAjBdGuxRidRk3PnlHji9Wy7j5UTkOxh61/CVQI/y68/0+dBlokHysOZ8wTYs
j1453Tc=
-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV
+BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk
+LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv
+b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ
+BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg
+THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v
+IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv
+xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H
+Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G
+A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB
+eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo
+jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ
++efcMQ==
+-----END CERTIFICATE-----
+
-----BEGIN CERTIFICATE-----
MIIFtjCCA56gAwIBAgIQFcKuKk2ZmmOM07oTGXYI9TANBgkqhkiG9w0BAQsFADB1
MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl
@@ -8857,3 +8873,37 @@ zWIEm5dte7Q3toaZwmAK/G0ZBkI3ZLwXum1LzddoRhN51ltgmc6NzfDIG97qKL7M
z4leKEMaJ8dId29zULSpxVPgc8dUrdyac1E=
-----END CERTIFICATE-----
+-----BEGIN CERTIFICATE-----
+MIIFojCCA4qgAwIBAgIUf/Go+fQ66IduLcb/XkM9su4wpkMwDQYJKoZIhvcNAQEN
+BQAwaTELMAkGA1UEBhMCVVMxFjAUBgNVBAoMDUVudHJ1c3QsIEluYy4xQjBABgNV
+BAMMOUVudHJ1c3QgQ29kZSBTaWduaW5nIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRo
+b3JpdHkgLSBDU0JSMTAeFw0yMTA1MDcxMzI2MzZaFw00MDEyMzAxMzI2MzZaMGkx
+CzAJBgNVBAYTAlVTMRYwFAYDVQQKDA1FbnRydXN0LCBJbmMuMUIwQAYDVQQDDDlF
+bnRydXN0IENvZGUgU2lnbmluZyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5
+IC0gQ1NCUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCngY/3FEW2
+YkPy2K7TJV5IT1G/xX2fUBw10dZ+YSqUGW0nRqSmGl33VFFqgCLGqGZ1TVSDyV5o
+G6v2W2Swra0gvVTvRmttAudFrnX2joq5Mi6LuHccUk15iF+lOhjJUCyXJy2/2gB9
+Y3/vMuxGh2Pbmp/DWiE2e/mb1cqgbnIs/OHxnnBNCFYVb5Cr+0i6udfBgniFZS5/
+tcnA4hS3NxFBBuKK4Kj25X62eAUBw2DtTwdBLgoTSeOQm3/dvfqsv2RR0VybtPVc
+51z/O5uloBrXfQmywrf/bhy8yH3m6Sv8crMU6UpVEoScRCV1HfYq8E+lID1oJeth
+l3wP5bY9867DwRG8G47M4EcwXkIAhnHjWKwGymUfe5SmS1dnDH5erXhnW1XjXuvH
+2OxMbobL89z4n4eqclgSD32m+PhCOTs8LOQyTUmM4OEAwjignPqEPkHcblauxhpb
+9GdoBQHNG7+uh7ydU/Yu6LZr5JnexU+HWKjSZR7IH9Vybu5ZHFc7CXKd18q3kMbN
+e0WSkUIDTH0/yvKquMIOhvMQn0YupGaGaFpoGHApOBGAYGuKQ6NzbOOzazf/5p1n
+AZKG3y9I0ftQYNVc/iHTAUJj/u9wtBfAj6ju08FLXxLq/f0uDodEYOOp9MIYo+P9
+zgyEIg3zp3jak/PbOM+5LzPG/wc8Xr5F0wIDAQABo0IwQDAdBgNVHQ4EFgQUgrrW
+PZfOn89x6JI3r/2ztWk1V88wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC
+AYYwDQYJKoZIhvcNAQENBQADggIBABLvOKGI4aGj1mXcR5zzvNzPrEuMPBq+K/T3
+0GcXaIZNcKjyzdAxAld9qQyKO1c5nvBu9yQiBWfRwZbBvtHw+FZnC96614ibjddr
+CHb1WJHZtcNAUxqk8YXNPwBOP06TO3i50gdSZAyGaW3oVGVWF+gAU4SK89v7s84L
+VWKPzUxJBjh/UsPzHNc99zPKMq3Bqa9v6xHL7qxRv7AmmnpOI7RK9mm0QmnWoI22
+jEdKOyA3t0EH7y8g2GYcaZeobDB8d0Nea74mmIMPOtbHcCoWRi0lVIZjZVdC9yNB
+6VBqB0POTrXpH2jY2NYJSqjosvyQZ5LkkCbzR/rWIPuJgOJEczn3ioYzC/iqqedN
+7Nxv1c8xTauOH5BA1nxcgg+uF1Jx6aznTTjtKth2eYetF6NMq7dCV78GrOXQTTDp
+VU/jRcrz4GohNI3HnxyjY0iS0pYHvqVHPsIqmTinjtohfFFt3Ms9B+mpvUnUXTVf
+W4wEUeqaWJC6G69oeLEWD5QpO4+bKo/JIPBxQkxcTasxjKvpfyZoaaClFg2BxNEF
+DMOHZuUHY6obTv+yB0FPpSJGUKxmAIdSbDyyO5yXoUa0W97PwmpZVQeMo6TRdzVn
+RgQv2Ti5Rq+6jhtyJgIvdlTvg8IvLHdwzHcQkqoDrcrM4E/pg0blszwZb3p5h7Y4
+mr1CzqRi
+-----END CERTIFICATE-----
+
diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs
index 5686a4a25ba4..5cd254c1e71f 100644
--- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs
+++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs
@@ -14,6 +14,9 @@
// Work around https://github.com/dotnet/roslyn-analyzers/issues/6094
#pragma warning disable CA1420
+// Work around https://github.com/dotnet/roslyn-analyzers/issues/6094
+#pragma warning disable CA1420
+
namespace Microsoft.DotNet.NativeWrapper
{
public static partial class Interop
diff --git a/src/Tasks/Common/FileUtilities.cs b/src/Tasks/Common/FileUtilities.cs
index 92375d98eeba..76f79690f00b 100644
--- a/src/Tasks/Common/FileUtilities.cs
+++ b/src/Tasks/Common/FileUtilities.cs
@@ -37,6 +37,5 @@ public static Version TryGetAssemblyVersion(string sourcePath)
return s_assemblyExtensions.Contains(extension) ? GetAssemblyVersion(sourcePath) : null;
}
-
}
}
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageAssetsTask.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageAssetsTask.cs
index e0aaa508409c..c75c1fe46a67 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageAssetsTask.cs
+++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageAssetsTask.cs
@@ -4,7 +4,6 @@
using FluentAssertions;
using Microsoft.Build.Framework;
using System;
-using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -117,15 +116,11 @@ public void It_does_not_error_on_duplicate_package_names()
new CacheWriter(task); // Should not error
}
- [Fact]
- public void It_warns_on_invalid_culture_codes_of_resources()
- {
- string projectAssetsJsonPath = Path.GetTempFileName();
- var assetsContent = @"
+ private static string AssetsFileWithInvalidLocale(string tfm, string locale) => @"
{
`version`: 3,
`targets`: {
- `net7.0`: {
+ `{tfm}`: {
`JavaScriptEngineSwitcher.Core/3.3.0`: {
`type`: `package`,
`compile`: {
@@ -136,59 +131,65 @@ public void It_warns_on_invalid_culture_codes_of_resources()
},
`resource`: {
`lib/netstandard2.0/ru-ru/JavaScriptEngineSwitcher.Core.resources.dll`: {
- `locale`: `what is this even`
+ `locale`: `{locale}`
}
}
}
}
+ },
+ `project`: {
+ `version`: `1.0.0`,
+ `frameworks`: {
+ `{tfm}`: {
+ `targetAlias`: `{tfm}`
+ }
+ }
}
-}".Replace("`", "\"");
+}".Replace("`", "\"").Replace("{tfm}", tfm).Replace("{locale}", locale);
+
+ [InlineData("net7.0", true)]
+ [InlineData("net6.0", false)]
+ [Theory]
+ public void It_warns_on_invalid_culture_codes_of_resources(string tfm, bool shouldHaveWarnings)
+ {
+ string projectAssetsJsonPath = Path.GetTempFileName();
+ var assetsContent = AssetsFileWithInvalidLocale(tfm, "what is this even");
File.WriteAllText(projectAssetsJsonPath, assetsContent);
var task = InitializeTask(out _);
task.ProjectAssetsFile = projectAssetsJsonPath;
- task.TargetFramework = "net7.0";
- var writer = new CacheWriter(task);
- writer.WriteToCacheFile();
- var messages = (task.BuildEngine as MockBuildEngine).Warnings;
- var invalidContextMessages = messages.Where(msg => msg.Code == "NETSDK1188");
- invalidContextMessages.Should().HaveCount(1);
+ task.TargetFramework = tfm;
+ var writer = new CacheWriter(task, new MockPackageResolver());
+ writer.WriteToMemoryStream();
+ var engine = task.BuildEngine as MockBuildEngine;
+
+ var invalidContextWarnings = engine.Warnings.Where(msg => msg.Code == "NETSDK1188");
+ invalidContextWarnings.Should().HaveCount(shouldHaveWarnings ? 1 : 0);
+
+ var invalidContextMessages = engine.Messages.Where(msg => msg.Code == "NETSDK1188");
+ invalidContextMessages.Should().HaveCount(shouldHaveWarnings ? 0 : 1);
+
}
-
- [Fact]
- public void It_warns_on_incorrectly_cased_culture_codes_of_resources()
+
+ [InlineData("net7.0", true)]
+ [InlineData("net6.0", false)]
+ [Theory]
+ public void It_warns_on_incorrectly_cased_culture_codes_of_resources(string tfm, bool shouldHaveWarnings)
{
string projectAssetsJsonPath = Path.GetTempFileName();
- var assetsContent = @"
-{
- `version`: 3,
- `targets`: {
- `net7.0`: {
- `JavaScriptEngineSwitcher.Core/3.3.0`: {
- `type`: `package`,
- `compile`: {
- `lib/netstandard2.0/JavaScriptEngineSwitcher.Core.dll`: {}
- },
- `runtime`: {
- `lib/netstandard2.0/JavaScriptEngineSwitcher.Core.dll`: {}
- },
- `resource`: {
- `lib/netstandard2.0/ru-ru/JavaScriptEngineSwitcher.Core.resources.dll`: {
- `locale`: `ru-ru`
- }
- }
- }
- }
- }
-}".Replace("`", "\"");
+ var assetsContent = AssetsFileWithInvalidLocale(tfm, "ru-ru");
File.WriteAllText(projectAssetsJsonPath, assetsContent);
var task = InitializeTask(out _);
task.ProjectAssetsFile = projectAssetsJsonPath;
- task.TargetFramework = "net7.0";
- var writer = new CacheWriter(task);
- writer.WriteToCacheFile();
- var messages = (task.BuildEngine as MockBuildEngine).Warnings;
- var invalidContextMessages = messages.Where(msg => msg.Code == "NETSDK1187");
- invalidContextMessages.Should().HaveCount(1);
+ task.TargetFramework = tfm;
+ var writer = new CacheWriter(task, new MockPackageResolver());
+ writer.WriteToMemoryStream();
+ var engine = task.BuildEngine as MockBuildEngine;
+
+ var invalidContextWarnings = engine.Warnings.Where(msg => msg.Code == "NETSDK1187");
+ invalidContextWarnings.Should().HaveCount(shouldHaveWarnings ? 1 : 0);
+
+ var invalidContextMessages = engine.Messages.Where(msg => msg.Code == "NETSDK1187");
+ invalidContextMessages.Should().HaveCount(shouldHaveWarnings ? 0 : 1);
}
private ResolvePackageAssets InitializeTask(out IEnumerable inputProperties)
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Mocks/MockPackageResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Mocks/MockPackageResolver.cs
index 85ccb9da17c2..1945185459ba 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Mocks/MockPackageResolver.cs
+++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Mocks/MockPackageResolver.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using NuGet.ProjectModel;
using NuGet.Versioning;
using System.IO;
@@ -24,5 +25,7 @@ public string GetPackageDirectory(string packageId, NuGetVersion version, out st
packageRoot = _root;
return Path.Combine(_root, packageId, version.ToNormalizedString(), "path");
}
+
+ public string ResolvePackageAssetPath(LockFileTargetLibrary package, string relativePath) => Path.Combine(GetPackageDirectory(package.Name, package.Version), relativePath);
}
-}
\ No newline at end of file
+}
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/IPackageResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks/IPackageResolver.cs
index 1540f00e5089..e208ba1bcfca 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks/IPackageResolver.cs
+++ b/src/Tasks/Microsoft.NET.Build.Tasks/IPackageResolver.cs
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using NuGet.ProjectModel;
using NuGet.Versioning;
namespace Microsoft.NET.Build.Tasks
@@ -9,5 +10,6 @@ internal interface IPackageResolver
{
string GetPackageDirectory(string packageId, NuGetVersion version);
string GetPackageDirectory(string packageId, NuGetVersion version, out string packageRoot);
+ string ResolvePackageAssetPath(LockFileTargetLibrary package, string relativePath);
}
}
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs
index 4ee121675e0c..48ee5d280c58 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs
+++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs
@@ -657,8 +657,8 @@ internal sealed class CacheWriter : IDisposable
private ResolvePackageAssets _task;
private BinaryWriter _writer;
private LockFile _lockFile;
- private NuGetPackageResolver _packageResolver;
private LockFileTarget _compileTimeTarget;
+ private IPackageResolver _packageResolver;
private LockFileTarget _runtimeTarget;
private Dictionary _stringTable;
private List _metadataStrings;
@@ -677,6 +677,15 @@ internal sealed class CacheWriter : IDisposable
private const char RelatedPropertySeparator = ';';
+ ///
+ /// This constructor should only be used for testing - IPackgeResolver carries a lot of
+ /// state so using mocks really help with testing this component.
+ ///
+ public CacheWriter(ResolvePackageAssets task, IPackageResolver resolver) : this(task)
+ {
+ _packageResolver = resolver;
+ }
+
public CacheWriter(ResolvePackageAssets task)
{
_targetFramework = task.TargetFramework;
@@ -1425,14 +1434,30 @@ private void WriteResourceAssemblies()
var normalizedLocale = System.Globalization.CultureInfo.GetCultureInfo(locale).Name;
if (normalizedLocale != locale)
{
- _task.Log.LogWarning(Strings.PackageContainsIncorrectlyCasedLocale, package.Name, package.Version.ToNormalizedString(), locale, normalizedLocale);
+ var tfm = _lockFile.GetTargetAndThrowIfNotFound(_targetFramework, null).TargetFramework;
+ if (tfm.Version.Major >= 7)
+ {
+ _task.Log.LogWarning(Strings.PackageContainsIncorrectlyCasedLocale, package.Name, package.Version.ToNormalizedString(), locale, normalizedLocale);
+ }
+ else
+ {
+ _task.Log.LogMessage(Strings.PackageContainsIncorrectlyCasedLocale, package.Name, package.Version.ToNormalizedString(), locale, normalizedLocale);
+ }
}
locale = normalizedLocale;
}
catch (System.Globalization.CultureNotFoundException cnf)
{
- _task.Log.LogWarning(Strings.PackageContainsUnknownLocale, package.Name, package.Version.ToNormalizedString(), cnf.InvalidCultureName);
- // We could potentially strip this unknown locales at this point, but we do not.
+ var tfm = _lockFile.GetTargetAndThrowIfNotFound(_targetFramework, null).TargetFramework;
+ if (tfm.Version.Major >= 7)
+ {
+ _task.Log.LogWarning(Strings.PackageContainsUnknownLocale, package.Name, package.Version.ToNormalizedString(), cnf.InvalidCultureName);
+ } else
+ {
+ _task.Log.LogMessage(Strings.PackageContainsUnknownLocale, package.Name, package.Version.ToNormalizedString(), cnf.InvalidCultureName);
+ }
+
+ // We could potentially strip this unknown locale at this point, but we do not.
// Locale data can change over time (it's typically an OS database that's kept updated),
// and the data on the system running the build may not be the same data as
// the system executing the built code. So we should be permissive for this case.
diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.EolTargetFrameworks.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.EolTargetFrameworks.targets
index 38e6b8697121..c9c840088206 100644
--- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.EolTargetFrameworks.targets
+++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.EolTargetFrameworks.targets
@@ -20,7 +20,7 @@ Copyright (c) .NET Foundation. All rights reserved.
receive servicing updates and security fixes.
-->
- <_EolNetCoreTargetFrameworkVersions Include="1.0;1.1;2.0;2.1;2.2;3.0" />
+ <_EolNetCoreTargetFrameworkVersions Include="1.0;1.1;2.0;2.1;2.2;3.0;5.0" />
int:[T:CompatTests.FooAttribute]"),
CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F->int:[T:CompatTests.BarAttribute]"),
- CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F->int:[T:CompatTests.BazAttribute]")
}
},
// Attributes on method parameter
@@ -620,7 +653,6 @@ public void F([Baz] int v, [Foo(""T"")] string s) {}
",
new CompatDifference[] {
CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F(System.Int32,System.String)$0:[T:CompatTests.BarAttribute]"),
- CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F(System.Int32,System.String)$0:[T:CompatTests.BazAttribute]"),
CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F(System.Int32,System.String)$1:[T:CompatTests.FooAttribute]"),
}
@@ -671,7 +703,6 @@ public class First<[Baz] T1, [Foo(""T"")] T2> {}
",
new CompatDifference[] {
CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "T:CompatTests.First`2<0>:[T:CompatTests.BarAttribute]"),
- CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "T:CompatTests.First`2<0>:[T:CompatTests.BazAttribute]"),
CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "T:CompatTests.First`2<1>:[T:CompatTests.FooAttribute]"),
}
@@ -728,25 +759,594 @@ public class First {
",
new CompatDifference[] {
CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F``2<0>:[T:CompatTests.BarAttribute]"),
- CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F``2<0>:[T:CompatTests.BazAttribute]"),
CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F``2<1>:[T:CompatTests.FooAttribute]"),
}
}
};
- [Theory]
- [MemberData(nameof(TypesCases))]
- [MemberData(nameof(MembersCases))]
- public void EnsureDiagnosticIsReported(string leftSyntax, string rightSyntax, CompatDifference[] expected)
+ public static TheoryData StrictMode => new()
{
- using TempDirectory root = new();
- string filePath = Path.Combine(root.DirPath, "exclusions.txt");
- File.Create(filePath).Dispose();
- TestRuleFactory s_ruleFactory = new((settings, context) => new AttributesMustMatch(settings, context, new[] { filePath }));
- IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax);
- IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax);
- ApiComparer differ = new(s_ruleFactory);
+ // Attribute added to type
+ {
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A;
+ public int B;
+ }
+
+ [Foo(""S"", A = true, B = 3)]
+ public class First {}
+}
+",
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A;
+ public int B;
+ }
+
+ [Serializable]
+ [Foo(""S"", A = true, B = 3)]
+ public class First {}
+}
+",
+new CompatDifference[] {
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "T:CompatTests.First:[T:System.SerializableAttribute]")
+}
+ },
+ // Attribute repeated with additional arguments
+ {
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A;
+ public int B;
+ }
+
+ [Serializable]
+ [Foo(""S"")]
+ public class First {}
+}
+",
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A;
+ public int B;
+ }
+
+ [Serializable]
+ [Foo(""S"")]
+ [Foo(""T"")]
+ public class First {}
+}
+",
+new CompatDifference[] {
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "T:CompatTests.First:[T:CompatTests.FooAttribute]")
+}
+ },
+ // Attributes on method
+ {
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A;
+ public int B;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ [Foo(""S"", A = true, B = 3)]
+ [Bar]
+ public void F() {}
+ }
+}
+",
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A = false;
+ public int B = 0;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ [Foo(""T"")]
+ [Baz]
+ public void F() {}
+ }
+}
+",
+new CompatDifference[] {
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F:[T:CompatTests.FooAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F:[T:CompatTests.BarAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F:[T:CompatTests.BazAttribute]")
+}
+ },
+ // Attributes on property
+ {
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A;
+ public int B;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ [Foo(""S"", A = true, B = 3)]
+ [Bar]
+ public int F { get; }
+ }
+}
+",
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A = false;
+ public int B = 0;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ [Foo(""T"")]
+ [Baz]
+ public int F { get; }
+ }
+}
+",
+new CompatDifference[] {
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "P:CompatTests.First.F:[T:CompatTests.FooAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "P:CompatTests.First.F:[T:CompatTests.BarAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "P:CompatTests.First.F:[T:CompatTests.BazAttribute]")
+}
+ },
+ // Attributes on event
+ {
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A;
+ public int B;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ public delegate void EventHandler(object sender, object e);
+
+ [Foo(""S"", A = true, B = 3)]
+ [Bar]
+ public event EventHandler F;
+ }
+}
+",
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A = false;
+ public int B = 0;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ public delegate void EventHandler(object sender, object e);
+
+ [Foo(""T"")]
+ [Baz]
+ public event EventHandler F;
+ }
+}
+",
+new CompatDifference[] {
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "E:CompatTests.First.F:[T:CompatTests.FooAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "E:CompatTests.First.F:[T:CompatTests.BarAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "E:CompatTests.First.F:[T:CompatTests.BazAttribute]")
+}
+ },
+ // Attributes on constructor
+ {
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A;
+ public int B;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ [Foo(""S"", A = true, B = 3)]
+ [Bar]
+ public First() {}
+ }
+}
+",
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A = false;
+ public int B = 0;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ [Foo(""T"")]
+ [Baz]
+ public First() {}
+ }
+}
+",
+new CompatDifference[] {
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.#ctor:[T:CompatTests.FooAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.#ctor:[T:CompatTests.BarAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.#ctor:[T:CompatTests.BazAttribute]")
+}
+ },
+ // Attributes on return type
+ {
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A;
+ public int B;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ [return: Foo(""S"", A = true, B = 3)]
+ [return: Bar]
+ public int F() => 0;
+ }
+}
+",
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A = false;
+ public int B = 0;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ [return: Foo(""T"")]
+ [return: Baz]
+ public int F() => 0;
+ }
+}
+",
+new CompatDifference[] {
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F->int:[T:CompatTests.FooAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F->int:[T:CompatTests.BarAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F->int:[T:CompatTests.BazAttribute]")
+}
+ },
+ // Attributes on method parameter
+ {
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A = false;
+ public int B = 0;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ public void F([Bar] int v, [Foo(""S"", A = true, B = 0)] string s) {}
+ }
+}
+",
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A = false;
+ public int B = 0;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ public void F([Baz] int v, [Foo(""T"")] string s) {}
+ }
+}
+",
+new CompatDifference[] {
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F(System.Int32,System.String)$0:[T:CompatTests.BarAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F(System.Int32,System.String)$0:[T:CompatTests.BazAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F(System.Int32,System.String)$1:[T:CompatTests.FooAttribute]"),
+
+}
+ },
+ // Attributes on type parameter of class
+ {
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A = false;
+ public int B = 0;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First<[Bar] T1, [Foo(""S"", A = true, B = 0)] T2> {}
+}
+",
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A = false;
+ public int B = 0;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First<[Baz] T1, [Foo(""T"")] T2> {}
+}
+",
+new CompatDifference[] {
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "T:CompatTests.First`2<0>:[T:CompatTests.BarAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "T:CompatTests.First`2<0>:[T:CompatTests.BazAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "T:CompatTests.First`2<1>:[T:CompatTests.FooAttribute]"),
+
+}
+ },
+ // Attributes on type parameter of method
+ {
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A = false;
+ public int B = 0;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ public void F<[Bar] T1, [Foo(""S"", A = true, B = 0)] T2>() {}
+ }
+}
+",
+ @"
+namespace CompatTests
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class FooAttribute : Attribute {
+ public FooAttribute(String s) {}
+ public bool A = false;
+ public int B = 0;
+ }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BarAttribute : Attribute { }
+
+ [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
+ public class BazAttribute : Attribute { }
+
+ public class First {
+
+ public void F<[Baz] T1, [Foo(""T"")] T2>() {}
+ }
+}
+",
+new CompatDifference[] {
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F``2<0>:[T:CompatTests.BarAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F``2<0>:[T:CompatTests.BazAttribute]"),
+ CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F``2<1>:[T:CompatTests.FooAttribute]"),
+
+}
+ }
+ };
+
+ [Theory]
+ [MemberData(nameof(TypesCases))]
+ [MemberData(nameof(MembersCases))]
+ public void EnsureDiagnosticIsReported(string leftSyntax, string rightSyntax, CompatDifference[] expected)
+ {
+ using TempDirectory root = new();
+ string filePath = Path.Combine(root.DirPath, "exclusions.txt");
+ File.Create(filePath).Dispose();
+ TestRuleFactory s_ruleFactory = new((settings, context) => new AttributesMustMatch(settings, context, new[] { filePath }));
+ IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax);
+ IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax);
+ ApiComparer differ = new(s_ruleFactory);
+
+ IEnumerable actual = differ.GetDifferences(left, right);
+
+ Assert.Equal(expected, actual);
+ }
+
+ [Theory]
+ [MemberData(nameof(StrictMode))]
+ public void EnsureStrictModeReported(string leftSyntax, string rightSyntax, CompatDifference[] expected)
+ {
+ using TempDirectory root = new();
+ string filePath = Path.Combine(root.DirPath, "exclusions.txt");
+ File.Create(filePath).Dispose();
+ TestRuleFactory s_ruleFactory = new((settings, context) => new AttributesMustMatch(settings, context, new[] { filePath }));
+ IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax);
+ IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax);
+ ApiComparer differ = new(s_ruleFactory, new ApiComparerSettings(strictMode: true));
IEnumerable actual = differ.GetDifferences(left, right);
diff --git a/src/Tests/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs b/src/Tests/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs
index 87d9440ed56d..9fe768c6270e 100644
--- a/src/Tests/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs
+++ b/src/Tests/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs
@@ -72,6 +72,13 @@ public static IEnumerable