From 38dac3bd5050888fb4bee62de9054471c332398b Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" Date: Mon, 25 Nov 2024 13:41:19 +0000 Subject: [PATCH 1/3] Update dependencies from https://github.com/nuget/nuget.client build 6.13.0.71 NuGet.Build.Tasks From Version 6.13.0-preview.1.62 -> To Version 6.13.0-preview.1.71 --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 7acb497a028..e20311bcacf 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -85,9 +85,9 @@ https://github.com/dotnet/arcade 1c7e09a8d9c9c9b15ba574cd6a496553505559de - + https://github.com/nuget/nuget.client - ce95a567627472f8abd9d155047392e22142ff72 + c0d3837b40a353b5178cd02953db2924aacb8712 https://github.com/dotnet/roslyn diff --git a/eng/Versions.props b/eng/Versions.props index 0d6f97b9da1..835a8b5f704 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -52,7 +52,7 @@ 4.2.0-1.22102.8 9.0.0-beta.24562.13 4.13.0-2.24561.1 - 6.13.0-preview.1.62 + 6.13.0-preview.1.71 9.0.200-preview.0.24523.19 From 755781254b7ffaaa2706ec46b17d5e732d5c07e6 Mon Sep 17 00:00:00 2001 From: dotnet bot Date: Wed, 27 Nov 2024 00:20:45 -0800 Subject: [PATCH 2/3] Localized file check-in by OneLocBuild Task: Build definition ID 9434: Build ID 10620354 (#11040) --- src/Build/Resources/xlf/Strings.cs.xlf | 4 ++-- src/Build/Resources/xlf/Strings.de.xlf | 4 ++-- src/Build/Resources/xlf/Strings.es.xlf | 4 ++-- src/Build/Resources/xlf/Strings.fr.xlf | 4 ++-- src/Build/Resources/xlf/Strings.it.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ja.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ko.xlf | 4 ++-- src/Build/Resources/xlf/Strings.pl.xlf | 4 ++-- src/Build/Resources/xlf/Strings.pt-BR.xlf | 4 ++-- src/Build/Resources/xlf/Strings.ru.xlf | 4 ++-- src/Build/Resources/xlf/Strings.tr.xlf | 4 ++-- src/Build/Resources/xlf/Strings.zh-Hans.xlf | 4 ++-- src/Build/Resources/xlf/Strings.zh-Hant.xlf | 4 ++-- 13 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/Build/Resources/xlf/Strings.cs.xlf b/src/Build/Resources/xlf/Strings.cs.xlf index 7bb0ca3b544..ff12e0468af 100644 --- a/src/Build/Resources/xlf/Strings.cs.xlf +++ b/src/Build/Resources/xlf/Strings.cs.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + Projekt {0} odkazuje na výstup projektu {1}. Odkazovaná cesta: {2}. Místo toho by se měla použít položka ProjectReference. A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + Na projekt by se nemělo odkazovat přes Reference na jeho výstup, ale přímo přes ProjectReference. diff --git a/src/Build/Resources/xlf/Strings.de.xlf b/src/Build/Resources/xlf/Strings.de.xlf index 608c6dbff77..47fa3acb9b5 100644 --- a/src/Build/Resources/xlf/Strings.de.xlf +++ b/src/Build/Resources/xlf/Strings.de.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + Das Projekt "{0}" verweist auf die Ausgabe eines Projekts "{1}". Pfad, auf den verwiesen wird: {2}. Stattdessen sollte ProjectReference verwendet werden. A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + Auf ein Projekt darf nicht über "Reference" auf seine Ausgabe verwiesen werden, sondern direkt über "ProjectReference". diff --git a/src/Build/Resources/xlf/Strings.es.xlf b/src/Build/Resources/xlf/Strings.es.xlf index 78621bb768b..b2cf227eb21 100644 --- a/src/Build/Resources/xlf/Strings.es.xlf +++ b/src/Build/Resources/xlf/Strings.es.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + El proyecto {0} hace referencia a la salida de un proyecto {1}. Ruta de acceso a la que se hace referencia: {2}. En su lugar, se debe usar ProjectReference. A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + No se debe hacer referencia a un proyecto a través de "Reference" a su salida, sino directamente a través de "ProjectReference". diff --git a/src/Build/Resources/xlf/Strings.fr.xlf b/src/Build/Resources/xlf/Strings.fr.xlf index 3c8975404d5..80df3b188af 100644 --- a/src/Build/Resources/xlf/Strings.fr.xlf +++ b/src/Build/Resources/xlf/Strings.fr.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + Project {0} référence la sortie d’un projet {1}. Chemin d’accès référencé : {2}. ProjectReference doit être utilisé à la place. A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + Un projet ne doit pas être référencé via « Reference » à sa sortie, mais directement via « ProjectReference ». diff --git a/src/Build/Resources/xlf/Strings.it.xlf b/src/Build/Resources/xlf/Strings.it.xlf index 0a7afe4e9a8..7c1c3bf7557 100644 --- a/src/Build/Resources/xlf/Strings.it.xlf +++ b/src/Build/Resources/xlf/Strings.it.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + Il progetto {0} fa riferimento all'output di un progetto {1}. Percorso di riferimento: {2}. Usare invece ProjectReference. A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + Un progetto non può fare riferimento al relativo output tramite 'Reference', ma direttamente tramite 'ProjectReference'. diff --git a/src/Build/Resources/xlf/Strings.ja.xlf b/src/Build/Resources/xlf/Strings.ja.xlf index addb4c4369d..c2c57270135 100644 --- a/src/Build/Resources/xlf/Strings.ja.xlf +++ b/src/Build/Resources/xlf/Strings.ja.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + プロジェクト {0} は、プロジェクト {1} の出力を参照します。参照パス: {2}。代わりに ProjectReference を使用する必要があります。 A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + プロジェクトは、出力に対する 'Reference' を介して参照するのではなく、'ProjectReference' を介して直接参照する必要があります。 diff --git a/src/Build/Resources/xlf/Strings.ko.xlf b/src/Build/Resources/xlf/Strings.ko.xlf index cc5d4fc3d82..2649c525183 100644 --- a/src/Build/Resources/xlf/Strings.ko.xlf +++ b/src/Build/Resources/xlf/Strings.ko.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + {0} 프로젝트가 {1} 프로젝트의 출력을 참조합니다. 참조된 경로: {2}. ProjectReference를 대신 사용해야 합니다. A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + 프로젝트는 ''Reference''를 통해 출력을 참조하지 말고 ''ProjectReference''를 통해 직접 참조해야 합니다. diff --git a/src/Build/Resources/xlf/Strings.pl.xlf b/src/Build/Resources/xlf/Strings.pl.xlf index 15e7e3b1418..746b03ca198 100644 --- a/src/Build/Resources/xlf/Strings.pl.xlf +++ b/src/Build/Resources/xlf/Strings.pl.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + Projekt {0} odwołuje się do danych wyjściowych projektu {1}. Ścieżka, do której istnieje odwołanie: {2}. Zamiast tego należy użyć elementu ProjectReference. A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + Do projektu nie należy odwoływać się poprzez opcję „Odwołanie” do jego danych wyjściowych, ale raczej bezpośrednio poprzez opcję „Odwołanie do projektu”. diff --git a/src/Build/Resources/xlf/Strings.pt-BR.xlf b/src/Build/Resources/xlf/Strings.pt-BR.xlf index 9361e4b3f3b..2173625f9c1 100644 --- a/src/Build/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Build/Resources/xlf/Strings.pt-BR.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + O projeto {0} faz referência à saída de um projeto {1}. Caminho referenciado: {2}. Em vez disso, ProjectReference deve ser usado. A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + Um projeto não deve ser referenciado por meio de "Referência" à sua saída, mas diretamente por meio de "ProjectReference". diff --git a/src/Build/Resources/xlf/Strings.ru.xlf b/src/Build/Resources/xlf/Strings.ru.xlf index 8b581ae95a0..dd8154f048a 100644 --- a/src/Build/Resources/xlf/Strings.ru.xlf +++ b/src/Build/Resources/xlf/Strings.ru.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + Проект {0} ссылается на выходные данные проекта {1}. Указанный по ссылке путь: {2}. Вместо этого следует использовать ProjectReference. A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + На проект следует ссылаться не с помощью параметра "Reference" для его выходных данных, а непосредственно посредством "ProjectReference". diff --git a/src/Build/Resources/xlf/Strings.tr.xlf b/src/Build/Resources/xlf/Strings.tr.xlf index 08edf588636..497a5fd2043 100644 --- a/src/Build/Resources/xlf/Strings.tr.xlf +++ b/src/Build/Resources/xlf/Strings.tr.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + {0} projesi bir {1} projesinin çıkışına referans başvuru yapar. Başvurulan yol:{2}. Bunun yerine ProjectReference kullanılmalıdır. A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + Bir projeye, çıkışına 'Reference' aracılığıyla değil doğrudan 'ProjectReference' aracılığıyla başvurulmalıdır. diff --git a/src/Build/Resources/xlf/Strings.zh-Hans.xlf b/src/Build/Resources/xlf/Strings.zh-Hans.xlf index c26a7049d4f..7857453e19a 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hans.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + 项目 {0} 引用项目 {1} 的输出。引用的路径: {2}。应转而使用 ProjectReference。 A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + 不得通过 "Reference" 引用项目的输出来引用项目,而是应通过 "ProjectReference" 直接引用。 diff --git a/src/Build/Resources/xlf/Strings.zh-Hant.xlf b/src/Build/Resources/xlf/Strings.zh-Hant.xlf index f007b349550..6e819e34aee 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hant.xlf @@ -173,12 +173,12 @@ Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. - Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + 專案 {0} 會參照專案 {1} 的輸出。參照的路徑: {2}。應該改用 ProjectReference。 A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. - A project should not be referenced via 'Reference' to its output, but rather directly via 'ProjectReference'. + 專案不應透過 'Reference' 來參照其輸出,而應直接透過 'ProjectReference'。 From bc2ad7fdf30ec404c4bc11cdc6d1002d988ee4a1 Mon Sep 17 00:00:00 2001 From: Jan Krivanek Date: Wed, 27 Nov 2024 09:44:28 +0100 Subject: [PATCH 3/3] EmbeddedResource Culture Check (#11023) * in progress work * Initial version of the Check * Add check documentation * Fix analyzers * Make the check report same extension as the AssighCulture task would * Reflect PR comments --- documentation/specs/BuildCheck/Codes.md | 26 +++- .../Checks/EmbeddedResourceCheck.cs | 115 +++++++++++++++++ .../BuildCheckManagerProvider.cs | 3 +- src/Build/Resources/Strings.resx | 8 ++ src/Build/Resources/xlf/Strings.cs.xlf | 10 ++ src/Build/Resources/xlf/Strings.de.xlf | 10 ++ src/Build/Resources/xlf/Strings.es.xlf | 10 ++ src/Build/Resources/xlf/Strings.fr.xlf | 10 ++ src/Build/Resources/xlf/Strings.it.xlf | 10 ++ src/Build/Resources/xlf/Strings.ja.xlf | 10 ++ src/Build/Resources/xlf/Strings.ko.xlf | 10 ++ src/Build/Resources/xlf/Strings.pl.xlf | 10 ++ src/Build/Resources/xlf/Strings.pt-BR.xlf | 10 ++ src/Build/Resources/xlf/Strings.ru.xlf | 10 ++ src/Build/Resources/xlf/Strings.tr.xlf | 10 ++ src/Build/Resources/xlf/Strings.zh-Hans.xlf | 10 ++ src/Build/Resources/xlf/Strings.zh-Hant.xlf | 10 ++ src/BuildCheck.UnitTests/EndToEndTests.cs | 108 +++++++++++++++- ...icrosoft.Build.BuildCheck.UnitTests.csproj | 4 + .../EntryProject/EntryProject.csproj | 13 ++ .../ReferencedProject.csproj | 31 +++++ .../ReferencedProject/Resource1.en.resx | 120 ++++++++++++++++++ .../ReferencedProject/Resource1.resx | 120 ++++++++++++++++++ src/Shared/StringExtensions.cs | 17 +++ ...Microsoft.Build.Utilities.UnitTests.csproj | 1 + 25 files changed, 691 insertions(+), 5 deletions(-) create mode 100644 src/Build/BuildCheck/Checks/EmbeddedResourceCheck.cs create mode 100644 src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj create mode 100644 src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj create mode 100644 src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/Resource1.en.resx create mode 100644 src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/Resource1.resx diff --git a/documentation/specs/BuildCheck/Codes.md b/documentation/specs/BuildCheck/Codes.md index 58b25712ae3..d2a518cedf6 100644 --- a/documentation/specs/BuildCheck/Codes.md +++ b/documentation/specs/BuildCheck/Codes.md @@ -4,15 +4,18 @@ Report codes are chosen to conform to suggested guidelines. Those guidelines are | Diagnostic Code | Default Severity | Default Scope | Available from SDK | Reason | |:-----|-------|-------|-------|----------| -| [BC0101](#bc0101---shared-output-path) | Warning | Project | 9.0.100 | Shared output path. | -| [BC0102](#bc0102---double-writes) | Warning | Project | 9.0.100 | Double writes. | +| [BC0101](#bc0101---shared-output-path) | Warning | N/A | 9.0.100 | Shared output path. | +| [BC0102](#bc0102---double-writes) | Warning | N/A | 9.0.100 | Double writes. | | [BC0103](#bc0103---used-environment-variable) | Suggestion | Project | 9.0.100 | Used environment variable. | -| [BC0104](#bc0104---projectreference-is-preferred-to-reference) | Warning | Project | 9.0.200 | ProjectReference is preferred to Reference. | +| [BC0104](#bc0104---projectreference-is-preferred-to-reference) | Warning | N/A | 9.0.200 | ProjectReference is preferred to Reference. | +| [BC0105](#bc0105---embeddedresource-should-specify-culture-metadata) | Warning | N/A | 9.0.200 | Culture specific EmbeddedResource should specify Culture metadata. | | [BC0201](#bc0201---usage-of-undefined-property) | Warning | Project | 9.0.100 | Usage of undefined property. | | [BC0202](#bc0202---property-first-declared-after-it-was-used) | Warning | Project | 9.0.100 | Property first declared after it was used. | | [BC0203](#bc0203----property-declared-but-never-used) | Suggestion | Project | 9.0.100 | Property declared but never used. | +Note: What does the 'N/A' scope mean? The scope of checks are only applicable and configurable in cases where evaluation-time data are being used and the source of the data is determinable and available. Otherwise the scope of whole build is always checked. + To enable verbose logging in order to troubleshoot issue(s), enable [binary logging](https://github.com/dotnet/msbuild/blob/main/documentation/wiki/Binary-Log.md#msbuild-binary-log-overview) _Cmd:_ @@ -58,6 +61,23 @@ It is not recommended to reference project outputs. Such practice leads to losin If you need to achieve more advanced dependency behavior - check [Controlling Dependencies Behavior](https://github.com/dotnet/msbuild/blob/main/documentation/wiki/Controlling-Dependencies-Behavior.md) document. If neither suits your needs - then you might need to disable this check for your build or for particular projects. + +## BC0105 - EmbeddedResource should specify Culture metadata. + +"It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation." + +[`EmbeddedResource` item](https://learn.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-items#embeddedresource) has a `Culture` and `WithCulture` metadata that are strongly recommended to be used - to prevent MSBuild to need to 'guess' the culture from the file extension - which may be dependent on the current OS/Runtime available cultures and hence it can lead to nondeterministic build. + +Examples: + * `` This indicates the culture to the MSBuild engine and the culture will be respected. No diagnostic (warning) is issued ([see below for exceptions](#RespectAlreadyAssignedItemCulture)). + * `` This indicates to the MSBuild engine that the file is culture neutral and the extension should not be treated as culture indicator. No diagnostic (warning) is issued. + * `` MSBuild infers the culture from the extra extension ('xyz') and if it is known to [`System.Globalization.CultureInfo`](https://learn.microsoft.com/en-us/dotnet/api/system.globalization.cultureinfo) it is being used as the resource culture. The `BC0105` diagnostic is emitted (if BuildCheck is enabled and BC0105 is not disabled) + * `` MSBuild infers that the resource is culture neutral. No diagnostic (warning) is issued. + + +**Note:** In Full Framework version of MSBuild (msbuild.exe, Visual Studio) and in .NET SDK prior 9.0 a global or project specific property `RespectAlreadyAssignedItemCulture` needs to be set to `'true'` in order for the explicit `Culture` metadata to be respected. Otherwise the explicit culture will be overwritten by MSBuild engine and if different from the extension - a `MSB3002` warning is emitted (`"MSB3002: Explicitly set culture "{0}" for item "{1}" was overwritten with inferred culture "{2}", because 'RespectAlreadyAssignedItemCulture' property was not set."`) + + ## BC0201 - Usage of undefined property. diff --git a/src/Build/BuildCheck/Checks/EmbeddedResourceCheck.cs b/src/Build/BuildCheck/Checks/EmbeddedResourceCheck.cs new file mode 100644 index 00000000000..73c7e6af9ed --- /dev/null +++ b/src/Build/BuildCheck/Checks/EmbeddedResourceCheck.cs @@ -0,0 +1,115 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.IO; +using System.Collections.Generic; +using Microsoft.Build.Collections; +using Microsoft.Build.Construction; +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; + +namespace Microsoft.Build.Experimental.BuildCheck.Checks; +internal class EmbeddedResourceCheck : Check +{ + private const string RuleId = "BC0105"; + public static CheckRule SupportedRule = new CheckRule(RuleId, "EmbeddedResourceCulture", + ResourceUtilities.GetResourceString("BuildCheck_BC0105_Title")!, + ResourceUtilities.GetResourceString("BuildCheck_BC0105_MessageFmt")!, + new CheckConfiguration() { RuleId = RuleId, Severity = CheckResultSeverity.Warning }); + + public override string FriendlyName => "MSBuild.EmbeddedResourceCulture"; + + public override IReadOnlyList SupportedRules { get; } = [SupportedRule]; + + public override void Initialize(ConfigurationContext configurationContext) + { + /* This is it - no custom configuration */ + } + + public override void RegisterActions(IBuildCheckRegistrationContext registrationContext) + { + registrationContext.RegisterEvaluatedItemsAction(EvaluatedItemsAction); + } + + internal override bool IsBuiltIn => true; + + private readonly HashSet _projects = new(MSBuildNameIgnoreCaseComparer.Default); + + private void EvaluatedItemsAction(BuildCheckDataContext context) + { + // Deduplication + if (!_projects.Add(context.Data.ProjectFilePath)) + { + return; + } + + foreach (ItemData itemData in context.Data.EnumerateItemsOfType("EmbeddedResource")) + { + string evaluatedEmbedItem = itemData.EvaluatedInclude; + bool hasDoubleExtension = HasDoubleExtension(evaluatedEmbedItem); + + if (!hasDoubleExtension) + { + continue; + } + + bool hasNeededMetadata = false; + foreach (KeyValuePair keyValuePair in itemData.EnumerateMetadata()) + { + if (MSBuildNameIgnoreCaseComparer.Default.Equals(keyValuePair.Key, ItemMetadataNames.culture)) + { + hasNeededMetadata = true; + break; + } + + if (MSBuildNameIgnoreCaseComparer.Default.Equals(keyValuePair.Key, ItemMetadataNames.withCulture) && + keyValuePair.Value.IsMSBuildFalseString()) + { + hasNeededMetadata = true; + break; + } + } + + if (!hasNeededMetadata) + { + context.ReportResult(BuildCheckResult.Create( + SupportedRule, + // Populating precise location tracked via https://github.com/orgs/dotnet/projects/373/views/1?pane=issue&itemId=58661732 + ElementLocation.EmptyLocation, + Path.GetFileName(context.Data.ProjectFilePath), + evaluatedEmbedItem, + GetSupposedCultureExtension(evaluatedEmbedItem))); + } + } + } + + private static bool HasDoubleExtension(string s) + { + const char extensionSeparator = '.'; + int firstIndex; + return + !string.IsNullOrEmpty(s) && + (firstIndex = s.IndexOf(extensionSeparator)) > -1 && + // We need at least 2 chars for this extension - separator and one char of extension, + // so next extension can start closest 2 chars from this one + // (this is to grace handle double dot - which is not double extension) + firstIndex + 2 <= s.Length && + s.IndexOf(extensionSeparator, firstIndex + 2) > -1; + } + + /// + /// Returns the extension that is supposed to implicitly denote the culture. + /// This is mimicking the behavior of Microsoft.Build.Tasks.Culture.GetItemCultureInfo + /// + private string GetSupposedCultureExtension(string s) + { + // If the item is defined as "Strings.en-US.resx", then we want to arrive to 'en-US' + + string extension = Path.GetExtension(Path.GetFileNameWithoutExtension(s)); + if (extension.Length > 1) + { + extension = extension.Substring(1); + } + return extension; + } +} diff --git a/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs b/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs index 93107640538..824e7983143 100644 --- a/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs +++ b/src/Build/BuildCheck/Infrastructure/BuildCheckManagerProvider.cs @@ -148,7 +148,8 @@ internal readonly record struct BuiltInCheckFactory( new BuiltInCheckFactory([SharedOutputPathCheck.SupportedRule.Id], SharedOutputPathCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), new BuiltInCheckFactory([PreferProjectReferenceCheck.SupportedRule.Id], PreferProjectReferenceCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), new BuiltInCheckFactory([DoubleWritesCheck.SupportedRule.Id], DoubleWritesCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), - new BuiltInCheckFactory([NoEnvironmentVariablePropertyCheck.SupportedRule.Id], NoEnvironmentVariablePropertyCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct) + new BuiltInCheckFactory([NoEnvironmentVariablePropertyCheck.SupportedRule.Id], NoEnvironmentVariablePropertyCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), + new BuiltInCheckFactory([EmbeddedResourceCheck.SupportedRule.Id], EmbeddedResourceCheck.SupportedRule.DefaultConfiguration.IsEnabled ?? false, Construct), ], // BuildCheckDataSource.Execution diff --git a/src/Build/Resources/Strings.resx b/src/Build/Resources/Strings.resx index 3fe987a5066..2fae26dae65 100644 --- a/src/Build/Resources/Strings.resx +++ b/src/Build/Resources/Strings.resx @@ -2174,6 +2174,14 @@ Utilization: {0} Average Utilization: {1:###.0} Project {0} references output of a project {1}. Referenced path: {2}. ProjectReference should be used instead. + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + A property that is accessed should be declared first. diff --git a/src/Build/Resources/xlf/Strings.cs.xlf b/src/Build/Resources/xlf/Strings.cs.xlf index ff12e0468af..bc75b630e00 100644 --- a/src/Build/Resources/xlf/Strings.cs.xlf +++ b/src/Build/Resources/xlf/Strings.cs.xlf @@ -181,6 +181,16 @@ Na projekt by se nemělo odkazovat přes Reference na jeho výstup, ale přímo přes ProjectReference. + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. K vlastnosti: {0} bylo přistupováno, ale nebyla nikdy inicializována. diff --git a/src/Build/Resources/xlf/Strings.de.xlf b/src/Build/Resources/xlf/Strings.de.xlf index 47fa3acb9b5..b3788cf3d57 100644 --- a/src/Build/Resources/xlf/Strings.de.xlf +++ b/src/Build/Resources/xlf/Strings.de.xlf @@ -181,6 +181,16 @@ Auf ein Projekt darf nicht über "Reference" auf seine Ausgabe verwiesen werden, sondern direkt über "ProjectReference". + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. Auf die Eigenschaft „{0}“ wurde zugegriffen, sie wurde jedoch nie initialisiert. diff --git a/src/Build/Resources/xlf/Strings.es.xlf b/src/Build/Resources/xlf/Strings.es.xlf index b2cf227eb21..88418d418d2 100644 --- a/src/Build/Resources/xlf/Strings.es.xlf +++ b/src/Build/Resources/xlf/Strings.es.xlf @@ -181,6 +181,16 @@ No se debe hacer referencia a un proyecto a través de "Reference" a su salida, sino directamente a través de "ProjectReference". + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. Propiedad: se obtuvo acceso a "{0}", pero nunca se inicializó. diff --git a/src/Build/Resources/xlf/Strings.fr.xlf b/src/Build/Resources/xlf/Strings.fr.xlf index 80df3b188af..04314b909f1 100644 --- a/src/Build/Resources/xlf/Strings.fr.xlf +++ b/src/Build/Resources/xlf/Strings.fr.xlf @@ -181,6 +181,16 @@ Un projet ne doit pas être référencé via « Reference » à sa sortie, mais directement via « ProjectReference ». + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. Propriété : « {0} » a été consultée, mais elle n'a jamais été initialisée. diff --git a/src/Build/Resources/xlf/Strings.it.xlf b/src/Build/Resources/xlf/Strings.it.xlf index 7c1c3bf7557..7c6adae56d1 100644 --- a/src/Build/Resources/xlf/Strings.it.xlf +++ b/src/Build/Resources/xlf/Strings.it.xlf @@ -181,6 +181,16 @@ Un progetto non può fare riferimento al relativo output tramite 'Reference', ma direttamente tramite 'ProjectReference'. + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. È stato eseguito l'accesso alla proprietà '{0}', ma non è mai stata inizializzata. diff --git a/src/Build/Resources/xlf/Strings.ja.xlf b/src/Build/Resources/xlf/Strings.ja.xlf index c2c57270135..5f828d7124b 100644 --- a/src/Build/Resources/xlf/Strings.ja.xlf +++ b/src/Build/Resources/xlf/Strings.ja.xlf @@ -181,6 +181,16 @@ プロジェクトは、出力に対する 'Reference' を介して参照するのではなく、'ProjectReference' を介して直接参照する必要があります。 + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. プロパティ: '{0}' にアクセスしましたが、初期化されませんでした。 diff --git a/src/Build/Resources/xlf/Strings.ko.xlf b/src/Build/Resources/xlf/Strings.ko.xlf index 2649c525183..1d13a250913 100644 --- a/src/Build/Resources/xlf/Strings.ko.xlf +++ b/src/Build/Resources/xlf/Strings.ko.xlf @@ -181,6 +181,16 @@ 프로젝트는 ''Reference''를 통해 출력을 참조하지 말고 ''ProjectReference''를 통해 직접 참조해야 합니다. + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. 속성: '{0}'에 액세스했지만 초기화되지 않았습니다. diff --git a/src/Build/Resources/xlf/Strings.pl.xlf b/src/Build/Resources/xlf/Strings.pl.xlf index 746b03ca198..2477e49d045 100644 --- a/src/Build/Resources/xlf/Strings.pl.xlf +++ b/src/Build/Resources/xlf/Strings.pl.xlf @@ -181,6 +181,16 @@ Do projektu nie należy odwoływać się poprzez opcję „Odwołanie” do jego danych wyjściowych, ale raczej bezpośrednio poprzez opcję „Odwołanie do projektu”. + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. Właściwość: uzyskano dostęp do „{0}”, ale nigdy nie dokonano inicjacji. diff --git a/src/Build/Resources/xlf/Strings.pt-BR.xlf b/src/Build/Resources/xlf/Strings.pt-BR.xlf index 2173625f9c1..23b7eff2bb2 100644 --- a/src/Build/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Build/Resources/xlf/Strings.pt-BR.xlf @@ -181,6 +181,16 @@ Um projeto não deve ser referenciado por meio de "Referência" à sua saída, mas diretamente por meio de "ProjectReference". + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. Propriedade: "{0}" foi acessada, mas nunca foi inicializada. diff --git a/src/Build/Resources/xlf/Strings.ru.xlf b/src/Build/Resources/xlf/Strings.ru.xlf index dd8154f048a..a10fa1e92fb 100644 --- a/src/Build/Resources/xlf/Strings.ru.xlf +++ b/src/Build/Resources/xlf/Strings.ru.xlf @@ -181,6 +181,16 @@ На проект следует ссылаться не с помощью параметра "Reference" для его выходных данных, а непосредственно посредством "ProjectReference". + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. Свойство: к "{0}" получен доступ, но он не инициализирован. diff --git a/src/Build/Resources/xlf/Strings.tr.xlf b/src/Build/Resources/xlf/Strings.tr.xlf index 497a5fd2043..c3d3913867b 100644 --- a/src/Build/Resources/xlf/Strings.tr.xlf +++ b/src/Build/Resources/xlf/Strings.tr.xlf @@ -181,6 +181,16 @@ Bir projeye, çıkışına 'Reference' aracılığıyla değil doğrudan 'ProjectReference' aracılığıyla başvurulmalıdır. + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. '{0}' özelliğine erişildi, ancak hiç başlatılmadı. diff --git a/src/Build/Resources/xlf/Strings.zh-Hans.xlf b/src/Build/Resources/xlf/Strings.zh-Hans.xlf index 7857453e19a..9ee0178a376 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hans.xlf @@ -181,6 +181,16 @@ 不得通过 "Reference" 引用项目的输出来引用项目,而是应通过 "ProjectReference" 直接引用。 + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. 已访问属性“{0}”,但从未将其初始化过。 diff --git a/src/Build/Resources/xlf/Strings.zh-Hant.xlf b/src/Build/Resources/xlf/Strings.zh-Hant.xlf index 6e819e34aee..7de56db9590 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hant.xlf @@ -181,6 +181,16 @@ 專案不應透過 'Reference' 來參照其輸出,而應直接透過 'ProjectReference'。 + + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Project {0} specifies 'EmbeddedResource' item '{1}', that has possibly a culture denoting extension ('{2}'), but explicit 'Culture' nor 'WithCulture=false' metadata are not specified. + Terms in quotes are not to be translated. + + + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + It is recommended to specify explicit 'Culture' metadata, or 'WithCulture=false' metadata with 'EmbeddedResource' item in order to avoid wrong or nondeterministic culture estimation. + Terms in quotes are not to be translated. + Property: '{0}' was accessed, but it was never initialized. 已存取屬性: '{0}',但從未初始化。 diff --git a/src/BuildCheck.UnitTests/EndToEndTests.cs b/src/BuildCheck.UnitTests/EndToEndTests.cs index 93723e17b4c..8b240e10c76 100644 --- a/src/BuildCheck.UnitTests/EndToEndTests.cs +++ b/src/BuildCheck.UnitTests/EndToEndTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text.Json.Nodes; using System.Text.RegularExpressions; using System.Xml; using Microsoft.Build.Experimental.BuildCheck; @@ -63,6 +64,112 @@ public void PropertiesUsageAnalyzerTest(bool buildInOutOfProcessNode) Regex.Matches(output, "BC0203 .* Property").Count.ShouldBe(2); } + // + + [Theory] + [InlineData( + "cs", + "cs", + """""", + "warning BC0105: .* 'Resource1\\.cs\\.resx'")] + // Following tests are prepared after the EmbeddedCulture handling fix is merged: https://github.com/dotnet/msbuild/pull/11000 + ////[InlineData( + //// "xyz", + //// "xyz", + //// """""", + //// "warning BC0105: .* 'Resource1\\.xyz\\.resx'")] + ////[InlineData( + //// "xyz", + //// "zyx", + //// """""", + //// "")] + public void EmbeddedResourceCheckTest(string culture, string resourceExtension, string resourceElement, string expectedDiagnostic) + { + EmbedResourceTestOutput output = RunEmbeddedResourceTest(resourceElement, resourceExtension); + + // each finding should be found just once - but reported twice, due to summary + if (!string.IsNullOrEmpty(expectedDiagnostic)) + { + Regex.Matches(output.LogOutput, expectedDiagnostic).Count.ShouldBe(2); + } + + AssertHasResourceForCulture("en"); + AssertHasResourceForCulture(culture); + output.DepsJsonResources.Count.ShouldBe(2); + + void AssertHasResourceForCulture(string culture) + { + KeyValuePair resource = output.DepsJsonResources.FirstOrDefault( + o => o.Value?["locale"]?.ToString().Equals(culture, StringComparison.Ordinal) ?? false); + resource.Equals(default(KeyValuePair)).ShouldBe(false, + $"Resource for culture {culture} was not found in deps.json:{Environment.NewLine}{output.DepsJsonResources.ToString()}"); + + resource.Key.ShouldBeEquivalentTo($"{culture}/ReferencedProject.resources.dll", + $"Unexpected resource for culture {culture} was found in deps.json:{Environment.NewLine}{output.DepsJsonResources.ToString()}"); + } + } + + private readonly record struct EmbedResourceTestOutput(String LogOutput, JsonObject DepsJsonResources); + + private EmbedResourceTestOutput RunEmbeddedResourceTest(string resourceXmlToAdd, string resourceExtension) + { + string testAssetsFolderName = "EmbeddedResourceTest"; + const string entryProjectName = "EntryProject"; + const string referencedProjectName = "ReferencedProject"; + const string templateToReplace = "###EmbeddedResourceToAdd"; + TransientTestFolder workFolder = _env.CreateFolder(createFolder: true); + + CopyFilesRecursively(Path.Combine(TestAssetsRootPath, testAssetsFolderName), workFolder.Path); + ReplaceStringInFile(Path.Combine(workFolder.Path, referencedProjectName, $"{referencedProjectName}.csproj"), + templateToReplace, resourceXmlToAdd); + File.Copy( + Path.Combine(workFolder.Path, referencedProjectName, "Resource1.resx"), + Path.Combine(workFolder.Path, referencedProjectName, $"Resource1.{resourceExtension}.resx")); + + _env.SetCurrentDirectory(Path.Combine(workFolder.Path, entryProjectName)); + + string output = RunnerUtilities.ExecBootstrapedMSBuild("-check -restore", out bool success); + _env.Output.WriteLine(output); + _env.Output.WriteLine("========================="); + success.ShouldBeTrue(); + + string[] depsFiles = Directory.GetFiles(Path.Combine(workFolder.Path, entryProjectName), $"{entryProjectName}.deps.json", SearchOption.AllDirectories); + depsFiles.Length.ShouldBe(1); + + JsonNode? depsJson = JsonObject.Parse(File.ReadAllText(depsFiles[0])); + + depsJson.ShouldNotBeNull("Valid deps.json file expected"); + + var resources = depsJson!["targets"]?.AsObject().First().Value?[$"{referencedProjectName}/1.0.0"]?["resources"]?.AsObject(); + + resources.ShouldNotBeNull("Expected deps.json with 'resources' section"); + + return new(output, resources); + + void ReplaceStringInFile(string filePath, string original, string replacement) + { + File.Exists(filePath).ShouldBeTrue($"File {filePath} expected to exist."); + string text = File.ReadAllText(filePath); + text = text.Replace(original, replacement); + File.WriteAllText(filePath, text); + } + } + + private static void CopyFilesRecursively(string sourcePath, string targetPath) + { + // First Create all directories + foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) + { + Directory.CreateDirectory(dirPath.Replace(sourcePath, targetPath)); + } + + // Then copy all the files & Replaces any files with the same name + foreach (string newPath in Directory.GetFiles(sourcePath, "*", SearchOption.AllDirectories)) + { + File.Copy(newPath, newPath.Replace(sourcePath, targetPath), true); + } + } + [Theory] [InlineData(true, true)] @@ -640,7 +747,6 @@ private void PrepareSampleProjectsAndConfig( _env.SetCurrentDirectory(Path.GetDirectoryName(projectFile.Path)); _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", buildInOutOfProcessNode ? "1" : "0"); - _env.SetEnvironmentVariable("MSBUILDLOGPROPERTIESANDITEMSAFTEREVALUATION", "1"); // Needed for testing check BC0103 _env.SetEnvironmentVariable("TestFromTarget", "FromTarget"); diff --git a/src/BuildCheck.UnitTests/Microsoft.Build.BuildCheck.UnitTests.csproj b/src/BuildCheck.UnitTests/Microsoft.Build.BuildCheck.UnitTests.csproj index 27bf2a1542b..92e73377603 100644 --- a/src/BuildCheck.UnitTests/Microsoft.Build.BuildCheck.UnitTests.csproj +++ b/src/BuildCheck.UnitTests/Microsoft.Build.BuildCheck.UnitTests.csproj @@ -46,4 +46,8 @@ + + + + diff --git a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj new file mode 100644 index 00000000000..1ac36d043de --- /dev/null +++ b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/EntryProject/EntryProject.csproj @@ -0,0 +1,13 @@ + + + + net9.0 + enable + enable + + + + + + + diff --git a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj new file mode 100644 index 00000000000..5191394acfe --- /dev/null +++ b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/ReferencedProject.csproj @@ -0,0 +1,31 @@ + + + + net9.0 + enable + enable + + + + + True + True + Resource1.resx + + + + + True + + + + + + + en + Test.en.resources + + ###EmbeddedResourceToAdd + + + diff --git a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/Resource1.en.resx b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/Resource1.en.resx new file mode 100644 index 00000000000..1af7de150c9 --- /dev/null +++ b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/Resource1.en.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/Resource1.resx b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/Resource1.resx new file mode 100644 index 00000000000..1af7de150c9 --- /dev/null +++ b/src/BuildCheck.UnitTests/TestAssets/EmbeddedResourceTest/ReferencedProject/Resource1.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Shared/StringExtensions.cs b/src/Shared/StringExtensions.cs index 7f7c41f8ebc..6ca90bbf3d1 100644 --- a/src/Shared/StringExtensions.cs +++ b/src/Shared/StringExtensions.cs @@ -92,5 +92,22 @@ public static void WriteLine(this TextWriter writer, ReadOnlySpan buffer) writer.WriteLine(buffer.ToString()); } #endif + + /// + /// Converts a string to a bool. We consider "true/false", "on/off", and + /// "yes/no" to be valid boolean representations in the XML. The '!' prefix for negation is allowed as well. + /// Unrecognized values lead to exception + /// + /// Thrown when given argument is unrecognized MSBuild boolean string. + public static bool IsMSBuildTrueString(this string msbuildString) => + ConversionUtilities.ConvertStringToBool(msbuildString, nullOrWhitespaceIsFalse: true); + + /// + /// Converts a string to a bool. We consider "true/false", "on/off", and + /// "yes/no" to be valid boolean representations in the XML. The '!' prefix for negation is allowed as well. + /// Unrecognized values lead to exception + /// + /// Thrown when given argument is unrecognized MSBuild boolean string. + public static bool IsMSBuildFalseString(this string msbuildString) => !IsMSBuildTrueString(msbuildString); } } diff --git a/src/Utilities.UnitTests/Microsoft.Build.Utilities.UnitTests.csproj b/src/Utilities.UnitTests/Microsoft.Build.Utilities.UnitTests.csproj index 3f86257bcfc..e29bd389537 100644 --- a/src/Utilities.UnitTests/Microsoft.Build.Utilities.UnitTests.csproj +++ b/src/Utilities.UnitTests/Microsoft.Build.Utilities.UnitTests.csproj @@ -29,6 +29,7 @@ + NativeMethods.cs