diff --git a/.editorconfig b/.editorconfig index 72c7d4a2b87..b8e856dc62d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,25 +1,199 @@ -# EditorConfig is awesome: -http://EditorConfig.org +# editorconfig.org # top-most EditorConfig file root = true # Default settings: +# A newline ending every file # Use 4 spaces as indentation [*] +insert_final_newline = true indent_style = space indent_size = 4 -insert_final_newline = true trim_trailing_whitespace = true +[project.json] +indent_size = 2 + +# C# files +[*.cs] +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current + +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion + +# avoid this. unless absolutely necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# Types: use keywords instead of BCL types, and permit var only when the type is clear +csharp_style_var_for_built_in_types = false:suggestion +csharp_style_var_when_type_is_apparent = false:none +csharp_style_var_elsewhere = false:suggestion +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# name all constant fields using PascalCase +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# static fields should have s_ prefix +dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion +dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields +dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static +dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected +dotnet_naming_style.static_prefix_style.required_prefix = s_ +dotnet_naming_style.static_prefix_style.capitalization = camel_case + +# internal and private fields should be _camelCase +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +# Code style defaults +csharp_using_directive_placement = outside_namespace:suggestion +dotnet_sort_system_directives_first = true +csharp_prefer_braces = true:silent +csharp_preserve_single_line_blocks = true:none +csharp_preserve_single_line_statements = false:none +csharp_prefer_static_local_function = true:suggestion +csharp_prefer_simple_using_statement = false:none +csharp_style_prefer_switch_expression = true:suggestion + +# Code quality +dotnet_style_readonly_field = true:suggestion +dotnet_code_quality_unused_parameters = non_public:suggestion + +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +csharp_prefer_simple_default_expression = true:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_methods = true:silent +csharp_style_expression_bodied_constructors = true:silent +csharp_style_expression_bodied_operators = true:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = true:silent + +# Pattern matching +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion + +# Null checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Other features +csharp_style_prefer_index_operator = false:none +csharp_style_prefer_range_operator = false:none +csharp_style_pattern_local_over_anonymous_function = false:none + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Analyzers +dotnet_code_quality.ca1802.api_surface = private, internal +dotnet_code_quality.ca2208.api_surface = public + +# License header +file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license.\n + +# C++ Files +[*.{cpp,h,in}] +curly_bracket_next_line = true +indent_brace_style = Allman + # Xml project files -[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}] +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] +indent_size = 2 + +[*.{csproj,vbproj,proj,nativeproj,locproj}] +charset = utf-8 + +# Xml build files +[*.builds] +indent_size = 2 + +# Xml files +[*.{xml,stylecop,resx,ruleset}] indent_size = 2 # Xml config files -[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +[*.{props,targets,config,nuspec}] indent_size = 2 +# YAML config files +[*.{yml,yaml}] +indent_size = 2 + +# Shell scripts +[*.sh] +end_of_line = lf +[*.{cmd, bat}] +end_of_line = crlf + [src/**/*.{cs,vb}] # IDE0005: Remove unnecessary usings/imports dotnet_diagnostic.IDE0005.severity = warning diff --git a/.github/ISSUE_TEMPLATE/01_bugreport.md b/.github/ISSUE_TEMPLATE/01_bugreport.md new file mode 100644 index 00000000000..58a8cb2c372 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/01_bugreport.md @@ -0,0 +1,58 @@ +--- +name: 🐞 Bug Report +about: Report a bug to help us improve MSBuild. +title: '' +labels: bug, needs-triage +--- + + + +### Issue Description + + +### Steps to Reproduce + + +### Expected Behavior + + +### Actual Behavior + + +### Analysis + + +### Versions & Configurations + + +### Attach a binlog + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/02_performanceissue.md b/.github/ISSUE_TEMPLATE/02_performanceissue.md new file mode 100644 index 00000000000..69ac8410706 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/02_performanceissue.md @@ -0,0 +1,69 @@ +--- +name: 📉 Performance Issue +about: Report a performance issue or regression. +title: '' +labels: performance, needs-triage +--- + + + +### Issue Description + + +### Steps to Reproduce + + +### Data + + +### Analysis + + +### Versions & Configurations + + +### Regression? + + +### Attach a binlog + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/03_mybuildisbroken.md b/.github/ISSUE_TEMPLATE/03_mybuildisbroken.md new file mode 100644 index 00000000000..a873a40f1e4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/03_mybuildisbroken.md @@ -0,0 +1,47 @@ +--- +name: 😵 My Build is Broken +about: Use this template for helping figure out what's wrong with your build. +title: '' +labels: needs-triage +--- + + + + + + +### Issue Description + + +### Steps to Reproduce + + +### Expected Behavior + + +### Actual Behavior + + +### Ask us questions + \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/04_blankissue.md b/.github/ISSUE_TEMPLATE/04_blankissue.md index 9be0e37587f..d3ae09b4431 100644 --- a/.github/ISSUE_TEMPLATE/04_blankissue.md +++ b/.github/ISSUE_TEMPLATE/04_blankissue.md @@ -2,5 +2,5 @@ name: 📄 Blank Issue about: Doesn't fit the other categories? File a blank ticket here. title: '' -labels: untriaged +labels: needs-triage --- \ No newline at end of file diff --git a/.vsts-dotnet-ci.yml b/.vsts-dotnet-ci.yml index 81b331862cc..bbbf0897830 100644 --- a/.vsts-dotnet-ci.yml +++ b/.vsts-dotnet-ci.yml @@ -24,11 +24,13 @@ jobs: continueOnError: true condition: always() - task: PublishTestResults@2 - displayName: Publish .NET Core 2.1 Test Results + displayName: Publish .NET Test Results inputs: - testRunTitle: 'Windows-on-full .NET Core 2.1' + testRunTitle: 'Windows-on-full .NET' testRunner: XUnit - testResultsFiles: 'artifacts/TestResults/Debug/*UnitTests_netcoreapp2.1*.xml' + testResultsFiles: | + artifacts/TestResults/**/*.xml + !**/*UnitTests_net472*.xml publishRunAttachments: true mergeTestResults: true continueOnError: true @@ -69,11 +71,13 @@ jobs: continueOnError: true condition: always() - task: PublishTestResults@2 - displayName: Publish .NET Core 2.1 Test Results + displayName: Publish .NET Test Results inputs: - testRunTitle: 'Windows-on-Core .NET Core 2.1' + testRunTitle: 'Windows-on-Core .NET' testRunner: XUnit - testResultsFiles: 'artifacts/TestResults/Debug/*UnitTests_netcoreapp2.1*.xml' + testResultsFiles: | + artifacts/TestResults/**/*.xml + !**/*UnitTests_net472*.xml publishRunAttachments: true mergeTestResults: true continueOnError: true @@ -103,12 +107,6 @@ jobs: inputs: filename: 'eng/cibuild.cmd' arguments: '-configuration Release -test' - - task: RichCodeNavIndexer@0 - displayName: RichCodeNav Upload - inputs: - languages: 'csharp' - continueOnError: true - condition: succeeded() - task: PublishTestResults@2 displayName: Publish .NET Framework Test Results inputs: @@ -120,11 +118,13 @@ jobs: continueOnError: true condition: always() - task: PublishTestResults@2 - displayName: Publish .NET Core 2.1 Test Results + displayName: Publish .NET Test Results inputs: - testRunTitle: 'Windows-on-full Release .NET Core 2.1' + testRunTitle: 'Windows-on-full Release .NET' testRunner: XUnit - testResultsFiles: 'artifacts/TestResults/Release/*UnitTests_netcoreapp2.1*.xml' + testResultsFiles: | + artifacts/TestResults/**/*.xml + !**/*UnitTests_net472*.xml publishRunAttachments: true mergeTestResults: true continueOnError: true @@ -144,19 +144,37 @@ jobs: continueOnError: true condition: always() +- job: RichCodeNavIndex + displayName: "Windows Code Indexing" + pool: + vmImage: 'windows-latest' + steps: + - task: BatchScript@1 + displayName: build.cmd + inputs: + filename: 'build.cmd' + - task: RichCodeNavIndexer@0 + displayName: RichCodeNav Upload + inputs: + languages: 'csharp' + continueOnError: true + condition: succeeded() + - job: CoreBootstrappedOnLinux displayName: "Linux Core" pool: - vmImage: 'ubuntu-16.04' + vmImage: 'ubuntu-latest' steps: - bash: . 'eng/cibuild_bootstrapped_msbuild.sh' displayName: CI Build - task: PublishTestResults@2 - displayName: Publish .NET Core 2.1 Test Results + displayName: Publish .NET Test Results inputs: - testRunTitle: 'Linux .NET Core 2.1' + testRunTitle: 'Linux .NET' testRunner: XUnit - testResultsFiles: 'artifacts/TestResults/Debug/*UnitTests*.xml' + testResultsFiles: | + artifacts/TestResults/**/*.xml + !**/*UnitTests_net472*.xml publishRunAttachments: true mergeTestResults: true continueOnError: true @@ -184,11 +202,13 @@ jobs: - bash: . 'eng/cibuild_bootstrapped_msbuild.sh' displayName: CI Build - task: PublishTestResults@2 - displayName: Publish .NET Core 2.1 Test Results + displayName: Publish .NET Test Results inputs: - testRunTitle: 'macOS .NET Core 2.1' + testRunTitle: 'macOS .NET' testRunner: XUnit - testResultsFiles: 'artifacts/TestResults/Debug/*UnitTests*.xml' + testResultsFiles: | + artifacts/TestResults/**/*.xml + !**/*UnitTests_net472*.xml publishRunAttachments: true mergeTestResults: true continueOnError: true @@ -248,3 +268,9 @@ jobs: ArtifactName: 'MonoOnMac test logs' continueOnError: true condition: always() + +- template: /eng/common/templates/job/source-build.yml + parameters: + platform: + name: 'Managed' + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-3e800f1-20190501005343' diff --git a/.vsts-dotnet.yml b/.vsts-dotnet.yml index 098cd64feaa..415fa36b412 100644 --- a/.vsts-dotnet.yml +++ b/.vsts-dotnet.yml @@ -10,12 +10,28 @@ trigger: # SignType: real # SkipApplyOptimizationData: false +parameters: +- name: OptProfDropName + displayName: Optional OptProfDrop Override + type: string + default: 'default' + variables: + # if OptProfDrop is not set, string '$(OptProfDrop)' will be passed to the build script. + - name: OptProfDrop + value: '' - name: SourceBranch value: $(IbcSourceBranchName) - - ${{ if startsWith(variables['Build.SourceBranch'], 'refs/heads/exp/') }}: + # If we're not on a vs* branch, use main as our optprof collection branch + - ${{ if not(startsWith(variables['Build.SourceBranch'], 'refs/heads/vs')) }}: - name: SourceBranch value: main + # if OptProfDropName is set as a parameter, set OptProfDrop to the parameter and unset SourceBranch + - ${{ if ne(parameters.OptProfDropName, 'default') }}: + - name: OptProfDrop + value: ${{parameters.OptProfDropName}} + - name: SourceBranch + value: '' - name: _DotNetArtifactsCategory value: .NETCore - name: _DotNetValidationArtifactsCategory @@ -26,15 +42,17 @@ stages: displayName: Build jobs: - - template: /eng/common/templates/job/onelocbuild.yml - parameters: - CreatePr: false - LclSource: lclFilesfromPackage - LclPackageId: 'LCL-JUNO-PROD-MSBUILD' + - ${{ if eq(variables['Build.SourceBranch'], 'refs/heads/main') }}: # should track next-release's active dev branch + - template: /eng/common/templates/job/onelocbuild.yml + parameters: + LclSource: lclFilesfromPackage + LclPackageId: 'LCL-JUNO-PROD-MSBUILD' + MirrorRepo: 'msbuild' + MirrorBranch: 'main' # should match condition above - job: Windows_NT pool: - name: VSEng-MicroBuildVS2019 + name: VSEngSS-MicroBuild2019-1ES demands: - agent.os -equals Windows_NT @@ -47,9 +65,9 @@ stages: - name: TeamName value: MSBuild - name: VisualStudio.MajorVersion - value: 16 + value: 17 - name: VisualStudio.ChannelName - value: 'int.d16.10' + value: 'int.main' - name: VisualStudio.DropName value: Products/$(System.TeamProject)/$(Build.Repository.Name)/$(Build.SourceBranchName)/$(Build.BuildNumber) @@ -72,11 +90,14 @@ stages: signType: $(SignType) zipSources: false condition: and(succeeded(), in(variables['SignType'], 'test', 'real')) - - - task: ms-vseng.MicroBuildTasks.965C8DC6-1483-45C9-B384-5AC75DA1F1A4.MicroBuildOptProfPlugin@1 + + - task: MicroBuildOptProfPlugin@6 inputs: - skipRunOptimize: true - displayName: 'Install OptProf Plugin' + ProfilingInputsDropName: '$(VisualStudio.DropName)' + ShouldSkipOptimize: true + AccessToken: '$(System.AccessToken)' + feedSource: 'https://devdiv.pkgs.visualstudio.com/DefaultCollection/_packaging/MicroBuildToolset/nuget/v3/index.json' + displayName: 'Install OptProf Plugin' # Required by MicroBuildBuildVSBootstrapper - task: ms-vseng.MicroBuildTasks.32f78468-e895-4f47-962c-58a699361df8.MicroBuildSwixPlugin@1 @@ -100,7 +121,7 @@ stages: /p:DotNetSymbolServerTokenSymWeb=$(symweb-symbol-server-pat) /p:TeamName=MSBuild /p:DotNetPublishUsingPipelines=true - /p:VisualStudioIbcDrop=$(OptProfDropName) + /p:VisualStudioIbcDrop=$(OptProfDrop) displayName: Build condition: succeeded() @@ -111,7 +132,7 @@ stages: buildNumber: 'ProfilingInputs/DevDiv/$(Build.Repository.Name)/$(Build.SourceBranchName)/$(Build.BuildNumber)' sourcePath: '$(Build.SourcesDirectory)\artifacts\OptProf\$(BuildConfiguration)\Data' toLowerCase: false - usePat: false + usePat: true displayName: 'OptProf - Publish to Artifact Services - ProfilingInputs' condition: succeeded() @@ -162,11 +183,14 @@ stages: # Publishes setup VSIXes to a drop. # Note: The insertion tool looks for the display name of this task in the logs. - - task: ms-vseng.MicroBuildTasks.4305a8de-ba66-4d8b-b2d1-0dc4ecbbf5e8.MicroBuildUploadVstsDropFolder@1 + - task: MicroBuildUploadVstsDropFolder@2 displayName: Upload VSTS Drop inputs: DropName: $(VisualStudio.DropName) DropFolder: 'artifacts\VSSetup\$(BuildConfiguration)\Insertion' + AccessToken: '$(System.AccessToken)' + DropServiceUri: 'https://devdiv.artifacts.visualstudio.com' + VSDropServiceUri: 'https://vsdrop.corp.microsoft.com/file/v1' condition: succeeded() # Publish an artifact that the RoslynInsertionTool is able to find by its name. @@ -215,12 +239,19 @@ stages: displayName: Execute cleanup tasks condition: succeededOrFailed() + - template: /eng/common/templates/job/source-build.yml + parameters: + platform: + name: 'Managed' + container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-3e800f1-20190501005343' + - template: /eng/common/templates/job/publish-build-assets.yml parameters: enablePublishBuildArtifacts: true publishUsingPipelines: true dependsOn: - Windows_NT + - Source_Build_Managed pool: vmImage: vs2017-win2016 diff --git a/Directory.Build.props b/Directory.Build.props index 469d7591866..4aef1d2544c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -62,10 +62,21 @@ true + + $(AssemblyInformationCachePaths);$(NetCoreRoot)sdk\$(NetCoreSdkVersion)\SDKPrecomputedAssemblyReferences.cache + + $(DefaultItemExcludes);*.log $(DefaultItemExcludes);*.binlog true + + + + + true + + diff --git a/NuGet.config b/NuGet.config index dfac183c72c..d53a04d46d2 100644 --- a/NuGet.config +++ b/NuGet.config @@ -6,8 +6,8 @@ - - + + diff --git a/README.md b/README.md index 9589de5beba..9fc80163443 100644 --- a/README.md +++ b/README.md @@ -4,23 +4,29 @@ The Microsoft Build Engine is a platform for building applications. This engine, For more information on MSBuild, see the [MSBuild documentation](https://docs.microsoft.com/visualstudio/msbuild/msbuild) on docs.microsoft.com. +The [changelog](documentation/Changelog.md) has detailed information about changes made in different releases. + ### Build Status -The current development branch is `main`. Changes in `main` will go into a future update of MSBuild, which will release with Visual Studio 16.10 and a corresponding version of the .NET Core SDK. +The current development branch is `main`. Changes in `main` will go into a future update of MSBuild, which will release with Visual Studio 17.0 and a corresponding version of the .NET Core SDK. + +[![Build Status](https://dev.azure.com/dnceng/public/_apis/build/status/Microsoft/msbuild/msbuild-pr?branchName=main)](https://dev.azure.com/dnceng/public/_build/latest?definitionId=887&branchName=main) + +We have forked for MSBuild 16.11 in the branch [`vs16.11`](https://github.com/Microsoft/msbuild/tree/vs16.11). Changes to that branch need special approval. -[![Build Status](https://dev.azure.com/dnceng/public/_apis/build/status/Microsoft/msbuild/msbuild-pr?branchName=main)](https://dev.azure.com/dnceng/public/_build/latest?definitionId=86&branchName=main) +[![Build Status](https://dev.azure.com/dnceng/public/_apis/build/status/Microsoft/msbuild/msbuild-pr?branchName=vs16.11)](https://dev.azure.com/dnceng/public/_build/latest?definitionId=887&branchName=vs16.11) -We have forked for MSBuild 16.9 in the branch [`vs16.9`](https://github.com/dotnet/msbuild/tree/vs16.9). Changes to that branch need special approval. +MSBuild 16.9 builds from the branch [`vs16.9`](https://github.com/dotnet/msbuild/tree/vs16.9). Only high-priority bugfixes will be considered for servicing 16.9. -[![Build Status](https://dev.azure.com/dnceng/public/_apis/build/status/Microsoft/msbuild/msbuild-pr?branchName=vs16.9)](https://dev.azure.com/dnceng/public/_build/latest?definitionId=86&branchName=vs16.9) +[![Build Status](https://dev.azure.com/dnceng/public/_apis/build/status/Microsoft/msbuild/msbuild-pr?branchName=vs16.9)](https://dev.azure.com/dnceng/public/_build/latest?definitionId=887&branchName=vs16.9) MSBuild 16.7 builds from the branch [`vs16.7`](https://github.com/dotnet/msbuild/tree/vs16.7). Only high-priority bugfixes will be considered for servicing 16.7. -[![Build Status](https://dev.azure.com/dnceng/public/_apis/build/status/Microsoft/msbuild/msbuild-pr?branchName=vs16.7)](https://dev.azure.com/dnceng/public/_build/latest?definitionId=86&branchName=vs16.7) +[![Build Status](https://dev.azure.com/dnceng/public/_apis/build/status/Microsoft/msbuild/msbuild-pr?branchName=vs16.7)](https://dev.azure.com/dnceng/public/_build/latest?definitionId=887&branchName=vs16.7) MSBuild 16.4 builds from the branch [`vs16.4`](https://github.com/dotnet/msbuild/tree/vs16.4). Only high-priority bugfixes will be considered for servicing 16.4. -[![Build Status](https://dev.azure.com/dnceng/public/_apis/build/status/Microsoft/msbuild/msbuild-pr?branchName=vs16.4)](https://dev.azure.com/dnceng/public/_build/latest?definitionId=86&branchName=vs16.4) +[![Build Status](https://dev.azure.com/dnceng/public/_apis/build/status/Microsoft/msbuild/msbuild-pr?branchName=vs16.4)](https://dev.azure.com/dnceng/public/_build/latest?definitionId=887&branchName=vs16.4) MSBuild 15.9 builds from the branch [`vs15.9`](https://github.com/dotnet/msbuild/tree/vs15.9). Only very-high-priority bugfixes will be considered for servicing 15.9. @@ -54,20 +60,24 @@ MSBuild can be run on Unix systems that support .NET Core. Set-up instructions c You can turn on localized builds via the `/p:LocalizedBuild=true` command line argument. For more information on localized builds and how to make contributions to MSBuild's translations, see our [localization documentation](documentation/wiki/Localization.md) -#### Getting Started - +### Interested in contributing? Before you contribute, please read through the contributing and developer guides to get an idea of what kinds of pull requests we accept. * [Contributing Guide](documentation/wiki/Contributing-Code.md) - * **Developer Guide on:** - [.NET Core](documentation/wiki/Building-Testing-and-Debugging-on-.Net-Core-MSBuild.md) - [Full Framework](documentation/wiki/Building-Testing-and-Debugging-on-Full-Framework-MSBuild.md) - [Mono](documentation/wiki/Building-Testing-and-Debugging-on-Mono-MSBuild.md) -Looking for something to work on? This list of [up for grabs issues](https://github.com/Microsoft/msbuild/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs) is a great place to start. +* See our [up for grabs issues](https://github.com/Microsoft/msbuild/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs) for a list of issues we think are great to onboard new developers. + - **Note:** Please leave a comment asking to be assigned the issue if you want to work on it. +* See our [label documentation](documentation/wiki/labels.md) for descriptions of labels we use throughout the repo. + +### Other ways to contribute +We encourage any contributions you decide to make to the repo! -You are also encouraged to start a discussion by filing an issue or creating a gist. +* [File an issue](https://github.com/dotnet/msbuild/issues/new/choose) +* [Start a discussion](https://github.com/dotnet/msbuild/discussions) ### MSBuild Components diff --git a/documentation/Changelog.md b/documentation/Changelog.md new file mode 100644 index 00000000000..19f97e2e2e7 --- /dev/null +++ b/documentation/Changelog.md @@ -0,0 +1,249 @@ +# MSBuild Changelog + +## MSBuild 16.11.0 + +This version of MSBuild shipped with Visual Studio 2019 version 16.11.0 and .NET SDK 5.0.400. + +### What's new + +* MSBuild now supports long paths in the 64-bit `amd64\MSBuild.exe` executable. +* New version properties `MSBuildFileVersion` (4-part, matches file version) and `MSBuildSemanticVersion` (matches package versions) are now available for use (#6534). + +### Detailed release notes + +#### Added + +* Additional properties documented and available for completion in Visual Studio (#6500, #6530). +* The `SignFile` task is now available in MSBuild on .NET 5.0 (#6509). Thanks, @Zastai! +* New version properties `MSBuildFileVersion` (4-part, matches file version) and `MSBuildSemanticVersion` (matches package versions) are now available for use (#6534). +#### Changed + +* When using the experimental cache API, schedule proxy builds to the in-proc node for performance (#6386). +* Experimental cache queries are now executed in parallel (#6468). +* The ETW events generated in `ResolveAssemblyReference` now include an approximation of the "size" of the RAR request (#6410). + +#### Fixed + +* Fixed memory leak in `ProjectRootElement.Reload` (#6457). +* Added locking to avoid race conditions in `BuildManager` (#6412). +* Allow `ResolveAssemblyReferences` precomputed cache files to be in read-only locations (#6393). +* 64-bit `al.exe` is used when targeting 64-bit architectures (for real this time) (#6484). +* Builds with `ProduceOnlyReferenceAssembly` no longer expect debug symbols to be produced (#6511). Thanks, @Zastai! +* 64-bit `MSBuild.exe` supports long paths (and other .NET default behaviors) (#6562). +* Non-graph builds no longer crash in the experimental project cache (#6568). +* The experimental project cache is initialized only once (#6569). +* The experimental project cache no longer tries to schedule proxy builds to the in-proc node (#6635). + +#### Infrastructure + +* Use a packaged C# compiler to avoid changes in reference assembly generation caused by compiler changes (#6431). +* Use more resilient test-result upload patterns (#6489). +* Conditional compilation for .NET Core within our repo now includes new .NET 5.0+ runtimes (#6538). +* Switched to OneLocBuild for localization PRs (#6561). +* Moved to latest Ubuntu image for PR test legs (#6573). + +## MSBuild 16.10.2 + +This version of MSBuild shipped with Visual Studio 2019 version 16.10.2 and will ship with .NET SDK 5.0.302. + +#### Fixed + +* Fixed a regression in the `MakeRelative` property function that dropped trailing slashes (#6513). Thanks, @dsparkplug and @pmisik! +* Fixed a regression in glob matching where files without extensions were erroneously not matched (#6531). +* Fixed a change in logging that caused crashes in Azure DevOps loggers (#6520). + +## MSBuild 16.10.2 + +This version of MSBuild shipped with Visual Studio 2019 version 16.10.2 and will ship with .NET SDK 5.0.302. + +#### Fixed + +* Fixed a regression in the `MakeRelative` property function that dropped trailing slashes (#6513). Thanks, @dsparkplug and @pmisik! +* Fixed a regression in glob matching where files without extensions were erroneously not matched (#6531). +* Fixed a change in logging that caused crashes in Azure DevOps loggers (#6520). + +## MSBuild 16.10.1 + +This version of MSBuild shipped with Visual Studio 2019 version 16.10.1 and .NET SDK 5.0.301. + +#### Fixed + +* Restore support for building individual project(s) within solutions by specifying `-t:Project` (#6465). + +## MSBuild 16.9.2 + +This version of MSBuild shipped with Visual Studio 2019 version 16.9.7. + +#### Fixed + +* Fixed MSB0001 error when building large solutions (#6437). + +## MSBuild 16.10.0 + +This version of MSBuild shipped with Visual Studio 2019 version 16.10.0 and .NET SDK 5.0.300. + +### What's new + +* MSBuild now targets .NET 5.0 and .NET Framework 4.7.2. +* MSBuild is faster and uses less memory. +* Binary logs are smaller and have less performance overhead. +* Tasks can now opt into resource management to improve parallelism in large builds. +* It's now possible to optionally embed arbitrary files in a binary log. + +### Detailed release notes + +#### Added + +* Projects can now specify `AdditionalTargetFrameworkInfoProperty` items to indicate that referencing projects should get those properties exposed as `AdditionalPropertiesFromProject` metadata on resolved reference items. (#5994). +* The `Unzip` task now accepts `Include` and `Exclude` arguments to filter what is extracted from the zip file (#6018). Thanks, @IvanLieckens! +* The `-graph:noBuild` command line argument can be used to validate that a graph is buildable without actually building it (#6016). +* `TaskParameterEventArgs` allow logging task parameters and values in a compact, structured way (#6155). Thanks, @KirillOsenkov! +* ClickOnce publish now supports Ready To Run (#6244). +* .NET 5.0 applications may now specify a toolset configuration file (#6220). +* `ResolveAssemblyReferences` can now consume information about assemblies distributed as part of the SDK (#6017). +* Allow constructing a `ProjectInstance` from a `ProjectLink` (#6262). +* Introduce cross-process resource management for tasks (#5859). +* `ProjectEvaluationFinished` now has fields for properties and items (#6287). Thanks, @KirillOsenkov! +* `WriteCodeFragment` can now write assembly attributes of specified types, and infers some common types (#6285). Thanks, @reduckted! +* The `-detailedSummary` option now accepts a boolean argument, preventing dumping details to the console logger when building with `-bl -ds:false` (#6338). Thanks, @KirillOsenkov! +* Binary logs now include files listed in the item `EmbedInBinlog` as well as MSBuild projects (#6339). Thanks, @KirillOsenkov! +* The `FindInvalidProjectReferences` task is now available in .NET Core/5.0+ scenarios (#6365). + +#### Changed + +* String deduplication is now much more sophisticated, reducing memory usage (#5663). +* Refactoring and performance improvements in `ResolveAssemblyReferences` (#5929, #6094). +* Binary logs now store strings only once, dramatically reducing log size (#6017, #6326). Thanks, @KirillOsenkov! +* Refactoring and code cleanup (#6120, #6159, #6158, #6282). Thanks, @Nirmal4G! +* `Span`-based methods are used on .NET Framework MSBuild as well as .NET 5.0 (#6130). +* Improved `MSB4064` error to include information about the loaded task that didn't have the argument (#5945). Thanks, @BartoszKlonowski! +* Performance improvements in inter-node communication (#6023). Thanks, @KirillOsenkov! +* Performance improvements in matching items based on metadata (#6035), property expansion (#6128), glob evaluation (#6151), enumerating files (#6227). +* When evaluated with `IgnoreInvalidImports`, _empty_ imports are also allowed (#6222). +* `Log.HasLoggedError` now respects `MSBuildWarningsAsErrors` (#6174). +* `TargetPath` metadata is now respected on items that copy to output directories, and takes precedence over `Link` (#6237). +* The `Restore` operation now fails when SDKs are unresolvable (#6312). +* `MSBuild.exe.config` now has explicit binding redirects for all assemblies in the MSBuild VSIX (#6334). + +#### Fixed + +* Inconsistencies between `XamlPreCompile` and the `CoreCompile` C## compiler invocation (#6093). Thanks, @huoyaoyuan! +* Wait for child nodes to exit before exiting the entry-point node in VSTest scenarios (#6053). Thanks, @tmds! +* Fix bad plugin EndBuild exception handling during graph builds (#6110). +* Allow specifying `UseUtf8Encoding` in `ToolTask`s (#6188). +* Failures on big-endian systems (#6204). Thanks, @uweigand! +* 64-bit `al.exe` is used when targeting 64-bit architectures (#6207). +* Improved error messages when encountering a `BadImageReferenceException` in `ResolveAssemblyReferences` (#6240, #6270). Thanks, @FiniteReality! +* Escape special characters in `Exec`’s generated batch files, allowing builds as users with some special characters in their Windows username (#6233). +* Permit comments and trailing commas in solution filter files (#6346). +* Exceptions thrown from experimental cache plugins are now handled and logged better (#6345, #6368). +* Source generators with configuration files can now be used in XamlPreCompile (#6438). +* Large builds no longer crash with an exception in `LogProjectStarted` (#6437). + +#### Infrastructure + +* Update to Arcade 5.0 and .NET 5.0 (#5836). +* The primary development branch is now named `main`. +* Test robustness improvements (#6055, #6336, #6337, #6332). Thanks, @tmds and @KirillOsenkov! +* Remove unnecessary NuGet package references (#6036). Thanks, @teo-tsirpanis! +* Correctly mark .NET Framework 3.5 reference assembly package dependency as private (#6214). +* Our own builds opt into text-based performance logging (#6274). +* Update to Arcade publishing v3 (#6349). +* Use OneLocBuild localization process (#6378). + +#### Documentation + +* Updates to static graph documentation (#6043). +* Short doc on the threading model (#6042). +* Update help text to indicate that `--` is a valid argument prefix (#6205). Thanks, @BartoszKlonowski! +* API documentation improvements (#6246, #6284). +* Details about interactions with the Global Assembly Cache (#6173). + +## MSBuild 16.9.0.2116703 + +⚠ This release should have been versioned `16.9.1` but was erroneously released as 16.9.0. + +This version of MSBuild shipped with Visual Studio 2019 version 16.9.3. + +#### Fixed + +* Restore support for building solutions with web site projects (#6238). + +## MSBuild 16.9.0 + +This version of MSBuild shipped with Visual Studio 2019 version 16.9.0 and .NET SDK 5.0.200. + +### What's new + +* `MSB3277` warnings now include information about the assembly identities involved, instead of saying to rerun under higher verbosity. +* It's now possible to opt out of culture-name detection of `EmbeddedResource`s, for instance to have a resource named `a.cs.template`. +* Common targets now support `$(BaseOutputPath)`, with the default value `bin`. +* Item `Update`s are no longer case-sensitive, fixing a regression in MSBuild 16.6 (#5888). +* `ParentBuildEventContext` now includes a parent `MSBuild` task if relevant, enabling proper nesting in GUI viewers. +* Builds that fail because a warning was elevated to an error now report overall failure in the `MSBuild.exe` exit code. + +### Detailed release notes + +#### Added + +* The `MSB4006` error has been enhanced to describe the cycle when possible (#5711). Thanks, @haiyuzhu!. +* More information is logged under `MSBUILDDEBUGCOMM` (#5759). +* The command line parser now accepts arguments with double hyphens (`--argument`) as well as single hyphens (`-argument`) and forward slashes (`/argument`) (#5786). Thanks, @BartoszKlonowski! +* MSBuild now participates in the .NET CLI text performance log system on an opt-in basis (#5861). +* Common targets now support `$(BaseOutputPath)`, with the default value `bin` (#5238). Thanks, @Nirmal4G! +* `Microsoft.Build.Exceptions.CircularDependencyException` is now public (#5988). Thanks, @tflynt91! +* `EvaluationId` is now preserved in the `ProjectStarted` event, allowing disambiguating related project start events (#5997). Thanks, @KirillOsenkov! +* The `ResolveAssemblyReference` task can now optionally emit items describing unresolved assembly conflicts (#5990). +* Experimental `ProjectCache` API to enable higher-order build systems (#5936). + +#### Changed + +* Warnings suppressed via `$(NoWarn)` (which formerly applied only to targets that opted in like the C## compiler) are now treated as `$(MSBuildWarningsAsMessages)` (#5671). +* Warnings elevated via `$(WarningsAsErrors )` (which formerly applied only to targets that opted in like the C## compiler) are now treated as `$(MSBuildWarningsAsErrors)` (#5774). +* Improved error message when using an old .NET (Core) SDK and targeting .NET 5.0 (#5826). +* Trailing spaces in property expressions inside conditionals now emit an error instead of silently expanding to the empty string (#5672, #5868). Thanks, @mfkl! +* `MSB3277` warnings now include information about the assembly identities involved, instead of saying to rerun under higher verbosity (#5798). +* `MSB5009` errors now indicate the project in the solution that is causing the nesting error (#5835). Thanks, @BartoszKlonowski! +* Avoid spawning a process to determine processor architecture (#5897). Thanks, @tmds! +* It's now possible to opt out of culture-name detection of `EmbeddedResource`s, for instance to have a resource named `a.cs.template` (#5824). +* `ProjectInSolution.AbsolutePath` returns a normalized full path when possible (#5949). +* Evaluation pass-stop events now include information about the "size" (number of properties/items/imports) of the project (#5978). Thanks, @arkalyanms! + +#### Fixed + +* `AllowFailureWithoutError` now does what it said it would do (#5743). +* The solution parser now no longer skips projects that are missing an EndProject line (#5808). Thanks, @BartoszKlonowski! +* `ProjectReference`s to `.vcxproj` projects from multi-targeted .NET projects no longer overbuild (#5838). +* Removed unused `InternalsVisibleTo` to obsolete test assemblies (#5914). Thanks, @SingleAccretion! +* Respect conditions when removing all items from an existing list at evaluation time (#5927). +* Common targets should no longer break if the environment variable `OS` is set (#5916). +* Some internal errors will now be reported as errors instead of hanging the build (#5917). +* Item `Update`s are no longer case-sensitive, fixing a regression in MSBuild 16.6 (#5888). +* Use lazy string formatting in more places (#5924). +* Redundant references to MSBuild assemblies no longer fail in 64 MSBuild inline tasks (#5975). +* The `Exec` task will now no longer emit the expanded `Command` to the log on failure (#5962). Thanks, @tmds! +* Tasks generated with `RoslynCodeTaskFactory` now no longer rebuild for every use, even with identical inputs (#5988). Thanks, @KirillOsenkov! +* `ParentBuildEventContext` now includes a parent `MSBuild` task if relevant (#5966). Thanks, @KirillOsenkov! +* Builds that fail because a warning was elevated to an error now report overall failure in the `MSBuild.exe` exit code (#6006). +* Performance of projects with large numbers of consecutive item updates without wildcards improved (#5853). +* Performance improvements in `ResolveAssemblyReferences` (#5973). +* PackageReferences that are marked as development dependencies are removed from the ClickOnce manifest (#6037). +* Stop overfiltering .NET Core assemblies from the ClickOnce manifest (#6080). + +#### Infrastructure + +* The MSBuild codebase now warns for unused `using` statements (#5761). +* The MSBuild codebase is now indexed for [Rich Code Navigation](https://visualstudio.microsoft.com/services/rich-code-navigation/) on CI build (#5790). Thanks, @jepetty! +* The 64-bit bootstrap directory is more usable (#5825). +* Test robustness improvements (#5827, #5944, #5995). +* Make non-shipping NuGet packages compliant (#5823). +* Use [Darc](https://github.com/dotnet/arcade/blob/main/Documentation/Darc.md) to keep bootstrap dependencies up to date (#5909). +* Replace MSBuild.Dev.sln and MSBuild.SourceBuild.sln with solution filters (#6010). +* Minimize and update NuGet feeds (#6019, #6136). + +#### Documentation + +* Improvements to MSBuild-internal Change Wave docs (#5770, #5851). +* High-level documentation for static graph functionality added (#5741). +* Instructions on testing private bits (#5818, #5831). +* XML doc comments updated to match public-ready API docs pages (#6028). Thanks, @ghogen! diff --git a/documentation/ProjectReference-Protocol.md b/documentation/ProjectReference-Protocol.md index f5363b9f43f..5e2a138e3d5 100644 --- a/documentation/ProjectReference-Protocol.md +++ b/documentation/ProjectReference-Protocol.md @@ -58,6 +58,7 @@ If implementing a project with an “outer” (determine what properties to pass * `TargetFrameworks` indicating what TargetFrameworks are available in the project * `TargetFrameworkMonikers` and `TargetPlatformMonikers` indicating what framework / platform the `TargetFrameworks` map to. This is to support implicitly setting the target platform version (for example inferring that `net5.0-windows` means the same as `net5.0-windows7.0`) as well as treating the `TargetFramework` values [as aliases](https://github.com/NuGet/Home/issues/5154) * Boolean metadata for `HasSingleTargetFramework` and `IsRidAgnostic`. + * `Platforms` indicating what platforms are available for the project to build as, and boolean metadata `IsVcxOrNativeProj` (used for [SetPlatform Negotiation](#setplatform-negotiation)) * The `GetReferenceNearestTargetFrameworkTask` (provided by NuGet) is responsible for selecting the best matching `TargetFramework` of the referenced project * This target is _optional_. If not present, the reference will be built with no additional properties. * **New** in MSBuild 15.5. (`TargetFrameworkMonikers` and `TargetPlatformMonikers` metadata is new in MSBuild 16.8) @@ -84,7 +85,6 @@ If implementing a project with an “outer” (determine what properties to pass * As of 15.7, this is _optional_. If a project does not contain a `GetCopyToOutputDirectoryItems` target, projects that reference it will not copy any of its outputs to their own output folders, but the build can succeed. * `Clean` should delete all outputs of the project. * It is not called during a normal build, only during "Clean" and "Rebuild". - ## Other protocol requirements As with all MSBuild logic, targets can be added to do other work with `ProjectReference`s. @@ -106,6 +106,8 @@ As of MSBuild 16.10, it is possible to gather additional properties from referen These properties will then be gathered via the `GetTargetFrameworks` call. They will be available to the referencing project via the `AdditionalPropertiesFromProject` metadata on the `_MSBuildProjectReferenceExistent` item. The `AdditionalPropertiesFromProject` value will be an XML string which contains the values of the properties for each `TargetFramework` in the referenced project. For example: +> :warning: This format is being changed. Soon, the schema will replace with . You can opt into that behavior early by setting the _UseAttributeForTargetFrameworkInfoPropertyNames property to true. This property will have no effect after the transition is complete. + ```xml @@ -119,4 +121,66 @@ These properties will then be gathered via the `GetTargetFrameworks` call. They ``` -The `NearestTargetFramework` metadata will be the target framework which was selected as the best one to use for the reference (via `GetReferenceNearestTargetFrameworkTask`). This can be used to select which set of properties were used in the target framework that was active for the reference. \ No newline at end of file +The `NearestTargetFramework` metadata will be the target framework which was selected as the best one to use for the reference (via `GetReferenceNearestTargetFrameworkTask`). This can be used to select which set of properties were used in the target framework that was active for the reference. + +## SetPlatform Negotiation +As of version 17.0, MSBuild can now dynamically figure out what platform a `ProjectReference` should build as. This includes a new target and task to determine what the `SetPlatform` metadata should be, or whether to undefine the platform so the referenced project builds with its default platform. + +* `_GetProjectReferenceTargetFrameworkProperties` target performs the majority of the work for assigning `SetPlatform` metadata to project references. + * Calls the `GetCompatiblePlatform` task, which is responsible for negotiating between the current project's platform and the platforms of the referenced project to assign a `NearestPlatform` metadata to the item. + * Sets or undefines `SetPlatform` based on the `NearestPlatform` assignment from `GetCompatiblePlatform` + * This target explicitly runs after `_GetProjectReferenceTargetFrameworkProperties` because it needs to use the `IsVcxOrNativeProj` and `Platforms` properties returned by the `GetTargetFrameworks` call. + +Note: If a `ProjectReference` has `SetPlatform` metadata defined already, the negotiation logic is skipped over. +### Impact on the build +In addition to the above task and target, `.vcxproj` and `.nativeproj` projects will receive an extra MSBuild call to the `GetTargetFrameworks` target. Previously, TargetFramework negotiation skipped over these projects because they could not multi-target in the first place. Because SetPlatform negotiation needs information given from the `GetTargetFrameworks` target, it is required that the `_GetProjectReferenceTargetFrameworkProperties` target calls the MSBuild task on the ProjectReference. + +This means most projects will see an evaluation with no global properties defined, unless set by the user. + +### How To Opt In +First, set the properties `EnableDynamicPlatformResolution` and `DisableTransitiveProjectReferences` to `true` for **every project** in your solution. The easiest way to do this is by creating a `Directory.Build.props` file and placing it at the root of your project directory: + +```xml + + + true + true + + +``` + +If only set in one project, the `SetPlatform` metadata will carry forward to every consecutive project reference. + +Next, every referenced project is required to define a `Platforms` property, where `Platforms` is a semicolon-delimited list of platforms that project could build as. For `.vcxproj` or `.nativeproj` projects, `Platforms` is constructed from the `ProjectConfiguration` items that already exist in the project. For managed SDK projects, the default is `AnyCPU`. Managed non-SDK projects need to define this manually. + +Lastly, a `PlatformLookupTable` may need to be defined for more complex scenarios. A `PlatformLookupTable` is a semicolon-delimited list of mappings between platforms. `Win32=x86`, for example. This means that when the current project is building as `Win32`, it will attempt to build the referenced project as x86. This property is **required** when a managed AnyCPU project references an unmanaged project because `AnyCPU` does not directly map to an architecture-specific platform. You can define the table in two ways: + +1. A standard property within the current project, in a Directory.Build.props/targets +2. Metadata on the `ProjectReference` item. This option takes priority over the first to allow customizations per `ProjectReference`. + +### References between managed and unmanaged projects +Some cases of `ProjectReference`s require a `$(PlatformLookupTable)` to correctly determine what a referenced project should build as. References between managed and unmanaged projects also get a default lookup table that can be opted out of by setting the property `UseDefaultPlatformLookupTables` to false. See the table below for details. + +Note: Defining a `PlatformLookupTable` overrides the default mapping. +| Project Reference Type | `PlatformLookupTable` Required? | Notes | +| :-- | :-: | :-: | +| Unmanaged -> Unmanaged | No | | +| Managed -> Managed | No | | +| Unmanaged -> Managed | Optional | Uses default mapping: `Win32=x86` | +| Managed -> Unmanaged | **Yes** when the project is AnyCPU | Uses default mapping: `x86=Win32` | + +Example: +Project A: Managed, building as `AnyCPU`, has a `ProjectReference` on Project B. +Project B: Unmanaged, has `$(Platforms)` constructed from its `Platform` metadata from its `ProjectConfiguration` items, defined as `x64;Win32`. + +Because `AnyCPU` does not map to anything architecture-specific, a custom mapping must be defined. Project A can either: +1. Define `PlatformLookupTable` in its project or a Directory.Build.props as `AnyCPU=x64` or `AnyCPU=Win32`. +2. Define `PlatformLookupTable` as metadata on the `ProjectReference` item, which would take priority over a lookup table defined elsewhere. + * When only one mapping is valid, you could also directly define `SetPlatform` metadata as `Platform=foo` (for unmanaged) or `PlatformTarget=bar` (for managed). This would skip over most negotiation logic. + +Example of project A defining a lookup table directly on the `ProjectReference`: +```xml + + + +``` diff --git a/documentation/specs/project-cache.md b/documentation/specs/project-cache.md index a9da734d26f..6a0095354ca 100644 --- a/documentation/specs/project-cache.md +++ b/documentation/specs/project-cache.md @@ -1,16 +1,3 @@ -- [Summary](#summary) -- [Motivation](#motivation) -- [Plugin requirements](#plugin-requirements) -- [High-level design](#high-level-design) -- [APIs and calling patterns](#apis-and-calling-patterns) - - [From BuildManager API users who have a project dependency graph at hand and want to manually issue builds for each graph node in reverse topo sort order.](#from-buildmanager-api-users-who-have-a-project-dependency-graph-at-hand-and-want-to-manually-issue-builds-for-each-graph-node-in-reverse-topo-sort-order) - - [From command line](#from-command-line) - - [From Visual Studio, a temporary workaround](#from-visual-studio-a-temporary-workaround) -- [Details](#details) -- [Caveats](#caveats) -- [Future work](#future-work) -- [Potential work of dubious value](#potential-work-of-dubious-value) - # Summary Project cache is a new assembly-based plugin extension point in MSBuild which determines whether a build request (a project) can be skipped during build. The main expected benefit is reduced build times via [caching and/or distribution](https://github.com/dotnet/msbuild/blob/master/documentation/specs/static-graph.md#weakness-of-the-old-model-caching-and-distributability). diff --git a/documentation/specs/sdk-resolvers.md b/documentation/specs/sdk-resolvers.md new file mode 100644 index 00000000000..ddb73668e2b --- /dev/null +++ b/documentation/specs/sdk-resolvers.md @@ -0,0 +1,29 @@ +> 🚧 Note +> +> This page is a work in progress. + +### Failed SDK Resolution +SDK resolvers previously attempted to continue when one critically fails (throws an unhandled exception). This lead to misleading error messages such as: + +``` +warning MSB4242: The SDK resolver "Microsoft.DotNet.MSBuildWorkloadSdkResolver" failed to run. 's' is an invalid start of a property name. Expected a '"'. LineNumber: 14 | BytePositionInLine: 8. +error MSB4236: The SDK 'Microsoft.NET.SDK.WorkloadAutoImportPropsLocator' specified could not be found. [C:\foo\bar.csproj] +``` + +`MSB4236` is a red herring while `MSB4242` is the real error despite being logged as a warning. Because of this, SDK resolvers now fail the build _immediately_ upon unhandled exceptions. These exceptions are propogated as `SdkResolverException`s, and `MSB4242` has been promoted to an error code. The new error message appears like so: + +``` +C:\src\temp\8-18>"C:\foo\dotnet-sdk-6.0.100-preview.7.21379.14-win-x64\dotnet.exe" build +Microsoft (R) Build Engine version 17.0.0-dev-21420-01+5df152759 for .NET +Copyright (C) Microsoft Corporation. All rights reserved. + +C:\foo\bar.csproj : error MSB4242: SDK Resolver Failure: "The SDK resolver "Microsoft.DotNet.MSBuildWorkloadSdkResolver" failed while attempting to resolve the SDK "Microsoft.NET.Sdk". Exception: "'s' is an invalid start of a property name. Expected a '"'. LineNumber: 14 | BytePositionInLine: 8."". + +Build FAILED. + +C:\foo\bar.csproj : error MSB4242: SDK Resolver Failure: "The SDK resolver "Microsoft.DotNet.MSBuildWorkloadSdkResolver" failed while attempting to resolve the SDK "Microsoft.NET.Sdk". Exception: "'s' is an invalid start of a property name. Expected a '"'. LineNumber: 14 | BytePositionInLine: 8."". + 0 Warning(s) + 1 Error(s) + +Time Elapsed 00:00:00.15 +``` \ No newline at end of file diff --git a/documentation/specs/static-graph-implementation-details.md b/documentation/specs/static-graph-implementation-details.md index 028fb333359..d8a109a0133 100644 --- a/documentation/specs/static-graph-implementation-details.md +++ b/documentation/specs/static-graph-implementation-details.md @@ -1,8 +1,3 @@ -- [Single project isolated builds: implementation details](#single-project-isolated-builds-implementation-details) - - [Input / Output cache implementation](#input--output-cache-implementation) - - [Isolation implementation](#isolation-implementation) - - [How isolation exemption complicates everything](#how-isolation-exemption-complicates-everything) - # Single project isolated builds: implementation details @@ -17,7 +12,7 @@ The presence of either input or output caches turns on [isolated build constrain ## Input / Output cache implementation -The cache files contain the serialized state of MSBuild's [ConfigCache](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/Components/Caching/ConfigCache.cs) and [ResultsCache](https://github.com/dotnet/msbuild/blob/master/src/Build/BackEnd/Components/Caching/ResultsCache.cs). These two caches have been traditionally used by the engine to cache build results. For example, it is these caches which ensure that a target is only built once per build submission. The `ConfigCache` entries are instances of [BuildRequestConfiguration](https://github.com/microsoft/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs#L25). The `ResultsCache` entries are instances of [BuildResult](https://github.com/microsoft/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/BuildResult.cs#L34), which contain or more instances of [TargetResult](https://github.com/microsoft/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/TargetResult.cs#L22). +The cache files contain the serialized state of MSBuild's [ConfigCache](https://github.com/dotnet/msbuild/blob/main/src/Build/BackEnd/Components/Caching/ConfigCache.cs) and [ResultsCache](https://github.com/dotnet/msbuild/blob/master/src/Build/BackEnd/Components/Caching/ResultsCache.cs). These two caches have been traditionally used by the engine to cache build results. For example, it is these caches which ensure that a target is only built once per build submission. The `ConfigCache` entries are instances of [BuildRequestConfiguration](https://github.com/microsoft/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs#L25). The `ResultsCache` entries are instances of [BuildResult](https://github.com/microsoft/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/BuildResult.cs#L34), which contain or more instances of [TargetResult](https://github.com/microsoft/msbuild/blob/37c5a9fec416b403212a63f95f15b03dbd5e8b5d/src/Build/BackEnd/Shared/TargetResult.cs#L22). One can view the two caches as the following mapping: `(project path, global properties) -> results`. `(project path, global properties)` is represented by a `BuildRequestConfiguration`, and the results are represented by `BuildResult` and `TargetResult`. diff --git a/documentation/specs/static-graph.md b/documentation/specs/static-graph.md index 6e4527a7470..90349da4c7e 100644 --- a/documentation/specs/static-graph.md +++ b/documentation/specs/static-graph.md @@ -1,36 +1,5 @@ # Static Graph -- [Static Graph](#static-graph) - - [What is static graph for?](#what-is-static-graph-for) - - [Weakness of the old model: project-level scheduling](#weakness-of-the-old-model-project-level-scheduling) - - [Weakness of the old model: incrementality](#weakness-of-the-old-model-incrementality) - - [Weakness of the old model: caching and distributability](#weakness-of-the-old-model-caching-and-distributability) - - [What is static graph?](#what-is-static-graph) - - [Design documentation](#design-documentation) - - [Design goals](#design-goals) - - [Project Graph](#project-graph) - - [Constructing the project graph](#constructing-the-project-graph) - - [Build dimensions](#build-dimensions) - - [Multitargeting](#multitargeting) - - [Executing targets on a graph](#executing-targets-on-a-graph) - - [Command line](#command-line) - - [APIs](#apis) - - [Inferring which targets to run for a project within the graph](#inferring-which-targets-to-run-for-a-project-within-the-graph) - - [Multitargeting details](#multitargeting-details) - - [Underspecified graphs](#underspecified-graphs) - - [Public API](#public-api) - - [Isolated builds](#isolated-builds) - - [Isolated graph builds](#isolated-graph-builds) - - [Single project isolated builds](#single-project-isolated-builds) - - [APIs](#apis-1) - - [Command line](#command-line-1) - - [Exempting references from isolation constraints](#exempting-references-from-isolation-constraints) - - [I/O Tracking](#io-tracking) - - [Detours](#detours) - - [Isolation requirement](#isolation-requirement) - - [Tool servers](#tool-servers) - - [Examples](#examples) - ## What is static graph for? As a repo gets bigger and more complex, weaknesses in MSBuild's scheduling and incrementality models become more apparent. MSBuild's static graph features are intended to ameliorate these weaknesses while remaining as compatible as possible with existing projects and SDKs. diff --git a/documentation/wiki/ChangeWaves.md b/documentation/wiki/ChangeWaves.md index 66b119cd485..5b152e7c1a7 100644 --- a/documentation/wiki/ChangeWaves.md +++ b/documentation/wiki/ChangeWaves.md @@ -7,6 +7,9 @@ Opt-out is a better approach for us because we'd likely get limited feedback whe ## How do they work? The opt-out comes in the form of setting the environment variable `MSBuildDisableFeaturesFromVersion` to the Change Wave (or version) that contains the feature you want **disabled**. This version happens to be the version of MSBuild that the features were developed for. See the mapping of change waves to features below. +## When do they become permanent? +A wave of features is set to "rotate out" (i.e. become standard functionality) two bands after its release. For example, wave 16.8 stayed opt-out through wave 16.10, becoming standard functionalty when wave 17.0 is introduced. + ## MSBuildDisableFeaturesFromVersion Values & Outcomes | `MSBuildDisableFeaturesFromVersion` Value | Result | Receive Warning? | | :------------- | :---------- | :----------: | @@ -19,15 +22,19 @@ The opt-out comes in the form of setting the environment variable `MSBuildDisabl # Change Waves & Associated Features ## Current Rotation of Change Waves -### 16.8 -- [Enable NoWarn](https://github.com/dotnet/msbuild/pull/5671) -- [Truncate Target/Task skipped log messages to 1024 chars](https://github.com/dotnet/msbuild/pull/5553) -- [Don't expand full drive globs with false condition](https://github.com/dotnet/msbuild/pull/5669) ### 16.10 - [Error when a property expansion in a condition has whitespace](https://github.com/dotnet/msbuild/pull/5672) - [Allow Custom CopyToOutputDirectory Location With TargetPath](https://github.com/dotnet/msbuild/pull/6237) - [Allow users that have certain special characters in their username to build successfully when using exec](https://github.com/dotnet/msbuild/pull/6223) - [Fail restore operations when an SDK is unresolveable](https://github.com/dotnet/msbuild/pull/6430) ### 17.0 +- [Scheduler should honor BuildParameters.DisableInprocNode](https://github.com/dotnet/msbuild/pull/6400) +- [Don't compile globbing regexes on .NET Framework](https://github.com/dotnet/msbuild/pull/6632) +- [Default to transitively copying content items](https://github.com/dotnet/msbuild/pull/6622) +- [Improve debugging experience: add global switch MSBuildDebugEngine; Inject binary logger from BuildManager; print static graph as .dot file](https://github.com/dotnet/msbuild/pull/6639) ## Change Waves No Longer In Rotation +### 16.8 +- [Enable NoWarn](https://github.com/dotnet/msbuild/pull/5671) +- [Truncate Target/Task skipped log messages to 1024 chars](https://github.com/dotnet/msbuild/pull/5553) +- [Don't expand full drive globs with false condition](https://github.com/dotnet/msbuild/pull/5669) diff --git a/documentation/wiki/Labels.md b/documentation/wiki/Labels.md new file mode 100644 index 00000000000..7f65e10ad9b --- /dev/null +++ b/documentation/wiki/Labels.md @@ -0,0 +1,12 @@ +# MSBuild Labels +Here's a brief explanation on the labels most often used by the MSBuild team excluding hopefully self-evident ones such as `bug`. + +| Label | Applied When | Notes | +|-------------------|--------------|-------| +| `needs-triage` | Team has yet to determine what area/prioritization applies to the issue. | This is the primary label queried during a regular bug triage meeting. Automatically removed when `needs-more-info` is applied. | +| `needs-attention` | An issue requires the team look at it during bug triage. | Automatically applied when a stale issue receives a comment. | +| `needs-more-info` | Team asked for info needed to continue an investigation. | If no response is given within 7 days, the `stale` label is applied. | +| `initial-investigation` | A member of the team does a "first pass" investigation. | `needs-triage` is applied and team member and unassigns themselves after the initial investigation is complete. | +| `stale` | An issue marked with `needs-more-info` is inactive for 7 days. | The issue will be closed after 30 days of inactivity while the `stale` label is applied. | +| `For consideration` | An issue should get higher prioritization when planning the next set of features. | | +| `up-for-grabs` | Anyone can take ownership over this issue. | If a contributor wants to take the issue on, they should ask that it be assigned to them BEFORE doing development work. | diff --git a/documentation/wiki/Localization.md b/documentation/wiki/Localization.md index e0d31a285b8..e58212cd5d9 100644 --- a/documentation/wiki/Localization.md +++ b/documentation/wiki/Localization.md @@ -35,17 +35,8 @@ Code completion ("IntelliSense") for MSBuild project files is provided minimally ### If there is a bug in XSD localization -File xsd localization bugs in this repo. The MSBuild team will coordinate with the Visual Studio localization team to redirect it appropriately. +File XSD localization bugs in this repo. The MSBuild team will coordinate with the Visual Studio localization team to redirect it appropriately. ### When an XSD has been updated -After updating an XSD in the GitHub repo, someone with internal access must update the copy in the `VS` repo. To do so: - -1. Locally clone VS following the standard instructions. -2. Locally update your clone of the GitHub msbuild repo to include the merge of the change. -3. Start a new branch in the VS repository from the current working branch (probably `master`). -4. Copy from the msbuild path `src/MSBuild/MSBuild/*.xsd` to the VS path `src/xmake/XMakeCommandLine`. -5. Ensure that the commit message has a full link to the commit used to update the `.xsd` files, like `https://github.com/microsoft/msbuild/commit/ba9a1d64a7abf15a8505827c00413156a3eb7f62`. -6. Push and submit through the usual VS PR process, including the `MSBuild` team as reviewers. - -Example PR doing this: https://dev.azure.com/devdiv/DevDiv/_git/VS/pullrequest/186890. +After updating an XSD in the GitHub repo, the MSBuild-to-VS-repo insertion process automatically updates the canonical Visual Studio copy of the XSD. diff --git a/eng/AfterSigning.targets b/eng/AfterSigning.targets index e699c81e7dc..0bc6b1db9cd 100644 --- a/eng/AfterSigning.targets +++ b/eng/AfterSigning.targets @@ -1,5 +1,5 @@ - + + + $(MSBuildThisFileDirectory)CodeAnalysis.ruleset + + false + + diff --git a/eng/BootStrapMSBuild.targets b/eng/BootStrapMSBuild.targets index 7855911871a..8eb80f91567 100644 --- a/eng/BootStrapMSBuild.targets +++ b/eng/BootStrapMSBuild.targets @@ -165,7 +165,7 @@ - + @@ -201,10 +201,22 @@ + + + + + + diff --git a/eng/CodeAnalysis.ruleset b/eng/CodeAnalysis.ruleset new file mode 100644 index 00000000000..2078c42fe6c --- /dev/null +++ b/eng/CodeAnalysis.ruleset @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/eng/Packages.props b/eng/Packages.props index 190386ea479..5e7f22432b9 100644 --- a/eng/Packages.props +++ b/eng/Packages.props @@ -15,8 +15,10 @@ + + - + @@ -26,21 +28,23 @@ + + - + - + @@ -50,16 +54,13 @@ - - - - + diff --git a/eng/Signing.props b/eng/Signing.props index 8969d795fee..66347f3d9d1 100644 --- a/eng/Signing.props +++ b/eng/Signing.props @@ -1,5 +1,9 @@ - - - + + + + + + true + \ No newline at end of file diff --git a/eng/SourceBuild.props b/eng/SourceBuild.props new file mode 100644 index 00000000000..d0989ae9c8b --- /dev/null +++ b/eng/SourceBuild.props @@ -0,0 +1,14 @@ + + + + msbuild + true + + + + + $(InnerBuildArgs) /p:Projects="$(InnerSourceBuildRepoRoot)\MSBuild.SourceBuild.slnf" + + + + diff --git a/eng/SourceBuildPrebuiltBaseline.xml b/eng/SourceBuildPrebuiltBaseline.xml new file mode 100644 index 00000000000..c1b6dfbf053 --- /dev/null +++ b/eng/SourceBuildPrebuiltBaseline.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 675da808d71..f1378bc39db 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -1,17 +1,26 @@ - + + https://github.com/dotnet/roslyn-analyzers + + + https://github.com/dotnet/arcade - c58c5dd7f2e9e106368caafb0d4a7a29f4b2e1e9 + 7324320f814152b72295946847ca72413507705a + - + https://github.com/nuget/nuget.client - d525b0e670f3b6cbd5c73a35f04730a9f658c852 + f82431ecc38a28f396d527446834c7de679a6722 - + https://github.com/dotnet/roslyn - e9fd4dc7d74932c0d4b042251bc5a88bb5b3c437 + c1d8c6f043bc80425c6828455eb57f8a404759c6 + + + https://github.com/dotnet/arcade + 7324320f814152b72295946847ca72413507705a diff --git a/eng/Versions.props b/eng/Versions.props index 4f22597a984..24f533d0b07 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -2,7 +2,7 @@ - 16.10.1release + 17.0.0release 15.1.0.0 preview true @@ -15,6 +15,8 @@ true + + true true true true @@ -22,17 +24,26 @@ Can be removed after Arcade moves up. --> 16.7.13 + + + 4.6.0 + + 3.1.400-preview.20365.20 3.1.6 3.1.300-servicing.20216.7 - 0.1.6-prerelease.20175.2 + 0.1.6-prerelease.19380.1 3.1.6-servicing.20316.4 - 5.0.300-servicing.21267.11 - 3.1.100 - 3.9.0-2.20574.26 - 5.9.1-rc.8 + 6.0.100-rtm.21527.8 + $([System.Text.RegularExpressions.Regex]::Match($([System.IO.File]::ReadAllText('$(MSBuildThisFileDirectory)..\global.json')), '"dotnet": "([^"]*)"').Groups.get_Item(1)) + 6.0.0-beta.21460.7 + 3.3.2 + 6.0.0-preview.2.21154.6 + 4.0.0-5.21469.2 + 6.0.0-preview.4.243 + net472 diff --git a/eng/common/msbuild.ps1 b/eng/common/msbuild.ps1 index c6401230002..eea19cd8452 100644 --- a/eng/common/msbuild.ps1 +++ b/eng/common/msbuild.ps1 @@ -5,6 +5,7 @@ Param( [bool] $nodeReuse = $true, [switch] $ci, [switch] $prepareMachine, + [switch] $excludePrereleaseVS, [Parameter(ValueFromRemainingArguments=$true)][String[]]$extraArgs ) diff --git a/eng/common/msbuild.sh b/eng/common/msbuild.sh index 8160cd5a59d..20d3dad5435 100755 --- a/eng/common/msbuild.sh +++ b/eng/common/msbuild.sh @@ -19,7 +19,7 @@ prepare_machine=false extra_args='' while (($# > 0)); do - lowerI="$(echo $1 | awk '{print tolower($0)}')" + lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" case $lowerI in --verbosity) verbosity=$2 diff --git a/eng/common/native/CommonLibrary.psm1 b/eng/common/native/CommonLibrary.psm1 index d7d1a651094..adf707c8fe7 100644 --- a/eng/common/native/CommonLibrary.psm1 +++ b/eng/common/native/CommonLibrary.psm1 @@ -48,7 +48,7 @@ function DownloadAndExtract { -Verbose:$Verbose if ($DownloadStatus -Eq $False) { - Write-Error "Download failed" + Write-Error "Download failed from $Uri" return $False } diff --git a/eng/common/native/common-library.sh b/eng/common/native/common-library.sh index bf272dcf55a..080c2c283ae 100755 --- a/eng/common/native/common-library.sh +++ b/eng/common/native/common-library.sh @@ -148,8 +148,12 @@ function NewScriptShim { fi if [[ ! -f $tool_file_path ]]; then - Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Specified tool file path:'$tool_file_path' does not exist" - return 1 + # try to see if the path is lower cased + tool_file_path="$(echo $tool_file_path | tr "[:upper:]" "[:lower:]")" + if [[ ! -f $tool_file_path ]]; then + Write-PipelineTelemetryError -category 'NativeToolsBootstrap' "Specified tool file path:'$tool_file_path' does not exist" + return 1 + fi fi local shim_contents=$'#!/usr/bin/env bash\n' diff --git a/eng/common/native/install-cmake-test.sh b/eng/common/native/install-cmake-test.sh index 12339a40761..8a5e7cf0db5 100755 --- a/eng/common/native/install-cmake-test.sh +++ b/eng/common/native/install-cmake-test.sh @@ -14,7 +14,7 @@ download_retries=5 retry_wait_time_seconds=30 while (($# > 0)); do - lowerI="$(echo $1 | awk '{print tolower($0)}')" + lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" case $lowerI in --baseuri) base_uri=$2 @@ -63,7 +63,7 @@ done tool_name="cmake-test" tool_os=$(GetCurrentOS) -tool_folder=$(echo $tool_os | awk '{print tolower($0)}') +tool_folder="$(echo $tool_os | tr "[:upper:]" "[:lower:]")" tool_arch="x86_64" tool_name_moniker="$tool_name-$version-$tool_os-$tool_arch" tool_install_directory="$install_path/$tool_name/$version" @@ -114,4 +114,4 @@ if [[ $? != 0 ]]; then exit 1 fi -exit 0 \ No newline at end of file +exit 0 diff --git a/eng/common/native/install-cmake.sh b/eng/common/native/install-cmake.sh index 18041be8763..de496beebc5 100755 --- a/eng/common/native/install-cmake.sh +++ b/eng/common/native/install-cmake.sh @@ -14,7 +14,7 @@ download_retries=5 retry_wait_time_seconds=30 while (($# > 0)); do - lowerI="$(echo $1 | awk '{print tolower($0)}')" + lowerI="$(echo $1 | tr "[:upper:]" "[:lower:]")" case $lowerI in --baseuri) base_uri=$2 @@ -63,7 +63,7 @@ done tool_name="cmake" tool_os=$(GetCurrentOS) -tool_folder=$(echo $tool_os | awk '{print tolower($0)}') +tool_folder="$(echo $tool_os | tr "[:upper:]" "[:lower:]")" tool_arch="x86_64" tool_name_moniker="$tool_name-$version-$tool_os-$tool_arch" tool_install_directory="$install_path/$tool_name/$version" @@ -114,4 +114,4 @@ if [[ $? != 0 ]]; then exit 1 fi -exit 0 \ No newline at end of file +exit 0 diff --git a/eng/common/native/install-tool.ps1 b/eng/common/native/install-tool.ps1 index f397e1c75d4..78f2d84a4e4 100644 --- a/eng/common/native/install-tool.ps1 +++ b/eng/common/native/install-tool.ps1 @@ -105,7 +105,7 @@ try { Write-Error "There are multiple copies of $ToolName in $($ToolInstallDirectory): `n$(@($ToolFilePath | out-string))" exit 1 } elseif (@($ToolFilePath).Length -Lt 1) { - Write-Host "$ToolName was not found in $ToolFilePath." + Write-Host "$ToolName was not found in $ToolInstallDirectory." exit 1 } diff --git a/eng/common/performance/blazor_perf.proj b/eng/common/performance/blazor_perf.proj deleted file mode 100644 index 3b25359c438..00000000000 --- a/eng/common/performance/blazor_perf.proj +++ /dev/null @@ -1,30 +0,0 @@ - - - python3 - $(HelixPreCommands);chmod +x $HELIX_WORKITEM_PAYLOAD/SOD/SizeOnDisk - - - - - %(Identity) - - - - - %HELIX_CORRELATION_PAYLOAD%\performance\src\scenarios\ - $(ScenarioDirectory)blazor\ - - - $HELIX_CORRELATION_PAYLOAD/performance/src/scenarios/ - $(ScenarioDirectory)blazor/ - - - - - $(WorkItemDirectory) - cd $(BlazorDirectory);$(Python) pre.py publish --msbuild %27/p:_TrimmerDumpDependencies=true%27 --msbuild-static AdditionalMonoLinkerOptions=%27"%24(AdditionalMonoLinkerOptions) --dump-dependencies"%27 --binlog %27./traces/blazor_publish.binlog%27 - $(Python) test.py sod --scenario-name "%(Identity)" - $(Python) post.py - - - \ No newline at end of file diff --git a/eng/common/performance/crossgen_perf.proj b/eng/common/performance/crossgen_perf.proj deleted file mode 100644 index 4264920382e..00000000000 --- a/eng/common/performance/crossgen_perf.proj +++ /dev/null @@ -1,69 +0,0 @@ - - - - - %(Identity) - - - - - - py -3 - $(HelixPreCommands) - %HELIX_CORRELATION_PAYLOAD%\Core_Root - %HELIX_CORRELATION_PAYLOAD%\performance\src\scenarios\ - $(ScenarioDirectory)crossgen\ - $(ScenarioDirectory)crossgen2\ - - - python3 - $(HelixPreCommands);chmod +x $HELIX_WORKITEM_PAYLOAD/startup/Startup;chmod +x $HELIX_WORKITEM_PAYLOAD/startup/perfcollect;sudo apt update - $HELIX_CORRELATION_PAYLOAD/Core_Root - $HELIX_CORRELATION_PAYLOAD/performance/src/scenarios/ - $(ScenarioDirectory)crossgen/ - $(ScenarioDirectory)crossgen2/ - - - - - - - - - - - - - - - - $(WorkItemDirectory) - $(Python) $(CrossgenDirectory)test.py crossgen --core-root $(CoreRoot) --test-name %(Identity) - - - - - - $(WorkItemDirectory) - $(Python) $(Crossgen2Directory)test.py crossgen2 --core-root $(CoreRoot) --single %(Identity) - - - - - - - 4:00 - - - - 4:00 - - - $(WorkItemDirectory) - $(Python) $(Crossgen2Directory)test.py crossgen2 --core-root $(CoreRoot) --composite $(Crossgen2Directory)framework-r2r.dll.rsp - 1:00 - - - \ No newline at end of file diff --git a/eng/common/performance/microbenchmarks.proj b/eng/common/performance/microbenchmarks.proj deleted file mode 100644 index ab578e09fa9..00000000000 --- a/eng/common/performance/microbenchmarks.proj +++ /dev/null @@ -1,144 +0,0 @@ - - - - %HELIX_CORRELATION_PAYLOAD%\performance\scripts\benchmarks_ci.py --csproj %HELIX_CORRELATION_PAYLOAD%\performance\$(TargetCsproj) - --dotnet-versions %DOTNET_VERSION% --cli-source-info args --cli-branch %PERFLAB_BRANCH% --cli-commit-sha %PERFLAB_HASH% --cli-repository https://github.com/%PERFLAB_REPO% --cli-source-timestamp %PERFLAB_BUILDTIMESTAMP% - py -3 - %HELIX_CORRELATION_PAYLOAD%\Core_Root\CoreRun.exe - %HELIX_CORRELATION_PAYLOAD%\Baseline_Core_Root\CoreRun.exe - - $(HelixPreCommands);call %HELIX_CORRELATION_PAYLOAD%\performance\tools\machine-setup.cmd;set PYTHONPATH=%HELIX_WORKITEM_PAYLOAD%\scripts%3B%HELIX_WORKITEM_PAYLOAD% - %HELIX_CORRELATION_PAYLOAD%\artifacts\BenchmarkDotNet.Artifacts - %HELIX_CORRELATION_PAYLOAD%\artifacts\BenchmarkDotNet.Artifacts_Baseline - %HELIX_CORRELATION_PAYLOAD%\performance\src\tools\ResultsComparer\ResultsComparer.csproj - %HELIX_CORRELATION_PAYLOAD%\performance\tools\dotnet\$(Architecture)\dotnet.exe - %25%25 - %HELIX_WORKITEM_ROOT%\testResults.xml - - - - $HELIX_CORRELATION_PAYLOAD - $(BaseDirectory)/performance - - - - $HELIX_WORKITEM_PAYLOAD - $(BaseDirectory) - - - - $(PerformanceDirectory)/scripts/benchmarks_ci.py --csproj $(PerformanceDirectory)/$(TargetCsproj) - --dotnet-versions $DOTNET_VERSION --cli-source-info args --cli-branch $PERFLAB_BRANCH --cli-commit-sha $PERFLAB_HASH --cli-repository https://github.com/$PERFLAB_REPO --cli-source-timestamp $PERFLAB_BUILDTIMESTAMP - python3 - $(BaseDirectory)/Core_Root/corerun - $(BaseDirectory)/Baseline_Core_Root/corerun - $(HelixPreCommands);chmod +x $(PerformanceDirectory)/tools/machine-setup.sh;. $(PerformanceDirectory)/tools/machine-setup.sh - $(BaseDirectory)/artifacts/BenchmarkDotNet.Artifacts - $(BaseDirectory)/artifacts/BenchmarkDotNet.Artifacts_Baseline - $(PerformanceDirectory)/src/tools/ResultsComparer/ResultsComparer.csproj - $(PerformanceDirectory)/tools/dotnet/$(Architecture)/dotnet - %25 - $HELIX_WORKITEM_ROOT/testResults.xml - - - - $(CliArguments) --wasm - - - - --corerun %HELIX_CORRELATION_PAYLOAD%\dotnet-mono\shared\Microsoft.NETCore.App\6.0.0\corerun.exe - - - --corerun $(BaseDirectory)/dotnet-mono/shared/Microsoft.NETCore.App/6.0.0/corerun - - - - --corerun $(CoreRun) - - - - --corerun $(BaselineCoreRun) - - - - $(Python) $(WorkItemCommand) --incremental no --architecture $(Architecture) -f $(_Framework) $(PerfLabArguments) - - - - $(WorkItemCommand) $(CliArguments) - - - - 2:30 - 0:15 - - - - - %(Identity) - - - - - 30 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - false - - - - - - $(WorkItemDirectory) - $(WorkItemCommand) --bdn-arguments="--anyCategories $(BDNCategories) $(ExtraBenchmarkDotNetArguments) $(BaselineCoreRunArgument) --artifacts $(BaselineArtifactsDirectory) --partition-count $(PartitionCount) --partition-index %(HelixWorkItem.Index)" - $(WorkItemCommand) --bdn-arguments="--anyCategories $(BDNCategories) $(ExtraBenchmarkDotNetArguments) $(CoreRunArgument) --artifacts $(ArtifactsDirectory) --partition-count $(PartitionCount) --partition-index %(HelixWorkItem.Index)" - $(DotnetExe) run -f $(_Framework) -p $(ResultsComparer) --base $(BaselineArtifactsDirectory) --diff $(ArtifactsDirectory) --threshold 2$(Percent) --xml $(XMLResults);$(FinalCommand) - $(WorkItemTimeout) - - - - - - $(WorkItemDirectory) - $(WorkItemCommand) --bdn-arguments="--anyCategories $(BDNCategories) $(ExtraBenchmarkDotNetArguments) $(BaselineCoreRunArgument) --artifacts $(ArtifactsDirectory)" - $(WorkItemCommand) --bdn-arguments="--anyCategories $(BDNCategories) $(ExtraBenchmarkDotNetArguments) $(CoreRunArgument) --artifacts $(ArtifactsDirectory)" - $(DotnetExe) run -f $(_Framework) -p $(ResultsComparer) --base $(BaselineArtifactsDirectory) --diff $(ArtifactsDirectory) --threshold 2$(Percent) --xml $(XMLResults) - 4:00 - - - diff --git a/eng/common/performance/performance-setup.ps1 b/eng/common/performance/performance-setup.ps1 deleted file mode 100644 index 656c0bd9022..00000000000 --- a/eng/common/performance/performance-setup.ps1 +++ /dev/null @@ -1,147 +0,0 @@ -Param( - [string] $SourceDirectory=$env:BUILD_SOURCESDIRECTORY, - [string] $CoreRootDirectory, - [string] $BaselineCoreRootDirectory, - [string] $Architecture="x64", - [string] $Framework="net5.0", - [string] $CompilationMode="Tiered", - [string] $Repository=$env:BUILD_REPOSITORY_NAME, - [string] $Branch=$env:BUILD_SOURCEBRANCH, - [string] $CommitSha=$env:BUILD_SOURCEVERSION, - [string] $BuildNumber=$env:BUILD_BUILDNUMBER, - [string] $RunCategories="Libraries Runtime", - [string] $Csproj="src\benchmarks\micro\MicroBenchmarks.csproj", - [string] $Kind="micro", - [switch] $LLVM, - [switch] $MonoInterpreter, - [switch] $MonoAOT, - [switch] $Internal, - [switch] $Compare, - [string] $MonoDotnet="", - [string] $Configurations="CompilationMode=$CompilationMode RunKind=$Kind" -) - -$RunFromPerformanceRepo = ($Repository -eq "dotnet/performance") -or ($Repository -eq "dotnet-performance") -$UseCoreRun = ($CoreRootDirectory -ne [string]::Empty) -$UseBaselineCoreRun = ($BaselineCoreRootDirectory -ne [string]::Empty) - -$PayloadDirectory = (Join-Path $SourceDirectory "Payload") -$PerformanceDirectory = (Join-Path $PayloadDirectory "performance") -$WorkItemDirectory = (Join-Path $SourceDirectory "workitem") -$ExtraBenchmarkDotNetArguments = "--iterationCount 1 --warmupCount 0 --invocationCount 1 --unrollFactor 1 --strategy ColdStart --stopOnFirstError true" -$Creator = $env:BUILD_DEFINITIONNAME -$PerfLabArguments = "" -$HelixSourcePrefix = "pr" - -$Queue = "Windows.10.Amd64.ClientRS4.DevEx.15.8.Open" - -# TODO: Implement a better logic to determine if Framework is .NET Core or >= .NET 5. -if ($Framework.StartsWith("netcoreapp") -or ($Framework -eq "net5.0")) { - $Queue = "Windows.10.Amd64.ClientRS5.Open" -} - -if ($Compare) { - $Queue = "Windows.10.Amd64.19H1.Tiger.Perf.Open" - $PerfLabArguments = "" - $ExtraBenchmarkDotNetArguments = "" -} - -if ($Internal) { - $Queue = "Windows.10.Amd64.19H1.Tiger.Perf" - $PerfLabArguments = "--upload-to-perflab-container" - $ExtraBenchmarkDotNetArguments = "" - $Creator = "" - $HelixSourcePrefix = "official" -} - -if($MonoInterpreter) -{ - $ExtraBenchmarkDotNetArguments = "--category-exclusion-filter NoInterpreter" -} - -if($MonoDotnet -ne "") -{ - $Configurations += " LLVM=$LLVM MonoInterpreter=$MonoInterpreter MonoAOT=$MonoAOT" - if($ExtraBenchmarkDotNetArguments -eq "") - { - #FIX ME: We need to block these tests as they don't run on mono for now - $ExtraBenchmarkDotNetArguments = "--exclusion-filter *Perf_Image* *Perf_NamedPipeStream*" - } - else - { - #FIX ME: We need to block these tests as they don't run on mono for now - $ExtraBenchmarkDotNetArguments += " --exclusion-filter *Perf_Image* *Perf_NamedPipeStream*" - } -} - -# FIX ME: This is a workaround until we get this from the actual pipeline -$CommonSetupArguments="--channel master --queue $Queue --build-number $BuildNumber --build-configs $Configurations --architecture $Architecture" -$SetupArguments = "--repository https://github.com/$Repository --branch $Branch --get-perf-hash --commit-sha $CommitSha $CommonSetupArguments" - - -#This grabs the LKG version number of dotnet and passes it to our scripts -$VersionJSON = Get-Content global.json | ConvertFrom-Json -$DotNetVersion = $VersionJSON.tools.dotnet -$SetupArguments = "--dotnet-versions $DotNetVersion $SetupArguments" - - -if ($RunFromPerformanceRepo) { - $SetupArguments = "--perf-hash $CommitSha $CommonSetupArguments" - - robocopy $SourceDirectory $PerformanceDirectory /E /XD $PayloadDirectory $SourceDirectory\artifacts $SourceDirectory\.git -} -else { - git clone --branch master --depth 1 --quiet https://github.com/dotnet/performance $PerformanceDirectory -} - -if($MonoDotnet -ne "") -{ - $UsingMono = "true" - $MonoDotnetPath = (Join-Path $PayloadDirectory "dotnet-mono") - Move-Item -Path $MonoDotnet -Destination $MonoDotnetPath -} - -if ($UseCoreRun) { - $NewCoreRoot = (Join-Path $PayloadDirectory "Core_Root") - Move-Item -Path $CoreRootDirectory -Destination $NewCoreRoot -} -if ($UseBaselineCoreRun) { - $NewBaselineCoreRoot = (Join-Path $PayloadDirectory "Baseline_Core_Root") - Move-Item -Path $BaselineCoreRootDirectory -Destination $NewBaselineCoreRoot -} - -$DocsDir = (Join-Path $PerformanceDirectory "docs") -robocopy $DocsDir $WorkItemDirectory - -# Set variables that we will need to have in future steps -$ci = $true - -. "$PSScriptRoot\..\pipeline-logging-functions.ps1" - -# Directories -Write-PipelineSetVariable -Name 'PayloadDirectory' -Value "$PayloadDirectory" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'PerformanceDirectory' -Value "$PerformanceDirectory" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'WorkItemDirectory' -Value "$WorkItemDirectory" -IsMultiJobVariable $false - -# Script Arguments -Write-PipelineSetVariable -Name 'Python' -Value "py -3" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'ExtraBenchmarkDotNetArguments' -Value "$ExtraBenchmarkDotNetArguments" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'SetupArguments' -Value "$SetupArguments" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'PerfLabArguments' -Value "$PerfLabArguments" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'BDNCategories' -Value "$RunCategories" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'TargetCsproj' -Value "$Csproj" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'Kind' -Value "$Kind" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'Architecture' -Value "$Architecture" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'UseCoreRun' -Value "$UseCoreRun" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'UseBaselineCoreRun' -Value "$UseBaselineCoreRun" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'RunFromPerfRepo' -Value "$RunFromPerformanceRepo" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'Compare' -Value "$Compare" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'MonoDotnet' -Value "$UsingMono" -IsMultiJobVariable $false - -# Helix Arguments -Write-PipelineSetVariable -Name 'Creator' -Value "$Creator" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'Queue' -Value "$Queue" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name 'HelixSourcePrefix' -Value "$HelixSourcePrefix" -IsMultiJobVariable $false -Write-PipelineSetVariable -Name '_BuildConfig' -Value "$Architecture.$Kind.$Framework" -IsMultiJobVariable $false - -exit 0 \ No newline at end of file diff --git a/eng/common/performance/performance-setup.sh b/eng/common/performance/performance-setup.sh deleted file mode 100644 index 99d1b7bc1fc..00000000000 --- a/eng/common/performance/performance-setup.sh +++ /dev/null @@ -1,289 +0,0 @@ -#!/usr/bin/env bash - -source_directory=$BUILD_SOURCESDIRECTORY -core_root_directory= -baseline_core_root_directory= -architecture=x64 -framework=net5.0 -compilation_mode=tiered -repository=$BUILD_REPOSITORY_NAME -branch=$BUILD_SOURCEBRANCH -commit_sha=$BUILD_SOURCEVERSION -build_number=$BUILD_BUILDNUMBER -internal=false -compare=false -mono_dotnet= -kind="micro" -llvm=false -monointerpreter=false -monoaot=false -run_categories="Libraries Runtime" -csproj="src\benchmarks\micro\MicroBenchmarks.csproj" -configurations="CompliationMode=$compilation_mode RunKind=$kind" -run_from_perf_repo=false -use_core_run=true -use_baseline_core_run=true -using_mono=false -wasm_runtime_loc= -using_wasm=false -use_latest_dotnet=false - -while (($# > 0)); do - lowerI="$(echo $1 | awk '{print tolower($0)}')" - case $lowerI in - --sourcedirectory) - source_directory=$2 - shift 2 - ;; - --corerootdirectory) - core_root_directory=$2 - shift 2 - ;; - --baselinecorerootdirectory) - baseline_core_root_directory=$2 - shift 2 - ;; - --architecture) - architecture=$2 - shift 2 - ;; - --framework) - framework=$2 - shift 2 - ;; - --compilationmode) - compilation_mode=$2 - shift 2 - ;; - --repository) - repository=$2 - shift 2 - ;; - --branch) - branch=$2 - shift 2 - ;; - --commitsha) - commit_sha=$2 - shift 2 - ;; - --buildnumber) - build_number=$2 - shift 2 - ;; - --kind) - kind=$2 - configurations="CompilationMode=$compilation_mode RunKind=$kind" - shift 2 - ;; - --runcategories) - run_categories=$2 - shift 2 - ;; - --csproj) - csproj=$2 - shift 2 - ;; - --internal) - internal=true - shift 1 - ;; - --llvm) - llvm=true - shift 1 - ;; - --monointerpreter) - monointerpreter=true - shift 1 - ;; - --monoaot) - monoaot=true - shift 1 - ;; - --monodotnet) - mono_dotnet=$2 - shift 2 - ;; - --wasm) - wasm_runtime_loc=$2 - shift 2 - ;; - --compare) - compare=true - shift 1 - ;; - --configurations) - configurations=$2 - shift 2 - ;; - --latestdotnet) - use_latest_dotnet=true - shift 1 - ;; - *) - echo "Common settings:" - echo " --corerootdirectory Directory where Core_Root exists, if running perf testing with --corerun" - echo " --architecture Architecture of the testing being run" - echo " --configurations List of key=value pairs that will be passed to perf testing infrastructure." - echo " ex: --configurations \"CompilationMode=Tiered OptimzationLevel=PGO\"" - echo " --help Print help and exit" - echo "" - echo "Advanced settings:" - echo " --framework The framework to run, if not running in master" - echo " --compliationmode The compilation mode if not passing --configurations" - echo " --sourcedirectory The directory of the sources. Defaults to env:BUILD_SOURCESDIRECTORY" - echo " --repository The name of the repository in the / format. Defaults to env:BUILD_REPOSITORY_NAME" - echo " --branch The name of the branch. Defaults to env:BUILD_SOURCEBRANCH" - echo " --commitsha The commit sha1 to run against. Defaults to env:BUILD_SOURCEVERSION" - echo " --buildnumber The build number currently running. Defaults to env:BUILD_BUILDNUMBER" - echo " --csproj The relative path to the benchmark csproj whose tests should be run. Defaults to src\benchmarks\micro\MicroBenchmarks.csproj" - echo " --kind Related to csproj. The kind of benchmarks that should be run. Defaults to micro" - echo " --runcategories Related to csproj. Categories of benchmarks to run. Defaults to \"coreclr corefx\"" - echo " --internal If the benchmarks are running as an official job." - echo " --monodotnet Pass the path to the mono dotnet for mono performance testing." - echo " --wasm Path to the unpacked wasm runtime pack." - echo " --latestdotnet --dotnet-versions will not be specified. --dotnet-versions defaults to LKG version in global.json " - echo "" - exit 0 - ;; - esac -done - -if [ "$repository" == "dotnet/performance" ] || [ "$repository" == "dotnet-performance" ]; then - run_from_perf_repo=true -fi - -if [ -z "$configurations" ]; then - configurations="CompilationMode=$compilation_mode" -fi - -if [ -z "$core_root_directory" ]; then - use_core_run=false -fi - -if [ -z "$baseline_core_root_directory" ]; then - use_baseline_core_run=false -fi - -payload_directory=$source_directory/Payload -performance_directory=$payload_directory/performance -workitem_directory=$source_directory/workitem -extra_benchmark_dotnet_arguments="--iterationCount 1 --warmupCount 0 --invocationCount 1 --unrollFactor 1 --strategy ColdStart --stopOnFirstError true" -perflab_arguments= -queue=Ubuntu.1804.Amd64.Open -creator=$BUILD_DEFINITIONNAME -helix_source_prefix="pr" - -if [[ "$compare" == true ]]; then - extra_benchmark_dotnet_arguments= - perflab_arguments= - - # No open queues for arm64 - if [[ "$architecture" = "arm64" ]]; then - echo "Compare not available for arm64" - exit 1 - fi - - queue=Ubuntu.1804.Amd64.Tiger.Perf.Open -fi - -if [[ "$internal" == true ]]; then - perflab_arguments="--upload-to-perflab-container" - helix_source_prefix="official" - creator= - extra_benchmark_dotnet_arguments= - - if [[ "$architecture" = "arm64" ]]; then - queue=Ubuntu.1804.Arm64.Perf - else - queue=Ubuntu.1804.Amd64.Tiger.Perf - fi -fi - -if [[ "$mono_dotnet" != "" ]] && [[ "$monointerpreter" == "false" ]]; then - extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --category-exclusion-filter NoMono" -fi - -if [[ "$wasm_runtime_loc" != "" ]]; then - configurations="CompilationMode=wasm RunKind=$kind" - extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --category-exclusion-filter NoInterpreter NoWASM NoMono" -fi - -if [[ "$mono_dotnet" != "" ]] && [[ "$monointerpreter" == "true" ]]; then - configurations="$configurations LLVM=$llvm MonoInterpreter=$monointerpreter MonoAOT=$monoaot" - extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --category-exclusion-filter NoInterpreter NoMono" -fi - -common_setup_arguments="--channel master --queue $queue --build-number $build_number --build-configs $configurations --architecture $architecture" -setup_arguments="--repository https://github.com/$repository --branch $branch --get-perf-hash --commit-sha $commit_sha $common_setup_arguments" - - -if [[ "$use_latest_dotnet" = false ]]; then - # Get the tools section from the global.json. - # This grabs the LKG version number of dotnet and passes it to our scripts - dotnet_version=`cat global.json | python3 -c 'import json,sys;obj=json.load(sys.stdin);print(obj["tools"]["dotnet"])'` - setup_arguments="--dotnet-versions $dotnet_version $setup_arguments" -fi - -if [[ "$run_from_perf_repo" = true ]]; then - payload_directory= - workitem_directory=$source_directory - performance_directory=$workitem_directory - setup_arguments="--perf-hash $commit_sha $common_setup_arguments" -else - git clone --branch master --depth 1 --quiet https://github.com/dotnet/performance $performance_directory - - docs_directory=$performance_directory/docs - mv $docs_directory $workitem_directory -fi - -if [[ "$wasm_runtime_loc" != "" ]]; then - using_wasm=true - wasm_dotnet_path=$payload_directory/dotnet-wasm - mv $wasm_runtime_loc $wasm_dotnet_path - extra_benchmark_dotnet_arguments="$extra_benchmark_dotnet_arguments --wasmMainJS \$HELIX_CORRELATION_PAYLOAD/dotnet-wasm/runtime-test.js --wasmEngine /home/helixbot/.jsvu/v8 --customRuntimePack \$HELIX_CORRELATION_PAYLOAD/dotnet-wasm" -fi - -if [[ "$mono_dotnet" != "" ]]; then - using_mono=true - mono_dotnet_path=$payload_directory/dotnet-mono - mv $mono_dotnet $mono_dotnet_path -fi - -if [[ "$use_core_run" = true ]]; then - new_core_root=$payload_directory/Core_Root - mv $core_root_directory $new_core_root -fi - -if [[ "$use_baseline_core_run" = true ]]; then - new_baseline_core_root=$payload_directory/Baseline_Core_Root - mv $baseline_core_root_directory $new_baseline_core_root -fi - -ci=true - -_script_dir=$(pwd)/eng/common -. "$_script_dir/pipeline-logging-functions.sh" - -# Make sure all of our variables are available for future steps -Write-PipelineSetVariable -name "UseCoreRun" -value "$use_core_run" -is_multi_job_variable false -Write-PipelineSetVariable -name "UseBaselineCoreRun" -value "$use_baseline_core_run" -is_multi_job_variable false -Write-PipelineSetVariable -name "Architecture" -value "$architecture" -is_multi_job_variable false -Write-PipelineSetVariable -name "PayloadDirectory" -value "$payload_directory" -is_multi_job_variable false -Write-PipelineSetVariable -name "PerformanceDirectory" -value "$performance_directory" -is_multi_job_variable false -Write-PipelineSetVariable -name "WorkItemDirectory" -value "$workitem_directory" -is_multi_job_variable false -Write-PipelineSetVariable -name "Queue" -value "$queue" -is_multi_job_variable false -Write-PipelineSetVariable -name "SetupArguments" -value "$setup_arguments" -is_multi_job_variable false -Write-PipelineSetVariable -name "Python" -value "python3" -is_multi_job_variable false -Write-PipelineSetVariable -name "PerfLabArguments" -value "$perflab_arguments" -is_multi_job_variable false -Write-PipelineSetVariable -name "ExtraBenchmarkDotNetArguments" -value "$extra_benchmark_dotnet_arguments" -is_multi_job_variable false -Write-PipelineSetVariable -name "BDNCategories" -value "$run_categories" -is_multi_job_variable false -Write-PipelineSetVariable -name "TargetCsproj" -value "$csproj" -is_multi_job_variable false -Write-PipelineSetVariable -name "RunFromPerfRepo" -value "$run_from_perf_repo" -is_multi_job_variable false -Write-PipelineSetVariable -name "Creator" -value "$creator" -is_multi_job_variable false -Write-PipelineSetVariable -name "HelixSourcePrefix" -value "$helix_source_prefix" -is_multi_job_variable false -Write-PipelineSetVariable -name "Kind" -value "$kind" -is_multi_job_variable false -Write-PipelineSetVariable -name "_BuildConfig" -value "$architecture.$kind.$framework" -is_multi_job_variable false -Write-PipelineSetVariable -name "Compare" -value "$compare" -is_multi_job_variable false -Write-PipelineSetVariable -name "MonoDotnet" -value "$using_mono" -is_multi_job_variable false -Write-PipelineSetVariable -name "WasmDotnet" -value "$using_wasm" -is_multi_job_variable false diff --git a/eng/common/pipeline-logging-functions.ps1 b/eng/common/pipeline-logging-functions.ps1 index 8484451f3a5..8e422c561e4 100644 --- a/eng/common/pipeline-logging-functions.ps1 +++ b/eng/common/pipeline-logging-functions.ps1 @@ -29,14 +29,14 @@ function Write-PipelineTelemetryError { [switch]$AsOutput, [switch]$Force) - $PSBoundParameters.Remove('Category') | Out-Null + $PSBoundParameters.Remove('Category') | Out-Null - if($Force -Or ((Test-Path variable:ci) -And $ci)) { - $Message = "(NETCORE_ENGINEERING_TELEMETRY=$Category) $Message" - } - $PSBoundParameters.Remove('Message') | Out-Null - $PSBoundParameters.Add('Message', $Message) - Write-PipelineTaskError @PSBoundParameters + if ($Force -Or ((Test-Path variable:ci) -And $ci)) { + $Message = "(NETCORE_ENGINEERING_TELEMETRY=$Category) $Message" + } + $PSBoundParameters.Remove('Message') | Out-Null + $PSBoundParameters.Add('Message', $Message) + Write-PipelineTaskError @PSBoundParameters } # Specify "-Force" to force pipeline formatted output even if "$ci" is false or not set @@ -55,8 +55,8 @@ function Write-PipelineTaskError { [switch]$Force ) - if(!$Force -And (-Not (Test-Path variable:ci) -Or !$ci)) { - if($Type -eq 'error') { + if (!$Force -And (-Not (Test-Path variable:ci) -Or !$ci)) { + if ($Type -eq 'error') { Write-Host $Message -ForegroundColor Red return } @@ -66,47 +66,61 @@ function Write-PipelineTaskError { } } - if(($Type -ne 'error') -and ($Type -ne 'warning')) { + if (($Type -ne 'error') -and ($Type -ne 'warning')) { Write-Host $Message return } $PSBoundParameters.Remove('Force') | Out-Null - if(-not $PSBoundParameters.ContainsKey('Type')) { + if (-not $PSBoundParameters.ContainsKey('Type')) { $PSBoundParameters.Add('Type', 'error') } Write-LogIssue @PSBoundParameters - } +} - function Write-PipelineSetVariable { +function Write-PipelineSetVariable { [CmdletBinding()] param( - [Parameter(Mandatory = $true)] - [string]$Name, - [string]$Value, - [switch]$Secret, - [switch]$AsOutput, - [bool]$IsMultiJobVariable=$true) - - if((Test-Path variable:ci) -And $ci) { + [Parameter(Mandatory = $true)] + [string]$Name, + [string]$Value, + [switch]$Secret, + [switch]$AsOutput, + [bool]$IsMultiJobVariable = $true) + + if ((Test-Path variable:ci) -And $ci) { Write-LoggingCommand -Area 'task' -Event 'setvariable' -Data $Value -Properties @{ - 'variable' = $Name - 'isSecret' = $Secret - 'isOutput' = $IsMultiJobVariable + 'variable' = $Name + 'isSecret' = $Secret + 'isOutput' = $IsMultiJobVariable } -AsOutput:$AsOutput - } - } + } +} - function Write-PipelinePrependPath { +function Write-PipelinePrependPath { [CmdletBinding()] param( - [Parameter(Mandatory=$true)] - [string]$Path, - [switch]$AsOutput) + [Parameter(Mandatory = $true)] + [string]$Path, + [switch]$AsOutput) - if((Test-Path variable:ci) -And $ci) { + if ((Test-Path variable:ci) -And $ci) { Write-LoggingCommand -Area 'task' -Event 'prependpath' -Data $Path -AsOutput:$AsOutput - } - } + } +} + +function Write-PipelineSetResult { + [CmdletBinding()] + param( + [ValidateSet("Succeeded", "SucceededWithIssues", "Failed", "Cancelled", "Skipped")] + [Parameter(Mandatory = $true)] + [string]$Result, + [string]$Message) + if ((Test-Path variable:ci) -And $ci) { + Write-LoggingCommand -Area 'task' -Event 'complete' -Data $Message -Properties @{ + 'result' = $Result + } + } +} <######################################## # Private functions. @@ -123,7 +137,8 @@ function Format-LoggingCommandData { foreach ($mapping in $script:loggingCommandEscapeMappings) { $Value = $Value.Replace($mapping.Token, $mapping.Replacement) } - } else { + } + else { for ($i = $script:loggingCommandEscapeMappings.Length - 1 ; $i -ge 0 ; $i--) { $mapping = $script:loggingCommandEscapeMappings[$i] $Value = $Value.Replace($mapping.Replacement, $mapping.Token) @@ -156,7 +171,8 @@ function Format-LoggingCommand { if ($first) { $null = $sb.Append(' ') $first = $false - } else { + } + else { $null = $sb.Append(';') } @@ -193,7 +209,8 @@ function Write-LoggingCommand { $command = Format-LoggingCommand -Area $Area -Event $Event -Data $Data -Properties $Properties if ($AsOutput) { $command - } else { + } + else { Write-Host $command } } @@ -212,12 +229,12 @@ function Write-LogIssue { [switch]$AsOutput) $command = Format-LoggingCommand -Area 'task' -Event 'logissue' -Data $Message -Properties @{ - 'type' = $Type - 'code' = $ErrCode - 'sourcepath' = $SourcePath - 'linenumber' = $LineNumber - 'columnnumber' = $ColumnNumber - } + 'type' = $Type + 'code' = $ErrCode + 'sourcepath' = $SourcePath + 'linenumber' = $LineNumber + 'columnnumber' = $ColumnNumber + } if ($AsOutput) { return $command } @@ -229,7 +246,8 @@ function Write-LogIssue { $foregroundColor = [System.ConsoleColor]::Red $backgroundColor = [System.ConsoleColor]::Black } - } else { + } + else { $foregroundColor = $host.PrivateData.WarningForegroundColor $backgroundColor = $host.PrivateData.WarningBackgroundColor if ($foregroundColor -isnot [System.ConsoleColor] -or $backgroundColor -isnot [System.ConsoleColor]) { diff --git a/eng/common/pipeline-logging-functions.sh b/eng/common/pipeline-logging-functions.sh index 6cd0a3400e6..6a0b2255e91 100644 --- a/eng/common/pipeline-logging-functions.sh +++ b/eng/common/pipeline-logging-functions.sh @@ -6,7 +6,7 @@ function Write-PipelineTelemetryError { local function_args=() local message='' while [[ $# -gt 0 ]]; do - opt="$(echo "${1/#--/-}" | awk '{print tolower($0)}')" + opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" case "$opt" in -category|-c) telemetry_category=$2 @@ -48,7 +48,7 @@ function Write-PipelineTaskError { local force=false while [[ $# -gt 0 ]]; do - opt="$(echo "${1/#--/-}" | awk '{print tolower($0)}')" + opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" case "$opt" in -type|-t) message_type=$2 @@ -122,7 +122,7 @@ function Write-PipelineSetVariable { local is_multi_job_variable=true while [[ $# -gt 0 ]]; do - opt="$(echo "${1/#--/-}" | awk '{print tolower($0)}')" + opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" case "$opt" in -name|-n) name=$2 @@ -164,7 +164,7 @@ function Write-PipelinePrependPath { local prepend_path='' while [[ $# -gt 0 ]]; do - opt="$(echo "${1/#--/-}" | awk '{print tolower($0)}')" + opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" case "$opt" in -path|-p) prepend_path=$2 @@ -179,4 +179,28 @@ function Write-PipelinePrependPath { if [[ "$ci" == true ]]; then echo "##vso[task.prependpath]$prepend_path" fi -} \ No newline at end of file +} + +function Write-PipelineSetResult { + local result='' + local message='' + + while [[ $# -gt 0 ]]; do + opt="$(echo "${1/#--/-}" | tr "[:upper:]" "[:lower:]")" + case "$opt" in + -result|-r) + result=$2 + shift + ;; + -message|-m) + message=$2 + shift + ;; + esac + shift + done + + if [[ "$ci" == true ]]; then + echo "##vso[task.complete result=$result;]$message" + fi +} diff --git a/eng/common/post-build/post-build-utils.ps1 b/eng/common/post-build/post-build-utils.ps1 index 7d49744795f..534f6988d5b 100644 --- a/eng/common/post-build/post-build-utils.ps1 +++ b/eng/common/post-build/post-build-utils.ps1 @@ -69,9 +69,9 @@ function Trigger-Subscription([string]$SubscriptionId) { function Validate-MaestroVars { try { - Get-Variable MaestroApiEndPoint -Scope Global | Out-Null - Get-Variable MaestroApiVersion -Scope Global | Out-Null - Get-Variable MaestroApiAccessToken -Scope Global | Out-Null + Get-Variable MaestroApiEndPoint | Out-Null + Get-Variable MaestroApiVersion | Out-Null + Get-Variable MaestroApiAccessToken | Out-Null if (!($MaestroApiEndPoint -Match '^http[s]?://maestro-(int|prod).westus2.cloudapp.azure.com$')) { Write-PipelineTelemetryError -Category 'MaestroVars' -Message "MaestroApiEndPoint is not a valid Maestro URL. '$MaestroApiEndPoint'" diff --git a/eng/common/post-build/publish-using-darc.ps1 b/eng/common/post-build/publish-using-darc.ps1 index a40ee827a43..2427ca6b6ae 100644 --- a/eng/common/post-build/publish-using-darc.ps1 +++ b/eng/common/post-build/publish-using-darc.ps1 @@ -10,21 +10,27 @@ param( [Parameter(Mandatory=$false)][string] $EnableNugetValidation, [Parameter(Mandatory=$false)][string] $PublishInstallersAndChecksums, [Parameter(Mandatory=$false)][string] $ArtifactsPublishingAdditionalParameters, + [Parameter(Mandatory=$false)][string] $SymbolPublishingAdditionalParameters, [Parameter(Mandatory=$false)][string] $SigningValidationAdditionalParameters ) try { . $PSScriptRoot\post-build-utils.ps1 - # Hard coding darc version till the next arcade-services roll out, cos this version has required API changes for darc add-build-to-channel - $darc = Get-Darc "1.1.0-beta.20418.1" + + $darc = Get-Darc $optionalParams = [System.Collections.ArrayList]::new() if ("" -ne $ArtifactsPublishingAdditionalParameters) { - $optionalParams.Add("artifact-publishing-parameters") | Out-Null + $optionalParams.Add("--artifact-publishing-parameters") | Out-Null $optionalParams.Add($ArtifactsPublishingAdditionalParameters) | Out-Null } + if ("" -ne $SymbolPublishingAdditionalParameters) { + $optionalParams.Add("--symbol-publishing-parameters") | Out-Null + $optionalParams.Add($SymbolPublishingAdditionalParameters) | Out-Null + } + if ("false" -eq $WaitPublishingFinish) { $optionalParams.Add("--no-wait") | Out-Null } diff --git a/eng/common/post-build/sourcelink-validation.ps1 b/eng/common/post-build/sourcelink-validation.ps1 index c7e7ae67d81..e8ab29afeb3 100644 --- a/eng/common/post-build/sourcelink-validation.ps1 +++ b/eng/common/post-build/sourcelink-validation.ps1 @@ -14,7 +14,10 @@ param( $global:RepoFiles = @{} # Maximum number of jobs to run in parallel -$MaxParallelJobs = 6 +$MaxParallelJobs = 16 + +$MaxRetries = 5 +$RetryWaitTimeInSeconds = 30 # Wait time between check for system load $SecondsBetweenLoadChecks = 10 @@ -29,7 +32,10 @@ $ValidatePackage = { # Ensure input file exist if (!(Test-Path $PackagePath)) { Write-Host "Input file does not exist: $PackagePath" - return 1 + return [pscustomobject]@{ + result = 1 + packagePath = $PackagePath + } } # Extensions for which we'll look for SourceLink information @@ -59,7 +65,10 @@ $ValidatePackage = { # We ignore resource DLLs if ($FileName.EndsWith('.resources.dll')) { - return + return [pscustomobject]@{ + result = 0 + packagePath = $PackagePath + } } [System.IO.Compression.ZipFileExtensions]::ExtractToFile($_, $TargetFile, $true) @@ -91,36 +100,59 @@ $ValidatePackage = { $Status = 200 $Cache = $using:RepoFiles - if ( !($Cache.ContainsKey($FilePath)) ) { - try { - $Uri = $Link -as [System.URI] - - # Only GitHub links are valid - if ($Uri.AbsoluteURI -ne $null -and ($Uri.Host -match 'github' -or $Uri.Host -match 'githubusercontent')) { - $Status = (Invoke-WebRequest -Uri $Link -UseBasicParsing -Method HEAD -TimeoutSec 5).StatusCode + $attempts = 0 + + while ($attempts -lt $using:MaxRetries) { + if ( !($Cache.ContainsKey($FilePath)) ) { + try { + $Uri = $Link -as [System.URI] + + if ($Link -match "submodules") { + # Skip submodule links until sourcelink properly handles submodules + $Status = 200 + } + elseif ($Uri.AbsoluteURI -ne $null -and ($Uri.Host -match 'github' -or $Uri.Host -match 'githubusercontent')) { + # Only GitHub links are valid + $Status = (Invoke-WebRequest -Uri $Link -UseBasicParsing -Method HEAD -TimeoutSec 5).StatusCode + } + else { + # If it's not a github link, we want to break out of the loop and not retry. + $Status = 0 + $attempts = $using:MaxRetries + } } - else { + catch { + Write-Host $_ $Status = 0 } } - catch { - write-host $_ - $Status = 0 - } - } - if ($Status -ne 200) { - if ($NumFailedLinks -eq 0) { - if ($FailedFiles.Value -eq 0) { - Write-Host + if ($Status -ne 200) { + $attempts++ + + if ($attempts -lt $using:MaxRetries) + { + $attemptsLeft = $using:MaxRetries - $attempts + Write-Warning "Download failed, $attemptsLeft attempts remaining, will retry in $using:RetryWaitTimeInSeconds seconds" + Start-Sleep -Seconds $using:RetryWaitTimeInSeconds + } + else { + if ($NumFailedLinks -eq 0) { + if ($FailedFiles.Value -eq 0) { + Write-Host + } + + Write-Host "`tFile $RealPath has broken links:" + } + + Write-Host "`t`tFailed to retrieve $Link" + + $NumFailedLinks++ } - - Write-Host "`tFile $RealPath has broken links:" } - - Write-Host "`t`tFailed to retrieve $Link" - - $NumFailedLinks++ + else { + break + } } } } @@ -136,7 +168,7 @@ $ValidatePackage = { } } catch { - + Write-Host $_ } finally { $zip.Dispose() @@ -161,9 +193,12 @@ $ValidatePackage = { function CheckJobResult( $result, $packagePath, - [ref]$ValidationFailures) { - if ($jobResult.result -ne '0') { - Write-PipelineTelemetryError -Category 'SourceLink' -Message "$packagePath has broken SourceLink links." + [ref]$ValidationFailures, + [switch]$logErrors) { + if ($result -ne '0') { + if ($logErrors) { + Write-PipelineTelemetryError -Category 'SourceLink' -Message "$packagePath has broken SourceLink links." + } $ValidationFailures.Value++ } } @@ -217,6 +252,7 @@ function ValidateSourceLinkLinks { # Process each NuGet package in parallel Get-ChildItem "$InputPath\*.symbols.nupkg" | ForEach-Object { + Write-Host "Starting $($_.FullName)" Start-Job -ScriptBlock $ValidatePackage -ArgumentList $_.FullName | Out-Null $NumJobs = @(Get-Job -State 'Running').Count @@ -228,16 +264,14 @@ function ValidateSourceLinkLinks { foreach ($Job in @(Get-Job -State 'Completed')) { $jobResult = Wait-Job -Id $Job.Id | Receive-Job - CheckJobResult $jobResult.result $jobResult.packagePath ([ref]$ValidationFailures) + CheckJobResult $jobResult.result $jobResult.packagePath ([ref]$ValidationFailures) -LogErrors Remove-Job -Id $Job.Id } } foreach ($Job in @(Get-Job)) { $jobResult = Wait-Job -Id $Job.Id | Receive-Job - if ($jobResult -ne '0') { - $ValidationFailures++ - } + CheckJobResult $jobResult.result $jobResult.packagePath ([ref]$ValidationFailures) Remove-Job -Id $Job.Id } if ($ValidationFailures -gt 0) { @@ -266,6 +300,10 @@ function InstallSourcelinkCli { try { InstallSourcelinkCli + foreach ($Job in @(Get-Job)) { + Remove-Job -Id $Job.Id + } + ValidateSourceLinkLinks } catch { diff --git a/eng/common/post-build/symbols-validation.ps1 b/eng/common/post-build/symbols-validation.ps1 index fcc6019b495..a5af041ba77 100644 --- a/eng/common/post-build/symbols-validation.ps1 +++ b/eng/common/post-build/symbols-validation.ps1 @@ -1,30 +1,49 @@ param( - [Parameter(Mandatory=$true)][string] $InputPath, # Full path to directory where NuGet packages to be checked are stored - [Parameter(Mandatory=$true)][string] $ExtractPath, # Full path to directory where the packages will be extracted during validation - [Parameter(Mandatory=$true)][string] $DotnetSymbolVersion, # Version of dotnet symbol to use - [Parameter(Mandatory=$false)][switch] $ContinueOnError, # If we should keep checking symbols after an error - [Parameter(Mandatory=$false)][switch] $Clean # Clean extracted symbols directory after checking symbols + [Parameter(Mandatory = $true)][string] $InputPath, # Full path to directory where NuGet packages to be checked are stored + [Parameter(Mandatory = $true)][string] $ExtractPath, # Full path to directory where the packages will be extracted during validation + [Parameter(Mandatory = $true)][string] $DotnetSymbolVersion, # Version of dotnet symbol to use + [Parameter(Mandatory = $false)][switch] $CheckForWindowsPdbs, # If we should check for the existence of windows pdbs in addition to portable PDBs + [Parameter(Mandatory = $false)][switch] $ContinueOnError, # If we should keep checking symbols after an error + [Parameter(Mandatory = $false)][switch] $Clean # Clean extracted symbols directory after checking symbols ) # Maximum number of jobs to run in parallel -$MaxParallelJobs = 6 +$MaxParallelJobs = 16 + +# Max number of retries +$MaxRetry = 5 # Wait time between check for system load $SecondsBetweenLoadChecks = 10 +# Set error codes +Set-Variable -Name "ERROR_BADEXTRACT" -Option Constant -Value -1 +Set-Variable -Name "ERROR_FILEDOESNOTEXIST" -Option Constant -Value -2 + +$WindowsPdbVerificationParam = "" +if ($CheckForWindowsPdbs) { + $WindowsPdbVerificationParam = "--windows-pdbs" +} + $CountMissingSymbols = { param( - [string] $PackagePath # Path to a NuGet package + [string] $PackagePath, # Path to a NuGet package + [string] $WindowsPdbVerificationParam # If we should check for the existence of windows pdbs in addition to portable PDBs ) . $using:PSScriptRoot\..\tools.ps1 Add-Type -AssemblyName System.IO.Compression.FileSystem + Write-Host "Validating $PackagePath " + # Ensure input file exist if (!(Test-Path $PackagePath)) { Write-PipelineTaskError "Input file does not exist: $PackagePath" - return -2 + return [pscustomobject]@{ + result = $using:ERROR_FILEDOESNOTEXIST + packagePath = $PackagePath + } } # Extensions for which we'll look for symbols @@ -45,24 +64,25 @@ $CountMissingSymbols = { Write-Host "Something went wrong extracting $PackagePath" Write-Host $_ return [pscustomobject]@{ - result = -1 + result = $using:ERROR_BADEXTRACT packagePath = $PackagePath } } Get-ChildItem -Recurse $ExtractPath | - Where-Object {$RelevantExtensions -contains $_.Extension} | - ForEach-Object { - $FileName = $_.FullName - if ($FileName -Match '\\ref\\') { - Write-Host "`t Ignoring reference assembly file " $FileName - return - } + Where-Object { $RelevantExtensions -contains $_.Extension } | + ForEach-Object { + $FileName = $_.FullName + if ($FileName -Match '\\ref\\') { + Write-Host "`t Ignoring reference assembly file " $FileName + return + } - $FirstMatchingSymbolDescriptionOrDefault = { + $FirstMatchingSymbolDescriptionOrDefault = { param( - [string] $FullPath, # Full path to the module that has to be checked - [string] $TargetServerParam, # Parameter to pass to `Symbol Tool` indicating the server to lookup for symbols + [string] $FullPath, # Full path to the module that has to be checked + [string] $TargetServerParam, # Parameter to pass to `Symbol Tool` indicating the server to lookup for symbols + [string] $WindowsPdbVerificationParam, # Parameter to pass to potential check for windows-pdbs. [string] $SymbolsPath ) @@ -87,56 +107,76 @@ $CountMissingSymbols = { # DWARF file for a .dylib $DylibDwarf = $SymbolPath.Replace($Extension, '.dylib.dwarf') - + $dotnetSymbolExe = "$env:USERPROFILE\.dotnet\tools" $dotnetSymbolExe = Resolve-Path "$dotnetSymbolExe\dotnet-symbol.exe" - & $dotnetSymbolExe --symbols --modules --windows-pdbs $TargetServerParam $FullPath -o $SymbolsPath | Out-Null + $totalRetries = 0 - if (Test-Path $PdbPath) { - return 'PDB' - } - elseif (Test-Path $NGenPdb) { - return 'NGen PDB' - } - elseif (Test-Path $SODbg) { - return 'DBG for SO' - } - elseif (Test-Path $DylibDwarf) { - return 'Dwarf for Dylib' - } - elseif (Test-Path $SymbolPath) { - return 'Module' - } - else { - return $null + while ($totalRetries -lt $using:MaxRetry) { + + # Save the output and get diagnostic output + $output = & $dotnetSymbolExe --symbols --modules $WindowsPdbVerificationParam $TargetServerParam $FullPath -o $SymbolsPath --diagnostics | Out-String + + if (Test-Path $PdbPath) { + return 'PDB' + } + elseif (Test-Path $NGenPdb) { + return 'NGen PDB' + } + elseif (Test-Path $SODbg) { + return 'DBG for SO' + } + elseif (Test-Path $DylibDwarf) { + return 'Dwarf for Dylib' + } + elseif (Test-Path $SymbolPath) { + return 'Module' + } + else + { + $totalRetries++ + } } + + return $null } - $SymbolsOnMSDL = & $FirstMatchingSymbolDescriptionOrDefault $FileName '--microsoft-symbol-server' $SymbolsPath - $SymbolsOnSymWeb = & $FirstMatchingSymbolDescriptionOrDefault $FileName '--internal-server' $SymbolsPath - - Write-Host -NoNewLine "`t Checking file " $FileName "... " + $FileGuid = New-Guid + $ExpandedSymbolsPath = Join-Path -Path $SymbolsPath -ChildPath $FileGuid + + $SymbolsOnMSDL = & $FirstMatchingSymbolDescriptionOrDefault ` + -FullPath $FileName ` + -TargetServerParam '--microsoft-symbol-server' ` + -SymbolsPath "$ExpandedSymbolsPath-msdl" ` + -WindowsPdbVerificationParam $WindowsPdbVerificationParam + $SymbolsOnSymWeb = & $FirstMatchingSymbolDescriptionOrDefault ` + -FullPath $FileName ` + -TargetServerParam '--internal-server' ` + -SymbolsPath "$ExpandedSymbolsPath-symweb" ` + -WindowsPdbVerificationParam $WindowsPdbVerificationParam + + Write-Host -NoNewLine "`t Checking file " $FileName "... " - if ($SymbolsOnMSDL -ne $null -and $SymbolsOnSymWeb -ne $null) { - Write-Host "Symbols found on MSDL ($SymbolsOnMSDL) and SymWeb ($SymbolsOnSymWeb)" + if ($SymbolsOnMSDL -ne $null -and $SymbolsOnSymWeb -ne $null) { + Write-Host "Symbols found on MSDL ($SymbolsOnMSDL) and SymWeb ($SymbolsOnSymWeb)" + } + else { + $MissingSymbols++ + + if ($SymbolsOnMSDL -eq $null -and $SymbolsOnSymWeb -eq $null) { + Write-Host 'No symbols found on MSDL or SymWeb!' } else { - $MissingSymbols++ - - if ($SymbolsOnMSDL -eq $null -and $SymbolsOnSymWeb -eq $null) { - Write-Host 'No symbols found on MSDL or SymWeb!' + if ($SymbolsOnMSDL -eq $null) { + Write-Host 'No symbols found on MSDL!' } else { - if ($SymbolsOnMSDL -eq $null) { - Write-Host 'No symbols found on MSDL!' - } - else { - Write-Host 'No symbols found on SymWeb!' - } + Write-Host 'No symbols found on SymWeb!' } } } + } if ($using:Clean) { Remove-Item $ExtractPath -Recurse -Force @@ -145,24 +185,31 @@ $CountMissingSymbols = { Pop-Location return [pscustomobject]@{ - result = $MissingSymbols - packagePath = $PackagePath - } + result = $MissingSymbols + packagePath = $PackagePath + } } function CheckJobResult( - $result, - $packagePath, - [ref]$DupedSymbols, - [ref]$TotalFailures) { - if ($result -eq '-1') { + $result, + $packagePath, + [ref]$DupedSymbols, + [ref]$TotalFailures) { + if ($result -eq $ERROR_BADEXTRACT) { Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "$packagePath has duplicated symbol files" $DupedSymbols.Value++ } - elseif ($jobResult.result -ne '0') { + elseif ($result -eq $ERROR_FILEDOESNOTEXIST) { + Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "$packagePath does not exist" + $TotalFailures.Value++ + } + elseif ($result -gt '0') { Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "Missing symbols for $result modules in the package $packagePath" $TotalFailures.Value++ } + else { + Write-Host "All symbols verified for package $packagePath" + } } function CheckSymbolsAvailable { @@ -170,6 +217,7 @@ function CheckSymbolsAvailable { Remove-Item $ExtractPath -Force -Recurse -ErrorAction SilentlyContinue } + $TotalPackages = 0 $TotalFailures = 0 $DupedSymbols = 0 @@ -192,9 +240,9 @@ function CheckSymbolsAvailable { return } - Write-Host "Validating $FileName " + $TotalPackages++ - Start-Job -ScriptBlock $CountMissingSymbols -ArgumentList $FullName | Out-Null + Start-Job -ScriptBlock $CountMissingSymbols -ArgumentList @($FullName,$WindowsPdbVerificationParam) | Out-Null $NumJobs = @(Get-Job -State 'Running').Count @@ -219,11 +267,11 @@ function CheckSymbolsAvailable { if ($TotalFailures -gt 0 -or $DupedSymbols -gt 0) { if ($TotalFailures -gt 0) { - Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "Symbols missing for $TotalFailures packages" + Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "Symbols missing for $TotalFailures/$TotalPackages packages" } if ($DupedSymbols -gt 0) { - Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "$DupedSymbols packages had duplicated symbol files" + Write-PipelineTelemetryError -Category 'CheckSymbols' -Message "$DupedSymbols/$TotalPackages packages had duplicated symbol files and could not be extracted" } ExitWithExitCode 1 diff --git a/eng/common/sdk-task.ps1 b/eng/common/sdk-task.ps1 index f55c43c6f47..7ab9baac5c8 100644 --- a/eng/common/sdk-task.ps1 +++ b/eng/common/sdk-task.ps1 @@ -34,7 +34,7 @@ function Print-Usage() { function Build([string]$target) { $logSuffix = if ($target -eq 'Execute') { '' } else { ".$target" } $log = Join-Path $LogDir "$task$logSuffix.binlog" - $outputPath = Join-Path $ToolsetDir "$task\\" + $outputPath = Join-Path $ToolsetDir "$task\" MSBuild $taskProject ` /bl:$log ` @@ -53,7 +53,7 @@ try { } if ($task -eq "") { - Write-PipelineTelemetryError -Category 'Build' -Message "Missing required parameter '-task '" -ForegroundColor Red + Write-PipelineTelemetryError -Category 'Build' -Message "Missing required parameter '-task '" Print-Usage ExitWithExitCode 1 } @@ -64,7 +64,7 @@ try { $GlobalJson.tools | Add-Member -Name "vs" -Value (ConvertFrom-Json "{ `"version`": `"16.5`" }") -MemberType NoteProperty } if( -not ($GlobalJson.tools.PSObject.Properties.Name -match "xcopy-msbuild" )) { - $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "16.8.0-preview3" -MemberType NoteProperty + $GlobalJson.tools | Add-Member -Name "xcopy-msbuild" -Value "16.10.0-preview2" -MemberType NoteProperty } if ($GlobalJson.tools."xcopy-msbuild".Trim() -ine "none") { $xcopyMSBuildToolsFolder = InitializeXCopyMSBuild $GlobalJson.tools."xcopy-msbuild" -install $true @@ -78,11 +78,14 @@ try { $taskProject = GetSdkTaskProject $task if (!(Test-Path $taskProject)) { - Write-PipelineTelemetryError -Category 'Build' -Message "Unknown task: $task" -ForegroundColor Red + Write-PipelineTelemetryError -Category 'Build' -Message "Unknown task: $task" ExitWithExitCode 1 } if ($restore) { + if ($ci) { + Try-LogClientIpAddress + } Build 'Restore' } diff --git a/eng/common/sdl/configure-sdl-tool.ps1 b/eng/common/sdl/configure-sdl-tool.ps1 new file mode 100644 index 00000000000..4999c307088 --- /dev/null +++ b/eng/common/sdl/configure-sdl-tool.ps1 @@ -0,0 +1,109 @@ +Param( + [string] $GuardianCliLocation, + [string] $WorkingDirectory, + [string] $TargetDirectory, + [string] $GdnFolder, + # The list of Guardian tools to configure. For each object in the array: + # - If the item is a [hashtable], it must contain these entries: + # - Name = The tool name as Guardian knows it. + # - Scenario = (Optional) Scenario-specific name for this configuration entry. It must be unique + # among all tool entries with the same Name. + # - Args = (Optional) Array of Guardian tool configuration args, like '@("Target > C:\temp")' + # - If the item is a [string] $v, it is treated as '@{ Name="$v" }' + [object[]] $ToolsList, + [string] $GuardianLoggerLevel='Standard', + # Optional: Additional params to add to any tool using CredScan. + [string[]] $CrScanAdditionalRunConfigParams, + # Optional: Additional params to add to any tool using PoliCheck. + [string[]] $PoliCheckAdditionalRunConfigParams +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 +$disableConfigureToolsetImport = $true +$global:LASTEXITCODE = 0 + +try { + # `tools.ps1` checks $ci to perform some actions. Since the SDL + # scripts don't necessarily execute in the same agent that run the + # build.ps1/sh script this variable isn't automatically set. + $ci = $true + . $PSScriptRoot\..\tools.ps1 + + # Normalize tools list: all in [hashtable] form with defined values for each key. + $ToolsList = $ToolsList | + ForEach-Object { + if ($_ -is [string]) { + $_ = @{ Name = $_ } + } + + if (-not ($_['Scenario'])) { $_.Scenario = "" } + if (-not ($_['Args'])) { $_.Args = @() } + $_ + } + + Write-Host "List of tools to configure:" + $ToolsList | ForEach-Object { $_ | Out-String | Write-Host } + + # We store config files in the r directory of .gdn + $gdnConfigPath = Join-Path $GdnFolder 'r' + $ValidPath = Test-Path $GuardianCliLocation + + if ($ValidPath -eq $False) + { + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Invalid Guardian CLI Location." + ExitWithExitCode 1 + } + + foreach ($tool in $ToolsList) { + # Put together the name and scenario to make a unique key. + $toolConfigName = $tool.Name + if ($tool.Scenario) { + $toolConfigName += "_" + $tool.Scenario + } + + Write-Host "=== Configuring $toolConfigName..." + + $gdnConfigFile = Join-Path $gdnConfigPath "$toolConfigName-configure.gdnconfig" + + # For some tools, add default and automatic args. + if ($tool.Name -eq 'credscan') { + if ($targetDirectory) { + $tool.Args += "TargetDirectory < $TargetDirectory" + } + $tool.Args += "OutputType < pre" + $tool.Args += $CrScanAdditionalRunConfigParams + } elseif ($tool.Name -eq 'policheck') { + if ($targetDirectory) { + $tool.Args += "Target < $TargetDirectory" + } + $tool.Args += $PoliCheckAdditionalRunConfigParams + } + + # Create variable pointing to the args array directly so we can use splat syntax later. + $toolArgs = $tool.Args + + # Configure the tool. If args array is provided or the current tool has some default arguments + # defined, add "--args" and splat each element on the end. Arg format is "{Arg id} < {Value}", + # one per parameter. Doc page for "guardian configure": + # https://dev.azure.com/securitytools/SecurityIntegration/_wiki/wikis/Guardian/1395/configure + Exec-BlockVerbosely { + & $GuardianCliLocation configure ` + --working-directory $WorkingDirectory ` + --tool $tool.Name ` + --output-path $gdnConfigFile ` + --logger-level $GuardianLoggerLevel ` + --noninteractive ` + --force ` + $(if ($toolArgs) { "--args" }) @toolArgs + Exit-IfNZEC "Sdl" + } + + Write-Host "Created '$toolConfigName' configuration file: $gdnConfigFile" + } +} +catch { + Write-Host $_.ScriptStackTrace + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/sdl/execute-all-sdl-tools.ps1 b/eng/common/sdl/execute-all-sdl-tools.ps1 index b681d797cda..1157151f486 100644 --- a/eng/common/sdl/execute-all-sdl-tools.ps1 +++ b/eng/common/sdl/execute-all-sdl-tools.ps1 @@ -7,8 +7,17 @@ Param( [string] $SourceDirectory=$env:BUILD_SOURCESDIRECTORY, # Required: the directory where source files are located [string] $ArtifactsDirectory = (Join-Path $env:BUILD_ARTIFACTSTAGINGDIRECTORY ('artifacts')), # Required: the directory where build artifacts are located [string] $AzureDevOpsAccessToken, # Required: access token for dnceng; should be provided via KeyVault - [string[]] $SourceToolsList, # Optional: list of SDL tools to run on source code - [string[]] $ArtifactToolsList, # Optional: list of SDL tools to run on built artifacts + + # Optional: list of SDL tools to run on source code. See 'configure-sdl-tool.ps1' for tools list + # format. + [object[]] $SourceToolsList, + # Optional: list of SDL tools to run on built artifacts. See 'configure-sdl-tool.ps1' for tools + # list format. + [object[]] $ArtifactToolsList, + # Optional: list of SDL tools to run without automatically specifying a target directory. See + # 'configure-sdl-tool.ps1' for tools list format. + [object[]] $CustomToolsList, + [bool] $TsaPublish=$False, # Optional: true will publish results to TSA; only set to true after onboarding to TSA; TSA is the automated framework used to upload test results as bugs. [string] $TsaBranchName=$env:BUILD_SOURCEBRANCH, # Optional: required for TSA publish; defaults to $(Build.SourceBranchName); TSA is the automated framework used to upload test results as bugs. [string] $TsaRepositoryName=$env:BUILD_REPOSITORY_NAME, # Optional: TSA repository name; will be generated automatically if not submitted; TSA is the automated framework used to upload test results as bugs. @@ -32,7 +41,7 @@ try { $ErrorActionPreference = 'Stop' Set-StrictMode -Version 2.0 $disableConfigureToolsetImport = $true - $LASTEXITCODE = 0 + $global:LASTEXITCODE = 0 # `tools.ps1` checks $ci to perform some actions. Since the SDL # scripts don't necessarily execute in the same agent that run the @@ -63,13 +72,16 @@ try { ExitWithExitCode 1 } - & $(Join-Path $PSScriptRoot 'init-sdl.ps1') -GuardianCliLocation $guardianCliLocation -Repository $RepoName -BranchName $BranchName -WorkingDirectory $workingDirectory -AzureDevOpsAccessToken $AzureDevOpsAccessToken -GuardianLoggerLevel $GuardianLoggerLevel + Exec-BlockVerbosely { + & $(Join-Path $PSScriptRoot 'init-sdl.ps1') -GuardianCliLocation $guardianCliLocation -Repository $RepoName -BranchName $BranchName -WorkingDirectory $workingDirectory -AzureDevOpsAccessToken $AzureDevOpsAccessToken -GuardianLoggerLevel $GuardianLoggerLevel + } $gdnFolder = Join-Path $workingDirectory '.gdn' if ($TsaOnboard) { if ($TsaCodebaseName -and $TsaNotificationEmail -and $TsaCodebaseAdmin -and $TsaBugAreaPath) { - Write-Host "$guardianCliLocation tsa-onboard --codebase-name `"$TsaCodebaseName`" --notification-alias `"$TsaNotificationEmail`" --codebase-admin `"$TsaCodebaseAdmin`" --instance-url `"$TsaInstanceUrl`" --project-name `"$TsaProjectName`" --area-path `"$TsaBugAreaPath`" --iteration-path `"$TsaIterationPath`" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel" - & $guardianCliLocation tsa-onboard --codebase-name "$TsaCodebaseName" --notification-alias "$TsaNotificationEmail" --codebase-admin "$TsaCodebaseAdmin" --instance-url "$TsaInstanceUrl" --project-name "$TsaProjectName" --area-path "$TsaBugAreaPath" --iteration-path "$TsaIterationPath" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + Exec-BlockVerbosely { + & $guardianCliLocation tsa-onboard --codebase-name "$TsaCodebaseName" --notification-alias "$TsaNotificationEmail" --codebase-admin "$TsaCodebaseAdmin" --instance-url "$TsaInstanceUrl" --project-name "$TsaProjectName" --area-path "$TsaBugAreaPath" --iteration-path "$TsaIterationPath" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + } if ($LASTEXITCODE -ne 0) { Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian tsa-onboard failed with exit code $LASTEXITCODE." ExitWithExitCode $LASTEXITCODE @@ -80,15 +92,41 @@ try { } } - if ($ArtifactToolsList -and $ArtifactToolsList.Count -gt 0) { - & $(Join-Path $PSScriptRoot 'run-sdl.ps1') -GuardianCliLocation $guardianCliLocation -WorkingDirectory $workingDirectory -TargetDirectory $ArtifactsDirectory -GdnFolder $gdnFolder -ToolsList $ArtifactToolsList -AzureDevOpsAccessToken $AzureDevOpsAccessToken -UpdateBaseline $UpdateBaseline -GuardianLoggerLevel $GuardianLoggerLevel -CrScanAdditionalRunConfigParams $CrScanAdditionalRunConfigParams -PoliCheckAdditionalRunConfigParams $PoliCheckAdditionalRunConfigParams - } - if ($SourceToolsList -and $SourceToolsList.Count -gt 0) { - & $(Join-Path $PSScriptRoot 'run-sdl.ps1') -GuardianCliLocation $guardianCliLocation -WorkingDirectory $workingDirectory -TargetDirectory $SourceDirectory -GdnFolder $gdnFolder -ToolsList $SourceToolsList -AzureDevOpsAccessToken $AzureDevOpsAccessToken -UpdateBaseline $UpdateBaseline -GuardianLoggerLevel $GuardianLoggerLevel -CrScanAdditionalRunConfigParams $CrScanAdditionalRunConfigParams -PoliCheckAdditionalRunConfigParams $PoliCheckAdditionalRunConfigParams + # Configure a list of tools with a default target directory. Populates the ".gdn/r" directory. + function Configure-ToolsList([object[]] $tools, [string] $targetDirectory) { + if ($tools -and $tools.Count -gt 0) { + Exec-BlockVerbosely { + & $(Join-Path $PSScriptRoot 'configure-sdl-tool.ps1') ` + -GuardianCliLocation $guardianCliLocation ` + -WorkingDirectory $workingDirectory ` + -TargetDirectory $targetDirectory ` + -GdnFolder $gdnFolder ` + -ToolsList $tools ` + -AzureDevOpsAccessToken $AzureDevOpsAccessToken ` + -GuardianLoggerLevel $GuardianLoggerLevel ` + -CrScanAdditionalRunConfigParams $CrScanAdditionalRunConfigParams ` + -PoliCheckAdditionalRunConfigParams $PoliCheckAdditionalRunConfigParams + if ($BreakOnFailure) { + Exit-IfNZEC "Sdl" + } + } + } } - if ($UpdateBaseline) { - & (Join-Path $PSScriptRoot 'push-gdn.ps1') -Repository $RepoName -BranchName $BranchName -GdnFolder $GdnFolder -AzureDevOpsAccessToken $AzureDevOpsAccessToken -PushReason 'Update baseline' + # Configure Artifact and Source tools with default Target directories. + Configure-ToolsList $ArtifactToolsList $ArtifactsDirectory + Configure-ToolsList $SourceToolsList $SourceDirectory + # Configure custom tools with no default Target directory. + Configure-ToolsList $CustomToolsList $null + + # At this point, all tools are configured in the ".gdn" directory. Run them all in a single call. + # (If we used "run" multiple times, each run would overwrite data from earlier runs.) + Exec-BlockVerbosely { + & $(Join-Path $PSScriptRoot 'run-sdl.ps1') ` + -GuardianCliLocation $guardianCliLocation ` + -WorkingDirectory $workingDirectory ` + -UpdateBaseline $UpdateBaseline ` + -GdnFolder $gdnFolder } if ($TsaPublish) { @@ -96,8 +134,9 @@ try { if (-not $TsaRepositoryName) { $TsaRepositoryName = "$($Repository)-$($BranchName)" } - Write-Host "$guardianCliLocation tsa-publish --all-tools --repository-name `"$TsaRepositoryName`" --branch-name `"$TsaBranchName`" --build-number `"$BuildNumber`" --codebase-name `"$TsaCodebaseName`" --notification-alias `"$TsaNotificationEmail`" --codebase-admin `"$TsaCodebaseAdmin`" --instance-url `"$TsaInstanceUrl`" --project-name `"$TsaProjectName`" --area-path `"$TsaBugAreaPath`" --iteration-path `"$TsaIterationPath`" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel" - & $guardianCliLocation tsa-publish --all-tools --repository-name "$TsaRepositoryName" --branch-name "$TsaBranchName" --build-number "$BuildNumber" --onboard $True --codebase-name "$TsaCodebaseName" --notification-alias "$TsaNotificationEmail" --codebase-admin "$TsaCodebaseAdmin" --instance-url "$TsaInstanceUrl" --project-name "$TsaProjectName" --area-path "$TsaBugAreaPath" --iteration-path "$TsaIterationPath" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + Exec-BlockVerbosely { + & $guardianCliLocation tsa-publish --all-tools --repository-name "$TsaRepositoryName" --branch-name "$TsaBranchName" --build-number "$BuildNumber" --onboard $True --codebase-name "$TsaCodebaseName" --notification-alias "$TsaNotificationEmail" --codebase-admin "$TsaCodebaseAdmin" --instance-url "$TsaInstanceUrl" --project-name "$TsaProjectName" --area-path "$TsaBugAreaPath" --iteration-path "$TsaIterationPath" --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + } if ($LASTEXITCODE -ne 0) { Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian tsa-publish failed with exit code $LASTEXITCODE." ExitWithExitCode $LASTEXITCODE @@ -110,7 +149,11 @@ try { if ($BreakOnFailure) { Write-Host "Failing the build in case of breaking results..." - & $guardianCliLocation break + Exec-BlockVerbosely { + & $guardianCliLocation break --working-directory $workingDirectory --logger-level $GuardianLoggerLevel + } + } else { + Write-Host "Letting the build pass even if there were breaking results..." } } catch { diff --git a/eng/common/sdl/extract-artifact-archives.ps1 b/eng/common/sdl/extract-artifact-archives.ps1 new file mode 100644 index 00000000000..68da4fbf257 --- /dev/null +++ b/eng/common/sdl/extract-artifact-archives.ps1 @@ -0,0 +1,63 @@ +# This script looks for each archive file in a directory and extracts it into the target directory. +# For example, the file "$InputPath/bin.tar.gz" extracts to "$ExtractPath/bin.tar.gz.extracted/**". +# Uses the "tar" utility added to Windows 10 / Windows 2019 that supports tar.gz and zip. +param( + # Full path to directory where archives are stored. + [Parameter(Mandatory=$true)][string] $InputPath, + # Full path to directory to extract archives into. May be the same as $InputPath. + [Parameter(Mandatory=$true)][string] $ExtractPath +) + +$ErrorActionPreference = 'Stop' +Set-StrictMode -Version 2.0 + +$disableConfigureToolsetImport = $true + +try { + # `tools.ps1` checks $ci to perform some actions. Since the SDL + # scripts don't necessarily execute in the same agent that run the + # build.ps1/sh script this variable isn't automatically set. + $ci = $true + . $PSScriptRoot\..\tools.ps1 + + Measure-Command { + $jobs = @() + + # Find archive files for non-Windows and Windows builds. + $archiveFiles = @( + Get-ChildItem (Join-Path $InputPath "*.tar.gz") + Get-ChildItem (Join-Path $InputPath "*.zip") + ) + + foreach ($targzFile in $archiveFiles) { + $jobs += Start-Job -ScriptBlock { + $file = $using:targzFile + $fileName = [System.IO.Path]::GetFileName($file) + $extractDir = Join-Path $using:ExtractPath "$fileName.extracted" + + New-Item $extractDir -ItemType Directory -Force | Out-Null + + Write-Host "Extracting '$file' to '$extractDir'..." + + # Pipe errors to stdout to prevent PowerShell detecting them and quitting the job early. + # This type of quit skips the catch, so we wouldn't be able to tell which file triggered the + # error. Save output so it can be stored in the exception string along with context. + $output = tar -xf $file -C $extractDir 2>&1 + # Handle NZEC manually rather than using Exit-IfNZEC: we are in a background job, so we + # don't have access to the outer scope. + if ($LASTEXITCODE -ne 0) { + throw "Error extracting '$file': non-zero exit code ($LASTEXITCODE). Output: '$output'" + } + + Write-Host "Extracted to $extractDir" + } + } + + Receive-Job $jobs -Wait + } +} +catch { + Write-Host $_ + Write-PipelineTelemetryError -Force -Category 'Sdl' -Message $_ + ExitWithExitCode 1 +} diff --git a/eng/common/sdl/init-sdl.ps1 b/eng/common/sdl/init-sdl.ps1 index bb6a4297110..3ac1d92b370 100644 --- a/eng/common/sdl/init-sdl.ps1 +++ b/eng/common/sdl/init-sdl.ps1 @@ -10,7 +10,7 @@ Param( $ErrorActionPreference = 'Stop' Set-StrictMode -Version 2.0 $disableConfigureToolsetImport = $true -$LASTEXITCODE = 0 +$global:LASTEXITCODE = 0 # `tools.ps1` checks $ci to perform some actions. Since the SDL # scripts don't necessarily execute in the same agent that run the @@ -46,7 +46,6 @@ try { Write-PipelineTelemetryError -Force -Category 'Build' -Message "Guardian baseline failed with exit code $LASTEXITCODE." ExitWithExitCode $LASTEXITCODE } - & $(Join-Path $PSScriptRoot 'push-gdn.ps1') -Repository $Repository -BranchName $BranchName -GdnFolder $gdnFolder -AzureDevOpsAccessToken $AzureDevOpsAccessToken -PushReason 'Initialize gdn folder' ExitWithExitCode 0 } catch { diff --git a/eng/common/sdl/push-gdn.ps1 b/eng/common/sdl/push-gdn.ps1 deleted file mode 100644 index d8fd2d82a68..00000000000 --- a/eng/common/sdl/push-gdn.ps1 +++ /dev/null @@ -1,69 +0,0 @@ -Param( - [string] $Repository, - [string] $BranchName='master', - [string] $GdnFolder, - [string] $AzureDevOpsAccessToken, - [string] $PushReason -) - -$ErrorActionPreference = 'Stop' -Set-StrictMode -Version 2.0 -$disableConfigureToolsetImport = $true -$LASTEXITCODE = 0 - -try { - # `tools.ps1` checks $ci to perform some actions. Since the SDL - # scripts don't necessarily execute in the same agent that run the - # build.ps1/sh script this variable isn't automatically set. - $ci = $true - . $PSScriptRoot\..\tools.ps1 - - # We create the temp directory where we'll store the sdl-config repository - $sdlDir = Join-Path $env:TEMP 'sdl' - if (Test-Path $sdlDir) { - Remove-Item -Force -Recurse $sdlDir - } - - Write-Host "git clone https://dnceng:`$AzureDevOpsAccessToken@dev.azure.com/dnceng/internal/_git/sdl-tool-cfg $sdlDir" - git clone https://dnceng:$AzureDevOpsAccessToken@dev.azure.com/dnceng/internal/_git/sdl-tool-cfg $sdlDir - if ($LASTEXITCODE -ne 0) { - Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Git clone failed with exit code $LASTEXITCODE." - ExitWithExitCode $LASTEXITCODE - } - # We copy the .gdn folder from our local run into the git repository so it can be committed - $sdlRepositoryFolder = Join-Path (Join-Path (Join-Path $sdlDir $Repository) $BranchName) '.gdn' - if (Get-Command Robocopy) { - Robocopy /S $GdnFolder $sdlRepositoryFolder - } else { - rsync -r $GdnFolder $sdlRepositoryFolder - } - # cd to the sdl-config directory so we can run git there - Push-Location $sdlDir - # git add . --> git commit --> git push - Write-Host 'git add .' - git add . - if ($LASTEXITCODE -ne 0) { - Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Git add failed with exit code $LASTEXITCODE." - ExitWithExitCode $LASTEXITCODE - } - Write-Host "git -c user.email=`"dn-bot@microsoft.com`" -c user.name=`"Dotnet Bot`" commit -m `"$PushReason for $Repository/$BranchName`"" - git -c user.email="dn-bot@microsoft.com" -c user.name="Dotnet Bot" commit -m "$PushReason for $Repository/$BranchName" - if ($LASTEXITCODE -ne 0) { - Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Git commit failed with exit code $LASTEXITCODE." - ExitWithExitCode $LASTEXITCODE - } - Write-Host 'git push' - git push - if ($LASTEXITCODE -ne 0) { - Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Git push failed with exit code $LASTEXITCODE." - ExitWithExitCode $LASTEXITCODE - } - - # Return to the original directory - Pop-Location -} -catch { - Write-Host $_.ScriptStackTrace - Write-PipelineTelemetryError -Category 'Sdl' -Message $_ - ExitWithExitCode 1 -} diff --git a/eng/common/sdl/run-sdl.ps1 b/eng/common/sdl/run-sdl.ps1 index fe95ab35aa5..2eac8c78f10 100644 --- a/eng/common/sdl/run-sdl.ps1 +++ b/eng/common/sdl/run-sdl.ps1 @@ -1,19 +1,15 @@ Param( [string] $GuardianCliLocation, [string] $WorkingDirectory, - [string] $TargetDirectory, [string] $GdnFolder, - [string[]] $ToolsList, [string] $UpdateBaseline, - [string] $GuardianLoggerLevel='Standard', - [string[]] $CrScanAdditionalRunConfigParams, - [string[]] $PoliCheckAdditionalRunConfigParams + [string] $GuardianLoggerLevel='Standard' ) $ErrorActionPreference = 'Stop' Set-StrictMode -Version 2.0 $disableConfigureToolsetImport = $true -$LASTEXITCODE = 0 +$global:LASTEXITCODE = 0 try { # `tools.ps1` checks $ci to perform some actions. Since the SDL @@ -23,7 +19,6 @@ try { . $PSScriptRoot\..\tools.ps1 # We store config files in the r directory of .gdn - Write-Host $ToolsList $gdnConfigPath = Join-Path $GdnFolder 'r' $ValidPath = Test-Path $GuardianCliLocation @@ -33,37 +28,18 @@ try { ExitWithExitCode 1 } - $configParam = @('--config') - - foreach ($tool in $ToolsList) { - $gdnConfigFile = Join-Path $gdnConfigPath "$tool-configure.gdnconfig" - Write-Host $tool - # We have to manually configure tools that run on source to look at the source directory only - if ($tool -eq 'credscan') { - Write-Host "$GuardianCliLocation configure --working-directory $WorkingDirectory --tool $tool --output-path $gdnConfigFile --logger-level $GuardianLoggerLevel --noninteractive --force --args `" TargetDirectory < $TargetDirectory `" `" OutputType < pre `" $(If ($CrScanAdditionalRunConfigParams) {$CrScanAdditionalRunConfigParams})" - & $GuardianCliLocation configure --working-directory $WorkingDirectory --tool $tool --output-path $gdnConfigFile --logger-level $GuardianLoggerLevel --noninteractive --force --args " TargetDirectory < $TargetDirectory " "OutputType < pre" $(If ($CrScanAdditionalRunConfigParams) {$CrScanAdditionalRunConfigParams}) - if ($LASTEXITCODE -ne 0) { - Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian configure for $tool failed with exit code $LASTEXITCODE." - ExitWithExitCode $LASTEXITCODE - } - } - if ($tool -eq 'policheck') { - Write-Host "$GuardianCliLocation configure --working-directory $WorkingDirectory --tool $tool --output-path $gdnConfigFile --logger-level $GuardianLoggerLevel --noninteractive --force --args `" Target < $TargetDirectory `" $(If ($PoliCheckAdditionalRunConfigParams) {$PoliCheckAdditionalRunConfigParams})" - & $GuardianCliLocation configure --working-directory $WorkingDirectory --tool $tool --output-path $gdnConfigFile --logger-level $GuardianLoggerLevel --noninteractive --force --args " Target < $TargetDirectory " $(If ($PoliCheckAdditionalRunConfigParams) {$PoliCheckAdditionalRunConfigParams}) - if ($LASTEXITCODE -ne 0) { - Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian configure for $tool failed with exit code $LASTEXITCODE." - ExitWithExitCode $LASTEXITCODE - } - } - - $configParam+=$gdnConfigFile - } - - Write-Host "$GuardianCliLocation run --working-directory $WorkingDirectory --baseline mainbaseline --update-baseline $UpdateBaseline --logger-level $GuardianLoggerLevel $configParam" - & $GuardianCliLocation run --working-directory $WorkingDirectory --tool $tool --baseline mainbaseline --update-baseline $UpdateBaseline --logger-level $GuardianLoggerLevel $configParam - if ($LASTEXITCODE -ne 0) { - Write-PipelineTelemetryError -Force -Category 'Sdl' -Message "Guardian run for $ToolsList using $configParam failed with exit code $LASTEXITCODE." - ExitWithExitCode $LASTEXITCODE + $gdnConfigFiles = Get-ChildItem $gdnConfigPath -Recurse -Include '*.gdnconfig' + Write-Host "Discovered Guardian config files:" + $gdnConfigFiles | Out-String | Write-Host + + Exec-BlockVerbosely { + & $GuardianCliLocation run ` + --working-directory $WorkingDirectory ` + --baseline mainbaseline ` + --update-baseline $UpdateBaseline ` + --logger-level $GuardianLoggerLevel ` + --config @gdnConfigFiles + Exit-IfNZEC "Sdl" } } catch { diff --git a/eng/common/templates/job/execute-sdl.yml b/eng/common/templates/job/execute-sdl.yml index 53c100222b2..69eb67849d7 100644 --- a/eng/common/templates/job/execute-sdl.yml +++ b/eng/common/templates/job/execute-sdl.yml @@ -2,17 +2,41 @@ parameters: enable: 'false' # Whether the SDL validation job should execute or not overrideParameters: '' # Optional: to override values for parameters. additionalParameters: '' # Optional: parameters that need user specific values eg: '-SourceToolsList @("abc","def") -ArtifactToolsList @("ghi","jkl")' + # Optional: if specified, restore and use this version of Guardian instead of the default. + overrideGuardianVersion: '' + # Optional: if true, publish the '.gdn' folder as a pipeline artifact. This can help with in-depth + # diagnosis of problems with specific tool configurations. + publishGuardianDirectoryToPipeline: false + # The script to run to execute all SDL tools. Use this if you want to use a script to define SDL + # parameters rather than relying on YAML. It may be better to use a local script, because you can + # reproduce results locally without piecing together a command based on the YAML. + executeAllSdlToolsScript: 'eng/common/sdl/execute-all-sdl-tools.ps1' # There is some sort of bug (has been reported) in Azure DevOps where if this parameter is named # 'continueOnError', the parameter value is not correctly picked up. # This can also be remedied by the caller (post-build.yml) if it does not use a nested parameter sdlContinueOnError: false # optional: determines whether to continue the build if the step errors; - downloadArtifacts: true # optional: determines if the artifacts should be dowloaded + # optional: determines if build artifacts should be downloaded. + downloadArtifacts: true + # optional: determines if this job should search the directory of downloaded artifacts for + # 'tar.gz' and 'zip' archive files and extract them before running SDL validation tasks. + extractArchiveArtifacts: false dependsOn: '' # Optional: dependencies of the job artifactNames: '' # Optional: patterns supplied to DownloadBuildArtifacts # Usage: # artifactNames: # - 'BlobArtifacts' # - 'Artifacts_Windows_NT_Release' + # Optional: download a list of pipeline artifacts. 'downloadArtifacts' controls build artifacts, + # not pipeline artifacts, so doesn't affect the use of this parameter. + pipelineArtifactNames: [] + # Optional: location and ID of the AzDO build that the build/pipeline artifacts should be + # downloaded from. By default, uses runtime expressions to decide based on the variables set by + # the 'setupMaestroVars' dependency. Overriding this parameter is necessary if SDL tasks are + # running without Maestro++/BAR involved, or to download artifacts from a specific existing build + # to iterate quickly on SDL changes. + AzDOProjectName: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOProjectName'] ] + AzDOPipelineId: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOPipelineId'] ] + AzDOBuildId: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOBuildId'] ] jobs: - job: Run_SDL @@ -22,16 +46,29 @@ jobs: variables: - group: DotNet-VSTS-Bot - name: AzDOProjectName - value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOProjectName'] ] + value: ${{ parameters.AzDOProjectName }} - name: AzDOPipelineId - value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOPipelineId'] ] + value: ${{ parameters.AzDOPipelineId }} - name: AzDOBuildId - value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.AzDOBuildId'] ] + value: ${{ parameters.AzDOBuildId }} + # The Guardian version specified in 'eng/common/sdl/packages.config'. This value must be kept in + # sync with the packages.config file. + - name: DefaultGuardianVersion + value: 0.53.3 + - name: GuardianVersion + value: ${{ coalesce(parameters.overrideGuardianVersion, '$(DefaultGuardianVersion)') }} + - name: GuardianPackagesConfigFile + value: $(Build.SourcesDirectory)\eng\common\sdl\packages.config pool: - name: Hosted VS2017 + # To extract archives (.tar.gz, .zip), we need access to "tar", added in Windows 10/2019. + ${{ if eq(parameters.extractArchiveArtifacts, 'false') }}: + name: Hosted VS2017 + ${{ if ne(parameters.extractArchiveArtifacts, 'false') }}: + vmImage: windows-2019 steps: - checkout: self clean: true + - ${{ if ne(parameters.downloadArtifacts, 'false')}}: - ${{ if ne(parameters.artifactNames, '') }}: - ${{ each artifactName in parameters.artifactNames }}: @@ -45,6 +82,7 @@ jobs: buildId: $(AzDOBuildId) artifactName: ${{ artifactName }} downloadPath: $(Build.ArtifactStagingDirectory)\artifacts + checkDownloadedFiles: true - ${{ if eq(parameters.artifactNames, '') }}: - task: DownloadBuildArtifacts@0 displayName: Download Build Artifacts @@ -57,16 +95,52 @@ jobs: downloadType: specific files itemPattern: "**" downloadPath: $(Build.ArtifactStagingDirectory)\artifacts + checkDownloadedFiles: true + + - ${{ each artifactName in parameters.pipelineArtifactNames }}: + - task: DownloadPipelineArtifact@2 + displayName: Download Pipeline Artifacts + inputs: + buildType: specific + buildVersionToDownload: specific + project: $(AzDOProjectName) + pipeline: $(AzDOPipelineId) + buildId: $(AzDOBuildId) + artifactName: ${{ artifactName }} + downloadPath: $(Build.ArtifactStagingDirectory)\artifacts + checkDownloadedFiles: true + - powershell: eng/common/sdl/extract-artifact-packages.ps1 -InputPath $(Build.ArtifactStagingDirectory)\artifacts\BlobArtifacts -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts\BlobArtifacts displayName: Extract Blob Artifacts continueOnError: ${{ parameters.sdlContinueOnError }} + - powershell: eng/common/sdl/extract-artifact-packages.ps1 -InputPath $(Build.ArtifactStagingDirectory)\artifacts\PackageArtifacts -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts\PackageArtifacts displayName: Extract Package Artifacts continueOnError: ${{ parameters.sdlContinueOnError }} + + - ${{ if ne(parameters.extractArchiveArtifacts, 'false') }}: + - powershell: eng/common/sdl/extract-artifact-archives.ps1 + -InputPath $(Build.ArtifactStagingDirectory)\artifacts + -ExtractPath $(Build.ArtifactStagingDirectory)\artifacts + displayName: Extract Archive Artifacts + continueOnError: ${{ parameters.sdlContinueOnError }} + + - ${{ if ne(parameters.overrideGuardianVersion, '') }}: + - powershell: | + $content = Get-Content $(GuardianPackagesConfigFile) + + Write-Host "packages.config content was:`n$content" + + $content = $content.Replace('$(DefaultGuardianVersion)', '$(GuardianVersion)') + $content | Set-Content $(GuardianPackagesConfigFile) + + Write-Host "packages.config content updated to:`n$content" + displayName: Use overridden Guardian version ${{ parameters.overrideGuardianVersion }} + - task: NuGetToolInstaller@1 displayName: 'Install NuGet.exe' - task: NuGetCommand@2 @@ -77,15 +151,35 @@ jobs: nugetConfigPath: $(Build.SourcesDirectory)\eng\common\sdl\NuGet.config externalFeedCredentials: GuardianConnect restoreDirectory: $(Build.SourcesDirectory)\.packages + - ${{ if ne(parameters.overrideParameters, '') }}: - - powershell: eng/common/sdl/execute-all-sdl-tools.ps1 ${{ parameters.overrideParameters }} + - powershell: ${{ parameters.executeAllSdlToolsScript }} ${{ parameters.overrideParameters }} displayName: Execute SDL continueOnError: ${{ parameters.sdlContinueOnError }} - ${{ if eq(parameters.overrideParameters, '') }}: - - powershell: eng/common/sdl/execute-all-sdl-tools.ps1 - -GuardianPackageName Microsoft.Guardian.Cli.0.53.3 + - powershell: ${{ parameters.executeAllSdlToolsScript }} + -GuardianPackageName Microsoft.Guardian.Cli.$(GuardianVersion) -NugetPackageDirectory $(Build.SourcesDirectory)\.packages -AzureDevOpsAccessToken $(dn-bot-dotnet-build-rw-code-rw) ${{ parameters.additionalParameters }} displayName: Execute SDL continueOnError: ${{ parameters.sdlContinueOnError }} + + - ${{ if ne(parameters.publishGuardianDirectoryToPipeline, 'false') }}: + # We want to publish the Guardian results and configuration for easy diagnosis. However, the + # '.gdn' dir is a mix of configuration, results, extracted dependencies, and Guardian default + # tooling files. Some of these files are large and aren't useful during an investigation, so + # exclude them by simply deleting them before publishing. (As of writing, there is no documented + # way to selectively exclude a dir from the pipeline artifact publish task.) + - task: DeleteFiles@1 + displayName: Delete Guardian dependencies to avoid uploading + inputs: + SourceFolder: $(Agent.BuildDirectory)/.gdn + Contents: | + c + i + condition: succeededOrFailed() + - publish: $(Agent.BuildDirectory)/.gdn + artifact: GuardianConfiguration + displayName: Publish GuardianConfiguration + condition: succeededOrFailed() diff --git a/eng/common/templates/job/job.yml b/eng/common/templates/job/job.yml index 8b81a7e5143..37dceb1bab0 100644 --- a/eng/common/templates/job/job.yml +++ b/eng/common/templates/job/job.yml @@ -24,9 +24,9 @@ parameters: enablePublishBuildAssets: false enablePublishTestResults: false enablePublishUsingPipelines: false - useBuildManifest: false mergeTestResults: false testRunTitle: '' + testResultsFormat: '' name: '' preSteps: [] runAsPublic: false @@ -103,7 +103,7 @@ jobs: - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - - task: MicroBuildSigningPlugin@2 + - task: MicroBuildSigningPlugin@3 displayName: Install MicroBuild plugin inputs: signType: $(_SignType) @@ -131,8 +131,8 @@ jobs: - task: RichCodeNavIndexer@0 displayName: RichCodeNav Upload inputs: - languages: 'csharp' - environment: ${{ coalesce(parameters.richCodeNavigationEnvironment, 'prod') }} + languages: ${{ coalesce(parameters.richCodeNavigationLanguage, 'csharp') }} + environment: ${{ coalesce(parameters.richCodeNavigationEnvironment, 'production') }} richNavLogOutputDirectory: $(Build.SourcesDirectory)/artifacts/bin continueOnError: true @@ -202,7 +202,7 @@ jobs: continueOnError: true condition: always() - - ${{ if eq(parameters.enablePublishTestResults, 'true') }}: + - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'xunit')) }}: - task: PublishTestResults@2 displayName: Publish XUnit Test Results inputs: @@ -213,6 +213,7 @@ jobs: mergeTestResults: ${{ parameters.mergeTestResults }} continueOnError: true condition: always() + - ${{ if or(and(eq(parameters.enablePublishTestResults, 'true'), eq(parameters.testResultsFormat, '')), eq(parameters.testResultsFormat, 'vstest')) }}: - task: PublishTestResults@2 displayName: Publish TRX Test Results inputs: @@ -241,12 +242,3 @@ jobs: ArtifactName: AssetManifests continueOnError: ${{ parameters.continueOnError }} condition: and(succeeded(), eq(variables['_DotNetPublishToBlobFeed'], 'true')) - - - ${{ if eq(parameters.useBuildManifest, true) }}: - - task: PublishBuildArtifacts@1 - displayName: Publish Build Manifest - inputs: - PathToPublish: '$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/manifest.props' - PublishLocation: Container - ArtifactName: BuildManifests - continueOnError: ${{ parameters.continueOnError }} diff --git a/eng/common/templates/job/onelocbuild.yml b/eng/common/templates/job/onelocbuild.yml index b27d6faf303..e8bc77d2ebb 100644 --- a/eng/common/templates/job/onelocbuild.yml +++ b/eng/common/templates/job/onelocbuild.yml @@ -12,11 +12,15 @@ parameters: SourcesDirectory: $(Build.SourcesDirectory) CreatePr: true AutoCompletePr: false + UseLfLineEndings: true UseCheckedInLocProjectJson: false LanguageSet: VS_Main_Languages LclSource: lclFilesInRepo LclPackageId: '' RepoType: gitHub + GitHubOrg: dotnet + MirrorRepo: '' + MirrorBranch: main condition: '' jobs: @@ -52,18 +56,24 @@ jobs: env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) inputs: - locProj: Localize/LocProject.json + locProj: eng/Localize/LocProject.json outDir: $(Build.ArtifactStagingDirectory) lclSource: ${{ parameters.LclSource }} lclPackageId: ${{ parameters.LclPackageId }} isCreatePrSelected: ${{ parameters.CreatePr }} ${{ if eq(parameters.CreatePr, true) }}: isAutoCompletePrSelected: ${{ parameters.AutoCompletePr }} + isUseLfLineEndingsSelected: ${{ parameters.UseLfLineEndings }} packageSourceAuth: patAuth patVariable: ${{ parameters.CeapexPat }} ${{ if eq(parameters.RepoType, 'gitHub') }}: repoType: ${{ parameters.RepoType }} gitHubPatVariable: "${{ parameters.GithubPat }}" + ${{ if ne(parameters.MirrorRepo, '') }}: + isMirrorRepoSelected: true + gitHubOrganization: ${{ parameters.GitHubOrg }} + mirrorRepo: ${{ parameters.MirrorRepo }} + mirrorBranch: ${{ parameters.MirrorBranch }} condition: ${{ parameters.condition }} - task: PublishBuildArtifacts@1 @@ -77,7 +87,7 @@ jobs: - task: PublishBuildArtifacts@1 displayName: Publish LocProject.json inputs: - PathtoPublish: '$(Build.SourcesDirectory)/Localize/' + PathtoPublish: '$(Build.SourcesDirectory)/eng/Localize/' PublishLocation: Container ArtifactName: Loc condition: ${{ parameters.condition }} \ No newline at end of file diff --git a/eng/common/templates/job/performance.yml b/eng/common/templates/job/performance.yml deleted file mode 100644 index f877fd7a898..00000000000 --- a/eng/common/templates/job/performance.yml +++ /dev/null @@ -1,95 +0,0 @@ -parameters: - steps: [] # optional -- any additional steps that need to happen before pulling down the performance repo and sending the performance benchmarks to helix (ie building your repo) - variables: [] # optional -- list of additional variables to send to the template - jobName: '' # required -- job name - displayName: '' # optional -- display name for the job. Will use jobName if not passed - pool: '' # required -- name of the Build pool - container: '' # required -- name of the container - osGroup: '' # required -- operating system for the job - extraSetupParameters: '' # optional -- extra arguments to pass to the setup script - frameworks: ['netcoreapp3.0'] # optional -- list of frameworks to run against - continueOnError: 'false' # optional -- determines whether to continue the build if the step errors - dependsOn: '' # optional -- dependencies of the job - timeoutInMinutes: 320 # optional -- timeout for the job - enableTelemetry: false # optional -- enable for telemetry - -jobs: -- template: ../jobs/jobs.yml - parameters: - dependsOn: ${{ parameters.dependsOn }} - enableTelemetry: ${{ parameters.enableTelemetry }} - enablePublishBuildArtifacts: true - continueOnError: ${{ parameters.continueOnError }} - - jobs: - - job: '${{ parameters.jobName }}' - - ${{ if ne(parameters.displayName, '') }}: - displayName: '${{ parameters.displayName }}' - ${{ if eq(parameters.displayName, '') }}: - displayName: '${{ parameters.jobName }}' - - timeoutInMinutes: ${{ parameters.timeoutInMinutes }} - - variables: - - - ${{ each variable in parameters.variables }}: - - ${{ if ne(variable.name, '') }}: - - name: ${{ variable.name }} - value: ${{ variable.value }} - - ${{ if ne(variable.group, '') }}: - - group: ${{ variable.group }} - - - IsInternal: '' - - HelixApiAccessToken: '' - - HelixPreCommand: '' - - - ${{ if and(ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - ${{ if eq( parameters.osGroup, 'Windows_NT') }}: - - HelixPreCommand: 'set "PERFLAB_UPLOAD_TOKEN=$(PerfCommandUploadToken)"' - - IsInternal: -Internal - - ${{ if ne(parameters.osGroup, 'Windows_NT') }}: - - HelixPreCommand: 'export PERFLAB_UPLOAD_TOKEN="$(PerfCommandUploadTokenLinux)"' - - IsInternal: --internal - - - group: DotNet-HelixApi-Access - - group: dotnet-benchview - - workspace: - clean: all - pool: - ${{ parameters.pool }} - container: ${{ parameters.container }} - strategy: - matrix: - ${{ each framework in parameters.frameworks }}: - ${{ framework }}: - _Framework: ${{ framework }} - steps: - - checkout: self - clean: true - # Run all of the steps to setup repo - - ${{ each step in parameters.steps }}: - - ${{ step }} - - powershell: $(Build.SourcesDirectory)\eng\common\performance\performance-setup.ps1 $(IsInternal) -Framework $(_Framework) ${{ parameters.extraSetupParameters }} - displayName: Performance Setup (Windows) - condition: and(succeeded(), eq(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - - script: $(Build.SourcesDirectory)/eng/common/performance/performance-setup.sh $(IsInternal) --framework $(_Framework) ${{ parameters.extraSetupParameters }} - displayName: Performance Setup (Unix) - condition: and(succeeded(), ne(variables['Agent.Os'], 'Windows_NT')) - continueOnError: ${{ parameters.continueOnError }} - - script: $(Python) $(PerformanceDirectory)/scripts/ci_setup.py $(SetupArguments) - displayName: Run ci setup script - # Run perf testing in helix - - template: /eng/common/templates/steps/perf-send-to-helix.yml - parameters: - HelixSource: '$(HelixSourcePrefix)/$(Build.Repository.Name)/$(Build.SourceBranch)' # sources must start with pr/, official/, prodcon/, or agent/ - HelixType: 'test/performance/$(Kind)/$(_Framework)/$(Architecture)' - HelixAccessToken: $(HelixApiAccessToken) - HelixTargetQueues: $(Queue) - HelixPreCommands: $(HelixPreCommand) - Creator: $(Creator) - WorkItemTimeout: 4:00 # 4 hours - WorkItemDirectory: '$(WorkItemDirectory)' # WorkItemDirectory can not be empty, so we send it some docs to keep it happy - CorrelationPayloadDirectory: '$(PayloadDirectory)' # it gets checked out to a folder with shorter path than WorkItemDirectory so we can avoid file name too long exceptions \ No newline at end of file diff --git a/eng/common/templates/job/publish-build-assets.yml b/eng/common/templates/job/publish-build-assets.yml index d0c3cc2b3ba..fe9dfdf720c 100644 --- a/eng/common/templates/job/publish-build-assets.yml +++ b/eng/common/templates/job/publish-build-assets.yml @@ -37,6 +37,7 @@ jobs: - name: _BuildConfig value: ${{ parameters.configuration }} - group: Publish-Build-Assets + - group: AzureDevOps-Artifact-Feeds-Pats # Skip component governance and codesign validation for SDL. These jobs # create no content. - name: skipComponentGovernanceDetection @@ -51,12 +52,19 @@ jobs: inputs: artifactName: AssetManifests downloadPath: '$(Build.StagingDirectory)/Download' + checkDownloadedFiles: true condition: ${{ parameters.condition }} continueOnError: ${{ parameters.continueOnError }} - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - task: NuGetAuthenticate@0 + - task: PowerShell@2 + displayName: Enable cross-org NuGet feed authentication + inputs: + filePath: $(Build.SourcesDirectory)/eng/common/enable-cross-org-publishing.ps1 + arguments: -token $(dn-bot-all-orgs-artifact-feeds-rw) + - task: PowerShell@2 displayName: Publish Build Assets inputs: @@ -86,7 +94,31 @@ jobs: PathtoPublish: '$(Build.StagingDirectory)/ReleaseConfigs.txt' PublishLocation: Container ArtifactName: ReleaseConfigs - + + - task: powershell@2 + displayName: Check if SymbolPublishingExclusionsFile.txt exists + inputs: + targetType: inline + script: | + $symbolExclusionfile = "$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt" + if(Test-Path -Path $symbolExclusionfile) + { + Write-Host "SymbolExclusionFile exists" + Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]true" + } + else{ + Write-Host "Symbols Exclusion file does not exists" + Write-Host "##vso[task.setvariable variable=SymbolExclusionFile]false" + } + + - task: PublishBuildArtifacts@1 + displayName: Publish SymbolPublishingExclusionsFile Artifact + condition: eq(variables['SymbolExclusionFile'], 'true') + inputs: + PathtoPublish: '$(Build.SourcesDirectory)/eng/SymbolPublishingExclusionsFile.txt' + PublishLocation: Container + ArtifactName: ReleaseConfigs + - ${{ if eq(parameters.enablePublishBuildArtifacts, 'true') }}: - template: /eng/common/templates/steps/publish-logs.yml parameters: diff --git a/eng/common/templates/job/source-build.yml b/eng/common/templates/job/source-build.yml index 9332f5ecc38..5023d36dcb3 100644 --- a/eng/common/templates/job/source-build.yml +++ b/eng/common/templates/job/source-build.yml @@ -15,6 +15,9 @@ parameters: # nonPortable: false # Enables non-portable mode. This means a more specific RID (e.g. fedora.32-x64 rather than # linux-x64), and compiling against distro-provided packages rather than portable ones. + # skipPublishValidation: false + # Disables publishing validation. By default, a check is performed to ensure no packages are + # published by source-build. # container: '' # A container to use. Runs in docker. # pool: {} @@ -28,6 +31,11 @@ parameters: # container and pool. platform: {} + # The default VM host AzDO pool. This should be capable of running Docker containers: almost all + # source-build builds run in Docker, including the default managed platform. + defaultContainerHostPool: + vmImage: ubuntu-20.04 + jobs: - job: ${{ parameters.jobNamePrefix }}_${{ parameters.platform.name }} displayName: Source-Build (${{ parameters.platform.name }}) @@ -37,6 +45,9 @@ jobs: ${{ if ne(parameters.platform.container, '') }}: container: ${{ parameters.platform.container }} + + ${{ if eq(parameters.platform.pool, '') }}: + pool: ${{ parameters.defaultContainerHostPool }} ${{ if ne(parameters.platform.pool, '') }}: pool: ${{ parameters.platform.pool }} diff --git a/eng/common/templates/job/source-index-stage1.yml b/eng/common/templates/job/source-index-stage1.yml new file mode 100644 index 00000000000..1cc0c29e4fd --- /dev/null +++ b/eng/common/templates/job/source-index-stage1.yml @@ -0,0 +1,57 @@ +parameters: + runAsPublic: false + sourceIndexPackageVersion: 1.0.1-20210614.1 + sourceIndexPackageSource: https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json + sourceIndexBuildCommand: powershell -NoLogo -NoProfile -ExecutionPolicy Bypass -Command "eng/common/build.ps1 -restore -build -binarylog -ci" + preSteps: [] + binlogPath: artifacts/log/Debug/Build.binlog + pool: + vmImage: vs2017-win2016 + condition: '' + dependsOn: '' + +jobs: +- job: SourceIndexStage1 + dependsOn: ${{ parameters.dependsOn }} + condition: ${{ parameters.condition }} + variables: + - name: SourceIndexPackageVersion + value: ${{ parameters.sourceIndexPackageVersion }} + - name: SourceIndexPackageSource + value: ${{ parameters.sourceIndexPackageSource }} + - name: BinlogPath + value: ${{ parameters.binlogPath }} + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - group: source-dot-net stage1 variables + + pool: ${{ parameters.pool }} + steps: + - ${{ each preStep in parameters.preSteps }}: + - ${{ preStep }} + + - task: UseDotNet@2 + displayName: Use .NET Core sdk 3.1 + inputs: + packageType: sdk + version: 3.1.x + installationPath: $(Agent.TempDirectory)/dotnet + workingDirectory: $(Agent.TempDirectory) + + - script: | + $(Agent.TempDirectory)/dotnet/dotnet tool install BinLogToSln --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + $(Agent.TempDirectory)/dotnet/dotnet tool install UploadIndexStage1 --version $(SourceIndexPackageVersion) --add-source $(SourceIndexPackageSource) --tool-path $(Agent.TempDirectory)/.source-index/tools + displayName: Download Tools + # Set working directory to temp directory so 'dotnet' doesn't try to use global.json and use the repo's sdk. + workingDirectory: $(Agent.TempDirectory) + + - script: ${{ parameters.sourceIndexBuildCommand }} + displayName: Build Repository + + - script: $(Agent.TempDirectory)/.source-index/tools/BinLogToSln -i $(BinlogPath) -r $(Build.SourcesDirectory) -n $(Build.Repository.Name) -o .source-index/stage1output + displayName: Process Binlog into indexable sln + + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - script: $(Agent.TempDirectory)/.source-index/tools/UploadIndexStage1 -i .source-index/stage1output -n $(Build.Repository.Name) + displayName: Upload stage1 artifacts to source index + env: + BLOB_CONTAINER_URL: $(source-dot-net-stage1-blob-container-url) diff --git a/eng/common/templates/jobs/jobs.yml b/eng/common/templates/jobs/jobs.yml index 08845950f44..a1f8fce96ca 100644 --- a/eng/common/templates/jobs/jobs.yml +++ b/eng/common/templates/jobs/jobs.yml @@ -7,7 +7,14 @@ parameters: # Optional: Enable publishing using release pipelines enablePublishUsingPipelines: false - + + # Optional: Enable running the source-build jobs to build repo from source + enableSourceBuild: false + + # Optional: Parameters for source-build template. + # See /eng/common/templates/jobs/source-build.yml for options + sourceBuildParameters: [] + graphFileGeneration: # Optional: Enable generating the graph files at the end of the build enabled: false @@ -24,12 +31,8 @@ parameters: # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. runAsPublic: false - # Optional: Enable running the source-build jobs to build repo from source - runSourceBuild: false - - # Optional: Parameters for source-build template. - # See /eng/common/templates/jobs/source-build.yml for options - sourceBuildParameters: [] + enableSourceIndex: false + sourceIndexParams: {} # Internal resources (telemetry, microbuild) can only be accessed from non-public projects, # and some (Microbuild) should only be applied to non-PR cases for internal builds. @@ -50,14 +53,22 @@ jobs: name: ${{ job.job }} -- ${{ if eq(parameters.runSourceBuild, true) }}: +- ${{ if eq(parameters.enableSourceBuild, true) }}: - template: /eng/common/templates/jobs/source-build.yml parameters: allCompletedJobId: Source_Build_Complete ${{ each parameter in parameters.sourceBuildParameters }}: ${{ parameter.key }}: ${{ parameter.value }} +- ${{ if eq(parameters.enableSourceIndex, 'true') }}: + - template: ../job/source-index-stage1.yml + parameters: + runAsPublic: ${{ parameters.runAsPublic }} + ${{ each parameter in parameters.sourceIndexParams }}: + ${{ parameter.key }}: ${{ parameter.value }} + - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: + - ${{ if or(eq(parameters.enablePublishBuildAssets, true), eq(parameters.artifacts.publish.manifests, 'true'), ne(parameters.artifacts.publish.manifests, '')) }}: - template: ../job/publish-build-assets.yml parameters: @@ -69,7 +80,7 @@ jobs: - ${{ if eq(parameters.publishBuildAssetsDependsOn, '') }}: - ${{ each job in parameters.jobs }}: - ${{ job.job }} - - ${{ if eq(parameters.runSourceBuild, true) }}: + - ${{ if eq(parameters.enableSourceBuild, true) }}: - Source_Build_Complete pool: vmImage: vs2017-win2016 diff --git a/eng/common/templates/jobs/source-build.yml b/eng/common/templates/jobs/source-build.yml index f463011e793..00aa98eb3bf 100644 --- a/eng/common/templates/jobs/source-build.yml +++ b/eng/common/templates/jobs/source-build.yml @@ -11,16 +11,14 @@ parameters: # See /eng/common/templates/job/source-build.yml jobNamePrefix: 'Source_Build' - # If changed to true, causes this template to include the default platform for a managed-only - # repo. The exact Docker image used for this build will be provided by Arcade. This has some risk, - # but since the repo is supposed to be managed-only, the risk should be very low. - includeDefaultManagedPlatform: false + # This is the default platform provided by Arcade, intended for use by a managed-only repo. defaultManagedPlatform: name: 'Managed' container: 'mcr.microsoft.com/dotnet-buildtools/prereqs:centos-7-3e800f1-20190501005343' # Defines the platforms on which to run build jobs. One job is created for each platform, and the - # object in this array is sent to the job template as 'platform'. + # object in this array is sent to the job template as 'platform'. If no platforms are specified, + # one job runs on 'defaultManagedPlatform'. platforms: [] jobs: @@ -32,7 +30,7 @@ jobs: dependsOn: - ${{ each platform in parameters.platforms }}: - ${{ parameters.jobNamePrefix }}_${{ platform.name }} - - ${{ if eq(parameters.includeDefaultManagedPlatform, true) }}: + - ${{ if eq(length(parameters.platforms), 0) }}: - ${{ parameters.jobNamePrefix }}_${{ parameters.defaultManagedPlatform.name }} - ${{ each platform in parameters.platforms }}: @@ -41,7 +39,7 @@ jobs: jobNamePrefix: ${{ parameters.jobNamePrefix }} platform: ${{ platform }} -- ${{ if eq(parameters.includeDefaultManagedPlatform, true) }}: +- ${{ if eq(length(parameters.platforms), 0) }}: - template: /eng/common/templates/job/source-build.yml parameters: jobNamePrefix: ${{ parameters.jobNamePrefix }} diff --git a/eng/common/templates/phases/base.yml b/eng/common/templates/phases/base.yml deleted file mode 100644 index 0123cf43b16..00000000000 --- a/eng/common/templates/phases/base.yml +++ /dev/null @@ -1,130 +0,0 @@ -parameters: - # Optional: Clean sources before building - clean: true - - # Optional: Git fetch depth - fetchDepth: '' - - # Optional: name of the phase (not specifying phase name may cause name collisions) - name: '' - # Optional: display name of the phase - displayName: '' - - # Optional: condition for the job to run - condition: '' - - # Optional: dependencies of the phase - dependsOn: '' - - # Required: A defined YAML queue - queue: {} - - # Required: build steps - steps: [] - - # Optional: variables - variables: {} - - # Optional: should run as a public build even in the internal project - # if 'true', the build won't run any of the internal only steps, even if it is running in non-public projects. - runAsPublic: false - - ## Telemetry variables - - # Optional: enable sending telemetry - # if 'true', these "variables" must be specified in the variables object or as part of the queue matrix - # _HelixBuildConfig - differentiate between Debug, Release, other - # _HelixSource - Example: build/product - # _HelixType - Example: official/dotnet/arcade/$(Build.SourceBranch) - enableTelemetry: false - - # Optional: Enable installing Microbuild plugin - # if 'true', these "variables" must be specified in the variables object or as part of the queue matrix - # _TeamName - the name of your team - # _SignType - 'test' or 'real' - enableMicrobuild: false - -# Internal resources (telemetry, microbuild) can only be accessed from non-public projects, -# and some (Microbuild) should only be applied to non-PR cases for internal builds. - -phases: -- phase: ${{ parameters.name }} - - ${{ if ne(parameters.displayName, '') }}: - displayName: ${{ parameters.displayName }} - - ${{ if ne(parameters.condition, '') }}: - condition: ${{ parameters.condition }} - - ${{ if ne(parameters.dependsOn, '') }}: - dependsOn: ${{ parameters.dependsOn }} - - queue: ${{ parameters.queue }} - - ${{ if ne(parameters.variables, '') }}: - variables: - ${{ insert }}: ${{ parameters.variables }} - - steps: - - checkout: self - clean: ${{ parameters.clean }} - ${{ if ne(parameters.fetchDepth, '') }}: - fetchDepth: ${{ parameters.fetchDepth }} - - - ${{ if eq(parameters.enableTelemetry, 'true') }}: - - template: /eng/common/templates/steps/telemetry-start.yml - parameters: - buildConfig: $(_HelixBuildConfig) - helixSource: $(_HelixSource) - helixType: $(_HelixType) - runAsPublic: ${{ parameters.runAsPublic }} - - - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - # Internal only resource, and Microbuild signing shouldn't be applied to PRs. - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: MicroBuildSigningPlugin@2 - displayName: Install MicroBuild plugin - inputs: - signType: $(_SignType) - zipSources: false - feedSource: https://dnceng.pkgs.visualstudio.com/_packaging/MicroBuildToolset/nuget/v3/index.json - - env: - TeamName: $(_TeamName) - continueOnError: false - condition: and(succeeded(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - - # Run provided build steps - - ${{ parameters.steps }} - - - ${{ if eq(parameters.enableMicrobuild, 'true') }}: - # Internal only resources - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: MicroBuildCleanup@1 - displayName: Execute Microbuild cleanup tasks - condition: and(always(), in(variables['_SignType'], 'real', 'test'), eq(variables['Agent.Os'], 'Windows_NT')) - env: - TeamName: $(_TeamName) - - - ${{ if eq(parameters.enableTelemetry, 'true') }}: - - template: /eng/common/templates/steps/telemetry-end.yml - parameters: - helixSource: $(_HelixSource) - helixType: $(_HelixType) - - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: CopyFiles@2 - displayName: Gather Asset Manifests - inputs: - SourceFolder: '$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/AssetManifest' - TargetFolder: '$(Build.StagingDirectory)/AssetManifests' - continueOnError: false - condition: and(succeeded(), eq(variables['_DotNetPublishToBlobFeed'], 'true')) - - task: PublishBuildArtifacts@1 - displayName: Push Asset Manifests - inputs: - PathtoPublish: '$(Build.StagingDirectory)/AssetManifests' - PublishLocation: Container - ArtifactName: AssetManifests - continueOnError: false - condition: and(succeeded(), eq(variables['_DotNetPublishToBlobFeed'], 'true')) diff --git a/eng/common/templates/phases/publish-build-assets.yml b/eng/common/templates/phases/publish-build-assets.yml deleted file mode 100644 index a0a8074282a..00000000000 --- a/eng/common/templates/phases/publish-build-assets.yml +++ /dev/null @@ -1,51 +0,0 @@ -parameters: - dependsOn: '' - queue: {} - configuration: 'Debug' - condition: succeeded() - continueOnError: false - runAsPublic: false - publishUsingPipelines: false -phases: - - phase: Asset_Registry_Publish - displayName: Publish to Build Asset Registry - dependsOn: ${{ parameters.dependsOn }} - queue: ${{ parameters.queue }} - variables: - _BuildConfig: ${{ parameters.configuration }} - steps: - - ${{ if and(eq(parameters.runAsPublic, 'false'), ne(variables['System.TeamProject'], 'public'), notin(variables['Build.Reason'], 'PullRequest')) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download artifact - inputs: - artifactName: AssetManifests - downloadPath: '$(Build.StagingDirectory)/Download' - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - - task: AzureKeyVault@1 - inputs: - azureSubscription: 'DotNet-Engineering-Services_KeyVault' - KeyVaultName: EngKeyVault - SecretsFilter: 'MaestroAccessToken' - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - - task: PowerShell@2 - displayName: Publish Build Assets - inputs: - filePath: eng\common\sdk-task.ps1 - arguments: -task PublishBuildAssets -restore -msbuildEngine dotnet - /p:ManifestsPath='$(Build.StagingDirectory)/Download/AssetManifests' - /p:BuildAssetRegistryToken=$(MaestroAccessToken) - /p:MaestroApiEndpoint=https://maestro-prod.westus2.cloudapp.azure.com - /p:PublishUsingPipelines=${{ parameters.publishUsingPipelines }} - /p:Configuration=$(_BuildConfig) - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - - task: PublishBuildArtifacts@1 - displayName: Publish Logs to VSTS - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)' - PublishLocation: Container - ArtifactName: $(Agent.Os)_Asset_Registry_Publish - continueOnError: true - condition: always() diff --git a/eng/common/templates/post-build/channels/generic-internal-channel.yml b/eng/common/templates/post-build/channels/generic-internal-channel.yml index 7ae5255921a..8990dfc8c87 100644 --- a/eng/common/templates/post-build/channels/generic-internal-channel.yml +++ b/eng/common/templates/post-build/channels/generic-internal-channel.yml @@ -40,6 +40,9 @@ stages: pool: vmImage: 'windows-2019' steps: + - script: echo "##vso[task.logissue type=warning]Going forward, v2 Arcade publishing is no longer supported. Please read https://github.com/dotnet/arcade/blob/main/Documentation/CorePackages/Publishing.md for details, then contact dnceng if you have further questions." + displayName: Warn about v2 Arcade Publishing Usage + # This is necessary whenever we want to publish/restore to an AzDO private feed - task: NuGetAuthenticate@0 displayName: 'Authenticate to AzDO Feeds' @@ -58,6 +61,7 @@ stages: PdbArtifacts/** BlobArtifacts/** downloadPath: '$(Build.ArtifactStagingDirectory)' + checkDownloadedFiles: true # This is necessary whenever we want to publish/restore to an AzDO private feed # Since sdk-task.ps1 tries to restore packages we need to do this authentication here @@ -109,6 +113,9 @@ stages: pool: vmImage: 'windows-2019' steps: + - script: echo "##vso[task.logissue type=warning]Going forward, v2 Arcade publishing is no longer supported. Please read https://github.com/dotnet/arcade/blob/main/Documentation/CorePackages/Publishing.md for details, then contact dnceng if you have further questions." + displayName: Warn about v2 Arcade Publishing Usage + - task: DownloadBuildArtifacts@0 displayName: Download Build Assets continueOnError: true @@ -124,6 +131,7 @@ stages: BlobArtifacts/** AssetManifests/** downloadPath: '$(Build.ArtifactStagingDirectory)' + checkDownloadedFiles: true - task: NuGetToolInstaller@1 displayName: 'Install NuGet.exe' diff --git a/eng/common/templates/post-build/channels/generic-public-channel.yml b/eng/common/templates/post-build/channels/generic-public-channel.yml index 6cf39dbb290..3220c6a4f92 100644 --- a/eng/common/templates/post-build/channels/generic-public-channel.yml +++ b/eng/common/templates/post-build/channels/generic-public-channel.yml @@ -42,6 +42,9 @@ stages: pool: vmImage: 'windows-2019' steps: + - script: echo "##vso[task.logissue type=warning]Going forward, v2 Arcade publishing is no longer supported. Please read https://github.com/dotnet/arcade/blob/main/Documentation/CorePackages/Publishing.md for details, then contact dnceng if you have further questions." + displayName: Warn about v2 Arcade Publishing Usage + - task: DownloadBuildArtifacts@0 displayName: Download Build Assets continueOnError: true @@ -56,6 +59,7 @@ stages: PdbArtifacts/** BlobArtifacts/** downloadPath: '$(Build.ArtifactStagingDirectory)' + checkDownloadedFiles: true # This is necessary whenever we want to publish/restore to an AzDO private feed # Since sdk-task.ps1 tries to restore packages we need to do this authentication here @@ -108,6 +112,9 @@ stages: pool: vmImage: 'windows-2019' steps: + - script: echo "##vso[task.logissue type=warning]Going forward, v2 Arcade publishing is no longer supported. Please read https://github.com/dotnet/arcade/blob/main/Documentation/CorePackages/Publishing.md for details, then contact dnceng if you have further questions." + displayName: Warn about v2 Arcade Publishing Usage + - task: DownloadBuildArtifacts@0 displayName: Download Build Assets continueOnError: true @@ -123,6 +130,7 @@ stages: BlobArtifacts/** AssetManifests/** downloadPath: '$(Build.ArtifactStagingDirectory)' + checkDownloadedFiles: true - task: NuGetToolInstaller@1 displayName: 'Install NuGet.exe' diff --git a/eng/common/templates/post-build/post-build.yml b/eng/common/templates/post-build/post-build.yml index 1b0af40d52f..bf9f2eb4617 100644 --- a/eng/common/templates/post-build/post-build.yml +++ b/eng/common/templates/post-build/post-build.yml @@ -32,7 +32,6 @@ parameters: symbolPublishingAdditionalParameters: '' artifactsPublishingAdditionalParameters: '' signingValidationAdditionalParameters: '' - useBuildManifest: false # Which stages should finish execution before post-build stages start validateDependsOn: @@ -54,9 +53,6 @@ parameters: NETCoreExperimentalChannelId: 562 NetEngServicesIntChannelId: 678 NetEngServicesProdChannelId: 679 - Net5Preview8ChannelId: 1155 - Net5RC1ChannelId: 1157 - Net5RC2ChannelId: 1329 NetCoreSDK313xxChannelId: 759 NetCoreSDK313xxInternalChannelId: 760 NetCoreSDK314xxChannelId: 921 @@ -96,7 +92,8 @@ stages: inputs: filePath: $(Build.SourcesDirectory)/eng/common/post-build/check-channel-consistency.ps1 arguments: -PromoteToChannels "$(TargetChannels)" - -AvailableChannelIds ${{parameters.NetEngLatestChannelId}},${{parameters.NetEngValidationChannelId}},${{parameters.NetDev5ChannelId}},${{parameters.NetDev6ChannelId}},${{parameters.GeneralTestingChannelId}},${{parameters.NETCoreToolingDevChannelId}},${{parameters.NETCoreToolingReleaseChannelId}},${{parameters.NETInternalToolingChannelId}},${{parameters.NETCoreExperimentalChannelId}},${{parameters.NetEngServicesIntChannelId}},${{parameters.NetEngServicesProdChannelId}},${{parameters.Net5Preview8ChannelId}},${{parameters.Net5RC1ChannelId}},${{parameters.Net5RC2ChannelId}},${{parameters.NetCoreSDK313xxChannelId}},${{parameters.NetCoreSDK313xxInternalChannelId}},${{parameters.NetCoreSDK314xxChannelId}},${{parameters.NetCoreSDK314xxInternalChannelId}},${{parameters.VS166ChannelId}},${{parameters.VS167ChannelId}},${{parameters.VS168ChannelId}},${{parameters.VSMasterChannelId}},${{parameters.VS169ChannelId}},${{parameters.VS1610ChannelId}} + -AvailableChannelIds ${{parameters.NetEngLatestChannelId}},${{parameters.NetEngValidationChannelId}},${{parameters.NetDev5ChannelId}},${{parameters.NetDev6ChannelId}},${{parameters.GeneralTestingChannelId}},${{parameters.NETCoreToolingDevChannelId}},${{parameters.NETCoreToolingReleaseChannelId}},${{parameters.NETInternalToolingChannelId}},${{parameters.NETCoreExperimentalChannelId}},${{parameters.NetEngServicesIntChannelId}},${{parameters.NetEngServicesProdChannelId}},${{parameters.NetCoreSDK313xxChannelId}},${{parameters.NetCoreSDK313xxInternalChannelId}},${{parameters.NetCoreSDK314xxChannelId}},${{parameters.NetCoreSDK314xxInternalChannelId}},${{parameters.VS166ChannelId}},${{parameters.VS167ChannelId}},${{parameters.VS168ChannelId}},${{parameters.VSMasterChannelId}},${{parameters.VS169ChannelId}},${{parameters.VS1610ChannelId}} + - job: displayName: NuGet Validation dependsOn: setupMaestroVars @@ -120,6 +117,7 @@ stages: pipeline: $(AzDOPipelineId) buildId: $(AzDOBuildId) artifactName: PackageArtifacts + checkDownloadedFiles: true - task: PowerShell@2 displayName: Validate @@ -131,7 +129,7 @@ stages: - job: displayName: Signing Validation dependsOn: setupMaestroVars - condition: eq( ${{ parameters.enableSigningValidation }}, 'true') + condition: and( eq( ${{ parameters.enableSigningValidation }}, 'true'), ne( variables['PostBuildSign'], 'true')) variables: - template: common-variables.yml - name: AzDOProjectName @@ -143,16 +141,6 @@ stages: pool: vmImage: 'windows-2019' steps: - - ${{ if eq(parameters.useBuildManifest, true) }}: - - task: DownloadBuildArtifacts@0 - displayName: Download build manifest - inputs: - buildType: specific - buildVersionToDownload: specific - project: $(AzDOProjectName) - pipeline: $(AzDOPipelineId) - buildId: $(AzDOBuildId) - artifactName: BuildManifests - task: DownloadBuildArtifacts@0 displayName: Download Package Artifacts inputs: @@ -162,6 +150,10 @@ stages: pipeline: $(AzDOPipelineId) buildId: $(AzDOBuildId) artifactName: PackageArtifacts + checkDownloadedFiles: true + itemPattern: | + ** + !**/Microsoft.SourceBuild.Intermediate.*.nupkg # This is necessary whenever we want to publish/restore to an AzDO private feed # Since sdk-task.ps1 tries to restore packages we need to do this authentication here @@ -174,6 +166,11 @@ stages: inputs: filePath: eng\common\enable-cross-org-publishing.ps1 arguments: -token $(dn-bot-dnceng-artifact-feeds-rw) + + - task: DeleteFiles@1 + inputs: + SourceFolder: $(Build.ArtifactStagingDirectory)/PackageArtifacts + Contents: Microsoft.SourceBuild.Intermediate* # Signing validation will optionally work with the buildmanifest file which is downloaded from # Azure DevOps above. @@ -215,6 +212,7 @@ stages: pipeline: $(AzDOPipelineId) buildId: $(AzDOBuildId) artifactName: BlobArtifacts + checkDownloadedFiles: true - task: PowerShell@2 displayName: Validate @@ -239,7 +237,7 @@ stages: - ${{ if or(ge(parameters.publishingInfraVersion, 3), eq(parameters.inline, 'false')) }}: - stage: publish_using_darc ${{ if or(eq(parameters.enableNugetValidation, 'true'), eq(parameters.enableSigningValidation, 'true'), eq(parameters.enableSourceLinkValidation, 'true'), eq(parameters.SDLValidationParameters.enable, 'true')) }}: - dependsOn: Validate + dependsOn: ${{ parameters.publishDependsOn }} ${{ if and(ne(parameters.enableNugetValidation, 'true'), ne(parameters.enableSigningValidation, 'true'), ne(parameters.enableSourceLinkValidation, 'true'), ne(parameters.SDLValidationParameters.enable, 'true')) }}: dependsOn: ${{ parameters.validateDependsOn }} displayName: Publish using Darc @@ -254,6 +252,7 @@ stages: - job: displayName: Publish Using Darc dependsOn: setupMaestroVars + timeoutInMinutes: 120 variables: - name: BARBuildId value: $[ dependencies.setupMaestroVars.outputs['setReleaseVars.BARBuildId'] ] @@ -270,6 +269,8 @@ stages: -MaestroToken '$(MaestroApiAccessToken)' -WaitPublishingFinish ${{ parameters.waitPublishingFinish }} -PublishInstallersAndChecksums ${{ parameters.publishInstallersAndChecksums }} + -ArtifactsPublishingAdditionalParameters '${{ parameters.artifactsPublishingAdditionalParameters }}' + -SymbolPublishingAdditionalParameters '${{ parameters.symbolPublishingAdditionalParameters }}' - ${{ if and(le(parameters.publishingInfraVersion, 2), eq(parameters.inline, 'true')) }}: - template: \eng\common\templates\post-build\channels\generic-public-channel.yml @@ -304,54 +305,6 @@ stages: shippingFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json' symbolsFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6-symbols/nuget/v3/index.json' - - template: \eng\common\templates\post-build\channels\generic-internal-channel.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} - dependsOn: ${{ parameters.publishDependsOn }} - publishInstallersAndChecksums: ${{ parameters.publishInstallersAndChecksums }} - symbolPublishingAdditionalParameters: ${{ parameters.symbolPublishingAdditionalParameters }} - stageName: 'Net5_Preview8_Publish' - channelName: '.NET 5 Preview 8' - akaMSChannelName: 'net5/preview8' - channelId: ${{ parameters.Net5Preview8ChannelId }} - transportFeed: 'https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet5-internal-transport/nuget/v3/index.json' - shippingFeed: 'https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet5-internal/nuget/v3/index.json' - symbolsFeed: 'https://pkgs.dev.azure.com/dnceng/internal/_packaging/dotnet5-internal-symbols/nuget/v3/index.json' - - - template: \eng\common\templates\post-build\channels\generic-public-channel.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} - dependsOn: ${{ parameters.publishDependsOn }} - publishInstallersAndChecksums: ${{ parameters.publishInstallersAndChecksums }} - symbolPublishingAdditionalParameters: ${{ parameters.symbolPublishingAdditionalParameters }} - stageName: 'Net5_RC1_Publish' - channelName: '.NET 5 RC 1' - akaMSChannelName: 'net5/rc1' - channelId: ${{ parameters.Net5RC1ChannelId }} - transportFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5-transport/nuget/v3/index.json' - shippingFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json' - symbolsFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5-symbols/nuget/v3/index.json' - - - template: \eng\common\templates\post-build\channels\generic-public-channel.yml - parameters: - BARBuildId: ${{ parameters.BARBuildId }} - PromoteToChannelIds: ${{ parameters.PromoteToChannelIds }} - artifactsPublishingAdditionalParameters: ${{ parameters.artifactsPublishingAdditionalParameters }} - dependsOn: ${{ parameters.publishDependsOn }} - publishInstallersAndChecksums: ${{ parameters.publishInstallersAndChecksums }} - symbolPublishingAdditionalParameters: ${{ parameters.symbolPublishingAdditionalParameters }} - stageName: 'Net5_RC2_Publish' - channelName: '.NET 5 RC 2' - akaMSChannelName: 'net5/rc2' - channelId: ${{ parameters.Net5RC2ChannelId }} - transportFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5-transport/nuget/v3/index.json' - shippingFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json' - symbolsFeed: 'https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5-symbols/nuget/v3/index.json' - - template: \eng\common\templates\post-build\channels\generic-public-channel.yml parameters: BARBuildId: ${{ parameters.BARBuildId }} diff --git a/eng/common/templates/post-build/setup-maestro-vars.yml b/eng/common/templates/post-build/setup-maestro-vars.yml index d0cbfb6c6ff..4a22b2e6f6d 100644 --- a/eng/common/templates/post-build/setup-maestro-vars.yml +++ b/eng/common/templates/post-build/setup-maestro-vars.yml @@ -18,6 +18,7 @@ jobs: inputs: buildType: current artifactName: ReleaseConfigs + checkDownloadedFiles: true - task: PowerShell@2 name: setReleaseVars diff --git a/eng/common/templates/steps/perf-send-to-helix.yml b/eng/common/templates/steps/perf-send-to-helix.yml deleted file mode 100644 index a468e92ce44..00000000000 --- a/eng/common/templates/steps/perf-send-to-helix.yml +++ /dev/null @@ -1,50 +0,0 @@ -# Please remember to update the documentation if you make changes to these parameters! -parameters: - ProjectFile: '' # required -- project file that specifies the helix workitems - HelixSource: 'pr/default' # required -- sources must start with pr/, official/, prodcon/, or agent/ - HelixType: 'tests/default/' # required -- Helix telemetry which identifies what type of data this is; should include "test" for clarity and must end in '/' - HelixBuild: $(Build.BuildNumber) # required -- the build number Helix will use to identify this -- automatically set to the AzDO build number - HelixTargetQueues: '' # required -- semicolon delimited list of Helix queues to test on; see https://helix.dot.net/ for a list of queues - HelixAccessToken: '' # required -- access token to make Helix API requests; should be provided by the appropriate variable group - HelixPreCommands: '' # optional -- commands to run before Helix work item execution - HelixPostCommands: '' # optional -- commands to run after Helix work item execution - WorkItemDirectory: '' # optional -- a payload directory to zip up and send to Helix; requires WorkItemCommand; incompatible with XUnitProjects - CorrelationPayloadDirectory: '' # optional -- a directory to zip up and send to Helix as a correlation payload - IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion - DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/master/release-notes/releases.json - DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/master/release-notes/releases.json - EnableXUnitReporter: false # optional -- true enables XUnit result reporting to Mission Control - WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget." - Creator: '' # optional -- if the build is external, use this to specify who is sending the job - DisplayNamePrefix: 'Send job to Helix' # optional -- rename the beginning of the displayName of the steps in AzDO - condition: succeeded() # optional -- condition for step to execute; defaults to succeeded() - continueOnError: false # optional -- determines whether to continue the build if the step errors; defaults to false - osGroup: '' # required -- operating system for the job - - -steps: -- template: /eng/pipelines/common/templates/runtimes/send-to-helix-inner-step.yml - parameters: - osGroup: ${{ parameters.osGroup }} - sendParams: $(Build.SourcesDirectory)/eng/common/performance/${{ parameters.ProjectFile }} /restore /t:Test /bl:$(Build.SourcesDirectory)/artifacts/log/$(_BuildConfig)/SendToHelix.binlog - displayName: ${{ parameters.DisplayNamePrefix }} - condition: ${{ parameters.condition }} - continueOnError: ${{ parameters.continueOnError }} - environment: - BuildConfig: $(_BuildConfig) - HelixSource: ${{ parameters.HelixSource }} - HelixType: ${{ parameters.HelixType }} - HelixBuild: ${{ parameters.HelixBuild }} - HelixTargetQueues: ${{ parameters.HelixTargetQueues }} - HelixAccessToken: ${{ parameters.HelixAccessToken }} - HelixPreCommands: ${{ parameters.HelixPreCommands }} - HelixPostCommands: ${{ parameters.HelixPostCommands }} - WorkItemDirectory: ${{ parameters.WorkItemDirectory }} - CorrelationPayloadDirectory: ${{ parameters.CorrelationPayloadDirectory }} - IncludeDotNetCli: ${{ parameters.IncludeDotNetCli }} - DotNetCliPackageType: ${{ parameters.DotNetCliPackageType }} - DotNetCliVersion: ${{ parameters.DotNetCliVersion }} - EnableXUnitReporter: ${{ parameters.EnableXUnitReporter }} - WaitForWorkItemCompletion: ${{ parameters.WaitForWorkItemCompletion }} - Creator: ${{ parameters.Creator }} - SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/eng/common/templates/steps/send-to-helix.yml b/eng/common/templates/steps/send-to-helix.yml index bb5f1a92938..cd02ae1607f 100644 --- a/eng/common/templates/steps/send-to-helix.yml +++ b/eng/common/templates/steps/send-to-helix.yml @@ -18,8 +18,8 @@ parameters: XUnitRuntimeTargetFramework: '' # optional -- framework to use for the xUnit console runner XUnitRunnerVersion: '' # optional -- version of the xUnit nuget package you wish to use on Helix; required for XUnitProjects IncludeDotNetCli: false # optional -- true will download a version of the .NET CLI onto the Helix machine as a correlation payload; requires DotNetCliPackageType and DotNetCliVersion - DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/master/release-notes/releases-index.json - DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/master/release-notes/releases-index.json + DotNetCliPackageType: '' # optional -- either 'sdk', 'runtime' or 'aspnetcore-runtime'; determines whether the sdk or runtime will be sent to Helix; see https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json + DotNetCliVersion: '' # optional -- version of the CLI to send to Helix; based on this: https://raw.githubusercontent.com/dotnet/core/main/release-notes/releases-index.json EnableXUnitReporter: false # optional -- true enables XUnit result reporting to Mission Control WaitForWorkItemCompletion: true # optional -- true will make the task wait until work items have been completed and fail the build if work items fail. False is "fire and forget." IsExternal: false # [DEPRECATED] -- doesn't do anything, jobs are external if HelixAccessToken is empty and Creator is set diff --git a/eng/common/templates/steps/source-build.yml b/eng/common/templates/steps/source-build.yml index 8e336b7d16b..ba40dc82f14 100644 --- a/eng/common/templates/steps/source-build.yml +++ b/eng/common/templates/steps/source-build.yml @@ -18,6 +18,35 @@ steps: set -x df -h + # If building on the internal project, the artifact feeds variable may be available (usually only if needed) + # In that case, call the feed setup script to add internal feeds corresponding to public ones. + # In addition, add an msbuild argument to copy the WIP from the repo to the target build location. + # This is because SetupNuGetSources.sh will alter the current NuGet.config file, and we need to preserve those + # changes. + $internalRestoreArgs= + if [ '$(dn-bot-dnceng-artifact-feeds-rw)' != '$''(dn-bot-dnceng-artifact-feeds-rw)' ]; then + # Temporarily work around https://github.com/dotnet/arcade/issues/7709 + chmod +x $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh + $(Build.SourcesDirectory)/eng/common/SetupNugetSources.sh $(Build.SourcesDirectory)/NuGet.config $(dn-bot-dnceng-artifact-feeds-rw) + internalRestoreArgs='/p:CopyWipIntoInnerSourceBuildRepo=true' + + # The 'Copy WIP' feature of source build uses git stash to apply changes from the original repo. + # This only works if there is a username/email configured, which won't be the case in most CI runs. + git config --get user.email + if [ $? -ne 0 ]; then + git config user.email dn-bot@microsoft.com + git config user.name dn-bot + fi + fi + + # If building on the internal project, the internal storage variable may be available (usually only if needed) + # In that case, add variables to allow the download of internal runtimes if the specified versions are not found + # in the default public locations. + internalRuntimeDownloadArgs= + if [ '$(dotnetclimsrc-read-sas-token-base64)' != '$''(dotnetclimsrc-read-sas-token-base64)' ]; then + internalRuntimeDownloadArgs='/p:DotNetRuntimeSourceFeed=https://dotnetclimsrc.blob.core.windows.net/dotnet /p:DotNetRuntimeSourceFeedKey=$(dotnetclimsrc-read-sas-token-base64) --runtimesourcefeed https://dotnetclimsrc.blob.core.windows.net/dotnet --runtimesourcefeedkey $(dotnetclimsrc-read-sas-token-base64)' + fi + buildConfig=Release # Check if AzDO substitutes in a build config from a variable, and use it if so. if [ '$(_BuildConfig)' != '$''(_BuildConfig)' ]; then @@ -34,10 +63,17 @@ steps: targetRidArgs='/p:TargetRid=${{ parameters.platform.targetRID }}' fi + publishArgs= + if [ '${{ parameters.platform.skipPublishValidation }}' != 'true' ]; then + publishArgs='--publish' + fi + ${{ coalesce(parameters.platform.buildScript, './build.sh') }} --ci \ --configuration $buildConfig \ - --restore --build --pack --publish \ + --restore --build --pack $publishArgs -bl \ $officialBuildArgs \ + $internalRuntimeDownloadArgs \ + $internalRestoreArgs \ $targetRidArgs \ /p:SourceBuildNonPortable=${{ parameters.platform.nonPortable }} \ /p:ArcadeBuildFromSource=true diff --git a/eng/common/tools.ps1 b/eng/common/tools.ps1 index 60eb601c8f3..02347914f5d 100644 --- a/eng/common/tools.ps1 +++ b/eng/common/tools.ps1 @@ -42,12 +42,15 @@ [bool]$useInstalledDotNetCli = if (Test-Path variable:useInstalledDotNetCli) { $useInstalledDotNetCli } else { $true } # Enable repos to use a particular version of the on-line dotnet-install scripts. -# default URL: https://dot.net/v1/dotnet-install.ps1 +# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1 [string]$dotnetInstallScriptVersion = if (Test-Path variable:dotnetInstallScriptVersion) { $dotnetInstallScriptVersion } else { 'v1' } # True to use global NuGet cache instead of restoring packages to repository-local directory. [bool]$useGlobalNuGetCache = if (Test-Path variable:useGlobalNuGetCache) { $useGlobalNuGetCache } else { !$ci } +# True to exclude prerelease versions Visual Studio during build +[bool]$excludePrereleaseVS = if (Test-Path variable:excludePrereleaseVS) { $excludePrereleaseVS } else { $false } + # An array of names of processes to stop on script exit if prepareMachine is true. $processesToStopOnExit = if (Test-Path variable:processesToStopOnExit) { $processesToStopOnExit } else { @('msbuild', 'dotnet', 'vbcscompiler') } @@ -57,7 +60,7 @@ set-strictmode -version 2.0 $ErrorActionPreference = 'Stop' [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -# If specified, provides an alternate path for getting .NET Core SDKs and Runtimes. This script will still try public sources first. +# If specifies, provides an alternate path for getting .NET Core SDKs and Runtimes. This script will still try public sources first. [string]$runtimeSourceFeed = if (Test-Path variable:runtimeSourceFeed) { $runtimeSourceFeed } else { $null } # Base-64 encoded SAS token that has permission to storage container described by $runtimeSourceFeed [string]$runtimeSourceFeedKey = if (Test-Path variable:runtimeSourceFeedKey) { $runtimeSourceFeedKey } else { $null } @@ -103,6 +106,46 @@ function Exec-Process([string]$command, [string]$commandArgs) { } } +# Take the given block, print it, print what the block probably references from the current set of +# variables using low-effort string matching, then run the block. +# +# This is intended to replace the pattern of manually copy-pasting a command, wrapping it in quotes, +# and printing it using "Write-Host". The copy-paste method is more readable in build logs, but less +# maintainable and less reliable. It is easy to make a mistake and modify the command without +# properly updating the "Write-Host" line, resulting in misleading build logs. The probability of +# this mistake makes the pattern hard to trust when it shows up in build logs. Finding the bug in +# existing source code can also be difficult, because the strings are not aligned to each other and +# the line may be 300+ columns long. +# +# By removing the need to maintain two copies of the command, Exec-BlockVerbosely avoids the issues. +# +# In Bash (or any posix-like shell), "set -x" prints usable verbose output automatically. +# "Set-PSDebug" appears to be similar at first glance, but unfortunately, it isn't very useful: it +# doesn't print any info about the variables being used by the command, which is normally the +# interesting part to diagnose. +function Exec-BlockVerbosely([scriptblock] $block) { + Write-Host "--- Running script block:" + $blockString = $block.ToString().Trim() + Write-Host $blockString + + Write-Host "--- List of variables that might be used:" + # For each variable x in the environment, check the block for a reference to x via simple "$x" or + # "@x" syntax. This doesn't detect other ways to reference variables ("${x}" nor "$variable:x", + # among others). It only catches what this function was originally written for: simple + # command-line commands. + $variableTable = Get-Variable | + Where-Object { + $blockString.Contains("`$$($_.Name)") -or $blockString.Contains("@$($_.Name)") + } | + Format-Table -AutoSize -HideTableHeaders -Wrap | + Out-String + Write-Host $variableTable.Trim() + + Write-Host "--- Executing:" + & $block + Write-Host "--- Done running script block!" +} + # createSdkLocationFile parameter enables a file being generated under the toolset directory # which writes the sdk's location into. This is only necessary for cmd --> powershell invocations # as dot sourcing isn't possible. @@ -120,6 +163,9 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { # Disable telemetry on CI. if ($ci) { $env:DOTNET_CLI_TELEMETRY_OPTOUT=1 + + # In case of network error, try to log the current IP for reference + Try-LogClientIpAddress } # Source Build uses DotNetCoreSdkDir variable @@ -141,7 +187,7 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { # Use dotnet installation specified in DOTNET_INSTALL_DIR if it contains the required SDK version, # otherwise install the dotnet CLI and SDK to repo local .dotnet directory to avoid potential permission issues. - if ((-not $globalJsonHasRuntimes) -and ($env:DOTNET_INSTALL_DIR -ne $null) -and (Test-Path(Join-Path $env:DOTNET_INSTALL_DIR "sdk\$dotnetSdkVersion"))) { + if ((-not $globalJsonHasRuntimes) -and (-not [string]::IsNullOrEmpty($env:DOTNET_INSTALL_DIR)) -and (Test-Path(Join-Path $env:DOTNET_INSTALL_DIR "sdk\$dotnetSdkVersion"))) { $dotnetRoot = $env:DOTNET_INSTALL_DIR } else { $dotnetRoot = Join-Path $RepoRoot '.dotnet' @@ -169,7 +215,7 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { Set-Content -Path $sdkCacheFileTemp -Value $dotnetRoot try { - Rename-Item -Force -Path $sdkCacheFileTemp 'sdk.txt' + Move-Item -Force $sdkCacheFileTemp (Join-Path $ToolsetDir 'sdk.txt') } catch { # Somebody beat us Remove-Item -Path $sdkCacheFileTemp @@ -190,38 +236,42 @@ function InitializeDotNetCli([bool]$install, [bool]$createSdkLocationFile) { return $global:_DotNetInstallDir = $dotnetRoot } +function Retry($downloadBlock, $maxRetries = 5) { + $retries = 1 + + while($true) { + try { + & $downloadBlock + break + } + catch { + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message $_ + } + + if (++$retries -le $maxRetries) { + $delayInSeconds = [math]::Pow(2, $retries) - 1 # Exponential backoff + Write-Host "Retrying. Waiting for $delayInSeconds seconds before next attempt ($retries of $maxRetries)." + Start-Sleep -Seconds $delayInSeconds + } + else { + Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Unable to download file in $maxRetries attempts." + break + } + + } +} + function GetDotNetInstallScript([string] $dotnetRoot) { $installScript = Join-Path $dotnetRoot 'dotnet-install.ps1' if (!(Test-Path $installScript)) { Create-Directory $dotnetRoot $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit + $uri = "https://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.ps1" - $maxRetries = 5 - $retries = 1 - - $uri = "https://dot.net/$dotnetInstallScriptVersion/dotnet-install.ps1" - - while($true) { - try { - Write-Host "GET $uri" - Invoke-WebRequest $uri -OutFile $installScript - break - } - catch { - Write-Host "Failed to download '$uri'" - Write-Error $_.Exception.Message -ErrorAction Continue - } - - if (++$retries -le $maxRetries) { - $delayInSeconds = [math]::Pow(2, $retries) - 1 # Exponential backoff - Write-Host "Retrying. Waiting for $delayInSeconds seconds before next attempt ($retries of $maxRetries)." - Start-Sleep -Seconds $delayInSeconds - } - else { - throw "Unable to download file in $maxRetries attempts." - } - - } + Retry({ + Write-Host "GET $uri" + Invoke-WebRequest $uri -OutFile $installScript + }) } return $installScript @@ -305,8 +355,8 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = # If the version of msbuild is going to be xcopied, # use this version. Version matches a package here: - # https://dev.azure.com/dnceng/public/_packaging?_a=package&feed=dotnet-eng&package=RoslynTools.MSBuild&protocolType=NuGet&version=16.8.0-preview3&view=overview - $defaultXCopyMSBuildVersion = '16.8.0-preview3' + # https://dev.azure.com/dnceng/public/_packaging?_a=package&feed=dotnet-eng&package=RoslynTools.MSBuild&protocolType=NuGet&version=16.10.0-preview2&view=overview + $defaultXCopyMSBuildVersion = '16.10.0-preview2' if (!$vsRequirements) { $vsRequirements = $GlobalJson.tools.vs } $vsMinVersionStr = if ($vsRequirements.version) { $vsRequirements.version } else { $vsMinVersionReqdStr } @@ -371,7 +421,16 @@ function InitializeVisualStudioMSBuild([bool]$install, [object]$vsRequirements = } $msbuildVersionDir = if ([int]$vsMajorVersion -lt 16) { "$vsMajorVersion.0" } else { "Current" } - return $global:_MSBuildExe = Join-Path $vsInstallDir "MSBuild\$msbuildVersionDir\Bin\msbuild.exe" + + $local:BinFolder = Join-Path $vsInstallDir "MSBuild\$msbuildVersionDir\Bin" + $local:Prefer64bit = if (Get-Member -InputObject $vsRequirements -Name 'Prefer64bit') { $vsRequirements.Prefer64bit } else { $false } + if ($local:Prefer64bit -and (Test-Path(Join-Path $local:BinFolder "amd64"))) { + $global:_MSBuildExe = Join-Path $local:BinFolder "amd64\msbuild.exe" + } else { + $global:_MSBuildExe = Join-Path $local:BinFolder "msbuild.exe" + } + + return $global:_MSBuildExe } function InitializeVisualStudioEnvironmentVariables([string] $vsInstallDir, [string] $vsMajorVersion) { @@ -400,9 +459,13 @@ function InitializeXCopyMSBuild([string]$packageVersion, [bool]$install) { } Create-Directory $packageDir + Write-Host "Downloading $packageName $packageVersion" $ProgressPreference = 'SilentlyContinue' # Don't display the console progress UI - it's a huge perf hit - Invoke-WebRequest "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/flat2/$packageName/$packageVersion/$packageName.$packageVersion.nupkg" -OutFile $packagePath + Retry({ + Invoke-WebRequest "https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/flat2/$packageName/$packageVersion/$packageName.$packageVersion.nupkg" -OutFile $packagePath + }) + Unzip $packagePath $packageDir } @@ -439,16 +502,17 @@ function LocateVisualStudio([object]$vsRequirements = $null){ if (!(Test-Path $vsWhereExe)) { Create-Directory $vsWhereDir Write-Host 'Downloading vswhere' - try { + Retry({ Invoke-WebRequest "https://netcorenativeassets.blob.core.windows.net/resource-packages/external/windows/vswhere/$vswhereVersion/vswhere.exe" -OutFile $vswhereExe - } - catch { - Write-PipelineTelemetryError -Category 'InitializeToolset' -Message $_ - } + }) } if (!$vsRequirements) { $vsRequirements = $GlobalJson.tools.vs } - $args = @('-latest', '-prerelease', '-format', 'json', '-requires', 'Microsoft.Component.MSBuild', '-products', '*') + $args = @('-latest', '-format', 'json', '-requires', 'Microsoft.Component.MSBuild', '-products', '*') + + if (!$excludePrereleaseVS) { + $args += '-prerelease' + } if (Get-Member -InputObject $vsRequirements -Name 'version') { $args += '-version' @@ -474,7 +538,13 @@ function LocateVisualStudio([object]$vsRequirements = $null){ function InitializeBuildTool() { if (Test-Path variable:global:_BuildTool) { - return $global:_BuildTool + # If the requested msbuild parameters do not match, clear the cached variables. + if($global:_BuildTool.Contains('ExcludePrereleaseVS') -and $global:_BuildTool.ExcludePrereleaseVS -ne $excludePrereleaseVS) { + Remove-Item variable:global:_BuildTool + Remove-Item variable:global:_MSBuildExe + } else { + return $global:_BuildTool + } } if (-not $msbuildEngine) { @@ -493,7 +563,7 @@ function InitializeBuildTool() { ExitWithExitCode 1 } $dotnetPath = Join-Path $dotnetRoot (GetExecutableFileName 'dotnet') - $buildTool = @{ Path = $dotnetPath; Command = 'msbuild'; Tool = 'dotnet'; Framework = 'netcoreapp2.1' } + $buildTool = @{ Path = $dotnetPath; Command = 'msbuild'; Tool = 'dotnet'; Framework = 'netcoreapp3.1' } } elseif ($msbuildEngine -eq "vs") { try { $msbuildPath = InitializeVisualStudioMSBuild -install:$restore @@ -502,7 +572,7 @@ function InitializeBuildTool() { ExitWithExitCode 1 } - $buildTool = @{ Path = $msbuildPath; Command = ""; Tool = "vs"; Framework = "net472" } + $buildTool = @{ Path = $msbuildPath; Command = ""; Tool = "vs"; Framework = "net472"; ExcludePrereleaseVS = $excludePrereleaseVS } } else { Write-PipelineTelemetryError -Category 'InitializeToolset' -Message "Unexpected value of -msbuildEngine: '$msbuildEngine'." ExitWithExitCode 1 @@ -527,7 +597,7 @@ function GetDefaultMSBuildEngine() { function GetNuGetPackageCachePath() { if ($env:NUGET_PACKAGES -eq $null) { - # Use local cache on CI to ensure deterministic build. + # Use local cache on CI to ensure deterministic build. # Avoid using the http cache as workaround for https://github.com/NuGet/Home/issues/3116 # use global cache in dev builds to avoid cost of downloading packages. # For directory normalization, see also: https://github.com/NuGet/Home/issues/7968 @@ -605,6 +675,17 @@ function ExitWithExitCode([int] $exitCode) { exit $exitCode } +# Check if $LASTEXITCODE is a nonzero exit code (NZEC). If so, print a Azure Pipeline error for +# diagnostics, then exit the script with the $LASTEXITCODE. +function Exit-IfNZEC([string] $category = "General") { + Write-Host "Exit code $LASTEXITCODE" + if ($LASTEXITCODE -ne 0) { + $message = "Last command failed with exit code $LASTEXITCODE." + Write-PipelineTelemetryError -Force -Category $category -Message $message + ExitWithExitCode $LASTEXITCODE + } +} + function Stop-Processes() { Write-Host 'Killing running build processes...' foreach ($processName in $processesToStopOnExit) { @@ -628,10 +709,38 @@ function MSBuild() { Write-PipelineSetVariable -Name 'NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS' -Value '20' } + if ($ci) { + $env:NUGET_ENABLE_EXPERIMENTAL_HTTP_RETRY = 'true' + $env:NUGET_EXPERIMENTAL_MAX_NETWORK_TRY_COUNT = 6 + $env:NUGET_EXPERIMENTAL_NETWORK_RETRY_DELAY_MILLISECONDS = 1000 + Write-PipelineSetVariable -Name 'NUGET_ENABLE_EXPERIMENTAL_HTTP_RETRY' -Value 'true' + Write-PipelineSetVariable -Name 'NUGET_EXPERIMENTAL_MAX_NETWORK_TRY_COUNT' -Value '6' + Write-PipelineSetVariable -Name 'NUGET_EXPERIMENTAL_NETWORK_RETRY_DELAY_MILLISECONDS' -Value '1000' + } + $toolsetBuildProject = InitializeToolset - $path = Split-Path -parent $toolsetBuildProject - $path = Join-Path $path (Join-Path $buildTool.Framework 'Microsoft.DotNet.Arcade.Sdk.dll') - $args += "/logger:$path" + $basePath = Split-Path -parent $toolsetBuildProject + $possiblePaths = @( + # new scripts need to work with old packages, so we need to look for the old names/versions + (Join-Path $basePath (Join-Path $buildTool.Framework 'Microsoft.DotNet.ArcadeLogging.dll')), + (Join-Path $basePath (Join-Path $buildTool.Framework 'Microsoft.DotNet.Arcade.Sdk.dll')), + (Join-Path $basePath (Join-Path netcoreapp2.1 'Microsoft.DotNet.ArcadeLogging.dll')), + (Join-Path $basePath (Join-Path netcoreapp2.1 'Microsoft.DotNet.Arcade.Sdk.dll')) + (Join-Path $basePath (Join-Path netcoreapp3.1 'Microsoft.DotNet.ArcadeLogging.dll')), + (Join-Path $basePath (Join-Path netcoreapp3.1 'Microsoft.DotNet.Arcade.Sdk.dll')) + ) + $selectedPath = $null + foreach ($path in $possiblePaths) { + if (Test-Path $path -PathType Leaf) { + $selectedPath = $path + break + } + } + if (-not $selectedPath) { + Write-PipelineTelemetryError -Category 'Build' -Message 'Unable to find arcade sdk logger assembly.' + ExitWithExitCode 1 + } + $args += "/logger:$selectedPath" } MSBuild-Core @args @@ -667,7 +776,10 @@ function MSBuild-Core() { } foreach ($arg in $args) { - if ($arg -ne $null -and $arg.Trim() -ne "") { + if ($null -ne $arg -and $arg.Trim() -ne "") { + if ($arg.EndsWith('\')) { + $arg = $arg + "\" + } $cmdArgs += " `"$arg`"" } } @@ -677,14 +789,23 @@ function MSBuild-Core() { $exitCode = Exec-Process $buildTool.Path $cmdArgs if ($exitCode -ne 0) { - Write-PipelineTelemetryError -Category 'Build' -Message 'Build failed.' + # We should not Write-PipelineTaskError here because that message shows up in the build summary + # The build already logged an error, that's the reason it failed. Producing an error here only adds noise. + Write-Host "Build failed with exit code $exitCode. Check errors above." -ForegroundColor Red $buildLog = GetMSBuildBinaryLogCommandLineArgument $args - if ($buildLog -ne $null) { + if ($null -ne $buildLog) { Write-Host "See log: $buildLog" -ForegroundColor DarkGray } - ExitWithExitCode $exitCode + if ($ci) { + Write-PipelineSetResult -Result "Failed" -Message "msbuild execution failed." + # Exiting with an exit code causes the azure pipelines task to log yet another "noise" error + # The above Write-PipelineSetResult will cause the task to be marked as failure without adding yet another error + ExitWithExitCode 0 + } else { + ExitWithExitCode $exitCode + } } } @@ -730,7 +851,7 @@ function Get-Darc($version) { . $PSScriptRoot\pipeline-logging-functions.ps1 -$RepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..\..') +$RepoRoot = Resolve-Path (Join-Path $PSScriptRoot '..\..\') $EngRoot = Resolve-Path (Join-Path $PSScriptRoot '..') $ArtifactsDir = Join-Path $RepoRoot 'artifacts' $ToolsetDir = Join-Path $ArtifactsDir 'toolset' @@ -765,3 +886,21 @@ if (!$disableConfigureToolsetImport) { } } } + +function Try-LogClientIpAddress() +{ + Write-Host "Attempting to log this client's IP for Azure Package feed telemetry purposes" + try + { + $result = Invoke-WebRequest -Uri "http://co1.msedge.net/fdv2/diagnostics.aspx" -UseBasicParsing + $lines = $result.Content.Split([Environment]::NewLine) + $socketIp = $lines | Select-String -Pattern "^Socket IP:.*" + Write-Host $socketIp + $clientIp = $lines | Select-String -Pattern "^Client IP:.*" + Write-Host $clientIp + } + catch + { + Write-Host "Unable to get this machine's effective IP address for logging: $_" + } +} diff --git a/eng/common/tools.sh b/eng/common/tools.sh index 98186e78496..6a4871ef72b 100755 --- a/eng/common/tools.sh +++ b/eng/common/tools.sh @@ -54,7 +54,7 @@ warn_as_error=${warn_as_error:-true} use_installed_dotnet_cli=${use_installed_dotnet_cli:-true} # Enable repos to use a particular version of the on-line dotnet-install scripts. -# default URL: https://dot.net/v1/dotnet-install.sh +# default URL: https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.sh dotnetInstallScriptVersion=${dotnetInstallScriptVersion:-'v1'} # True to use global NuGet cache instead of restoring packages to repository-local directory. @@ -89,16 +89,16 @@ function ResolvePath { function ReadGlobalVersion { local key=$1 - local line=$(awk "/$key/ {print; exit}" "$global_json_file") - local pattern="\"$key\" *: *\"(.*)\"" + if command -v jq &> /dev/null; then + _ReadGlobalVersion="$(jq -r ".[] | select(has(\"$key\")) | .\"$key\"" "$global_json_file")" + elif [[ "$(cat "$global_json_file")" =~ \"$key\"[[:space:]\:]*\"([^\"]+) ]]; then + _ReadGlobalVersion=${BASH_REMATCH[1]} + fi - if [[ ! $line =~ $pattern ]]; then + if [[ -z "$_ReadGlobalVersion" ]]; then Write-PipelineTelemetryError -category 'Build' "Error: Cannot find \"$key\" in $global_json_file" ExitWithExitCode 1 fi - - # return value - _ReadGlobalVersion=${BASH_REMATCH[1]} } function InitializeDotNetCli { @@ -249,7 +249,7 @@ function with_retries { return 0 fi - timeout=$((2**$retries-1)) + timeout=$((3**$retries-1)) echo "Failed to execute '$@'. Waiting $timeout seconds before next attempt ($retries out of $maxRetries)." 1>&2 sleep $timeout done @@ -262,7 +262,7 @@ function with_retries { function GetDotNetInstallScript { local root=$1 local install_script="$root/dotnet-install.sh" - local install_script_url="https://dot.net/$dotnetInstallScriptVersion/dotnet-install.sh" + local install_script_url="https://dotnet.microsoft.com/download/dotnet/scripts/$dotnetInstallScriptVersion/dotnet-install.sh" if [[ ! -a "$install_script" ]]; then mkdir -p "$root" @@ -271,10 +271,18 @@ function GetDotNetInstallScript { # Use curl if available, otherwise use wget if command -v curl > /dev/null; then - with_retries curl "$install_script_url" -sSL --retry 10 --create-dirs -o "$install_script" || { - local exit_code=$? - Write-PipelineTelemetryError -category 'InitializeToolset' "Failed to acquire dotnet install script (exit code '$exit_code')." - ExitWithExitCode $exit_code + # first, try directly, if this fails we will retry with verbose logging + curl "$install_script_url" -sSL --retry 10 --create-dirs -o "$install_script" || { + if command -v openssl &> /dev/null; then + echo "Curl failed; dumping some information about dotnet.microsoft.com for later investigation" + echo | openssl s_client -showcerts -servername dotnet.microsoft.com -connect dotnet.microsoft.com:443 + fi + echo "Will now retry the same URL with verbose logging." + with_retries curl "$install_script_url" -sSL --verbose --retry 10 --create-dirs -o "$install_script" || { + local exit_code=$? + Write-PipelineTelemetryError -category 'InitializeToolset' "Failed to acquire dotnet install script (exit code '$exit_code')." + ExitWithExitCode $exit_code + } } else with_retries wget -v -O "$install_script" "$install_script_url" || { @@ -298,7 +306,7 @@ function InitializeBuildTool { # return values _InitializeBuildTool="$_InitializeDotNetCli/dotnet" _InitializeBuildToolCommand="msbuild" - _InitializeBuildToolFramework="netcoreapp2.1" + _InitializeBuildToolFramework="netcoreapp3.1" } # Set RestoreNoCache as a workaround for https://github.com/NuGet/Home/issues/3116 @@ -391,6 +399,13 @@ function StopProcesses { return 0 } +function TryLogClientIpAddress () { + echo 'Attempting to log this client''s IP for Azure Package feed telemetry purposes' + if command -v curl > /dev/null; then + curl -s 'http://co1.msedge.net/fdv2/diagnostics.aspx' | grep ' IP: ' || true + fi +} + function MSBuild { local args=$@ if [[ "$pipelines_log" == true ]]; then @@ -402,11 +417,36 @@ function MSBuild { export NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS=20 Write-PipelineSetVariable -name "NUGET_PLUGIN_HANDSHAKE_TIMEOUT_IN_SECONDS" -value "20" Write-PipelineSetVariable -name "NUGET_PLUGIN_REQUEST_TIMEOUT_IN_SECONDS" -value "20" + + export NUGET_ENABLE_EXPERIMENTAL_HTTP_RETRY=true + export NUGET_EXPERIMENTAL_MAX_NETWORK_TRY_COUNT=6 + export NUGET_EXPERIMENTAL_NETWORK_RETRY_DELAY_MILLISECONDS=1000 + Write-PipelineSetVariable -name "NUGET_ENABLE_EXPERIMENTAL_HTTP_RETRY" -value "true" + Write-PipelineSetVariable -name "NUGET_EXPERIMENTAL_MAX_NETWORK_TRY_COUNT" -value "6" + Write-PipelineSetVariable -name "NUGET_EXPERIMENTAL_NETWORK_RETRY_DELAY_MILLISECONDS" -value "1000" fi local toolset_dir="${_InitializeToolset%/*}" - local logger_path="$toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.Arcade.Sdk.dll" - args=( "${args[@]}" "-logger:$logger_path" ) + # new scripts need to work with old packages, so we need to look for the old names/versions + local selectedPath= + local possiblePaths=() + possiblePaths+=( "$toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.ArcadeLogging.dll" ) + possiblePaths+=( "$toolset_dir/$_InitializeBuildToolFramework/Microsoft.DotNet.Arcade.Sdk.dll" ) + possiblePaths+=( "$toolset_dir/netcoreapp2.1/Microsoft.DotNet.ArcadeLogging.dll" ) + possiblePaths+=( "$toolset_dir/netcoreapp2.1/Microsoft.DotNet.Arcade.Sdk.dll" ) + possiblePaths+=( "$toolset_dir/netcoreapp3.1/Microsoft.DotNet.ArcadeLogging.dll" ) + possiblePaths+=( "$toolset_dir/netcoreapp3.1/Microsoft.DotNet.Arcade.Sdk.dll" ) + for path in "${possiblePaths[@]}"; do + if [[ -f $path ]]; then + selectedPath=$path + break + fi + done + if [[ -z "$selectedPath" ]]; then + Write-PipelineTelemetryError -category 'Build' "Unable to find arcade sdk logger assembly." + ExitWithExitCode 1 + fi + args+=( "-logger:$selectedPath" ) fi MSBuild-Core ${args[@]} @@ -437,8 +477,17 @@ function MSBuild-Core { "$_InitializeBuildTool" "$@" || { local exit_code=$? - Write-PipelineTaskError "Build failed (exit code '$exit_code')." - ExitWithExitCode $exit_code + # We should not Write-PipelineTaskError here because that message shows up in the build summary + # The build already logged an error, that's the reason it failed. Producing an error here only adds noise. + echo "Build failed with exit code $exit_code. Check errors above." + if [[ "$ci" == "true" ]]; then + Write-PipelineSetResult -result "Failed" -message "msbuild execution failed." + # Exiting with an exit code causes the azure pipelines task to log yet another "noise" error + # The above Write-PipelineSetResult will cause the task to be marked as failure without adding yet another error + ExitWithExitCode 0 + else + ExitWithExitCode $exit_code + fi } } @@ -452,23 +501,27 @@ _script_dir=`dirname "$_ResolvePath"` eng_root=`cd -P "$_script_dir/.." && pwd` repo_root=`cd -P "$_script_dir/../.." && pwd` -artifacts_dir="$repo_root/artifacts" +repo_root="${repo_root}/" +artifacts_dir="${repo_root}artifacts" toolset_dir="$artifacts_dir/toolset" -tools_dir="$repo_root/.tools" +tools_dir="${repo_root}.tools" log_dir="$artifacts_dir/log/$configuration" temp_dir="$artifacts_dir/tmp/$configuration" -global_json_file="$repo_root/global.json" +global_json_file="${repo_root}global.json" # determine if global.json contains a "runtimes" entry global_json_has_runtimes=false -dotnetlocal_key=$(awk "/runtimes/ {print; exit}" "$global_json_file") || true -if [[ -n "$dotnetlocal_key" ]]; then +if command -v jq &> /dev/null; then + if jq -er '. | select(has("runtimes"))' "$global_json_file" &> /dev/null; then + global_json_has_runtimes=true + fi +elif [[ "$(cat "$global_json_file")" =~ \"runtimes\"[[:space:]\:]*\{ ]]; then global_json_has_runtimes=true fi # HOME may not be defined in some scenarios, but it is required by NuGet if [[ -z $HOME ]]; then - export HOME="$repo_root/artifacts/.home/" + export HOME="${repo_root}artifacts/.home/" mkdir -p "$HOME" fi diff --git a/eng/config/OptProf.json b/eng/config/OptProf.json index 53061450ff8..84b2c0bd27d 100644 --- a/eng/config/OptProf.json +++ b/eng/config/OptProf.json @@ -22,6 +22,12 @@ "testCases": [ "ManagedLangs.OptProfTests.DDRIT_RPS_ManagedLangs" ] + }, + { + "container": "Microsoft.VisualStudio.ProjectSystem.DDRIT", + "testCases": [ + "Microsoft.VisualStudio.ProjectSystem.DDRIT.OptProfOpenCloseTest.OpenAndCloseProjectTestSolution" + ] } ] } diff --git a/gen_build_info.sh b/gen_build_info.sh index 5f98bea05e5..d0f1d0d8e72 100755 --- a/gen_build_info.sh +++ b/gen_build_info.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash if [ $# -ne 1 ]; then echo "Usage: $0 " diff --git a/global.json b/global.json index baac5529cfe..b247f1daaa4 100644 --- a/global.json +++ b/global.json @@ -1,17 +1,15 @@ { + "sdk": { + "allowPrerelease": true + }, "tools": { - "dotnet": "5.0.102", - "runtimes": { - "dotnet/x64": [ - "2.1.7" - ] - }, + "dotnet": "6.0.100", "vs": { "version": "16.0" } }, "msbuild-sdks": { "Microsoft.Build.CentralPackageVersions": "2.0.1", - "Microsoft.DotNet.Arcade.Sdk": "5.0.0-beta.21226.1" + "Microsoft.DotNet.Arcade.Sdk": "6.0.0-beta.21460.7" } } diff --git a/mono/create_bootstrap.sh b/mono/create_bootstrap.sh index 5ae5344e2fa..10fba3ad535 100644 --- a/mono/create_bootstrap.sh +++ b/mono/create_bootstrap.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # This creates a bootstrap from an exising mono installation # This is just to ensure that we have the correct "matched" Roslyn diff --git a/msbuild-deploy.in b/msbuild-deploy.in index 007899ef116..90738f03674 100755 --- a/msbuild-deploy.in +++ b/msbuild-deploy.in @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash ABSOLUTE_PATH=$(cd `dirname "${BASH_SOURCE[0]}"` && pwd)/`basename "${BASH_SOURCE[0]}"` MSBUILD_SRC_DIR=`dirname $ABSOLUTE_PATH` mono $MONO_OPTIONS $MSBUILD_SRC_DIR/MSBuild.exe $* diff --git a/ref/Microsoft.Build.Framework/net/Microsoft.Build.Framework.cs b/ref/Microsoft.Build.Framework/net/Microsoft.Build.Framework.cs index d8a78e04125..0aaa25088a2 100644 --- a/ref/Microsoft.Build.Framework/net/Microsoft.Build.Framework.cs +++ b/ref/Microsoft.Build.Framework/net/Microsoft.Build.Framework.cs @@ -70,6 +70,7 @@ public BuildEventContext(int submissionId, int nodeId, int evaluationId, int pro public override int GetHashCode() { throw null; } public static bool operator ==(Microsoft.Build.Framework.BuildEventContext left, Microsoft.Build.Framework.BuildEventContext right) { throw null; } public static bool operator !=(Microsoft.Build.Framework.BuildEventContext left, Microsoft.Build.Framework.BuildEventContext right) { throw null; } + public override string ToString() { throw null; } } public partial class BuildFinishedEventArgs : Microsoft.Build.Framework.BuildStatusEventArgs { @@ -151,6 +152,14 @@ protected CustomBuildEventArgs(string message, string helpKeyword, string sender protected CustomBuildEventArgs(string message, string helpKeyword, string senderName, System.DateTime eventTimestamp, params object[] messageArgs) { } } public delegate void CustomBuildEventHandler(object sender, Microsoft.Build.Framework.CustomBuildEventArgs e); + public abstract partial class EngineServices + { + public const int Version1 = 1; + protected EngineServices() { } + public virtual bool IsTaskInputLoggingEnabled { get { throw null; } } + public virtual int Version { get { throw null; } } + public virtual bool LogsMessagesOfImportance(Microsoft.Build.Framework.MessageImportance importance) { throw null; } + } public partial class EnvironmentVariableReadEventArgs : Microsoft.Build.Framework.BuildMessageEventArgs { public EnvironmentVariableReadEventArgs() { } @@ -185,6 +194,10 @@ public partial interface IBuildEngine void LogMessageEvent(Microsoft.Build.Framework.BuildMessageEventArgs e); void LogWarningEvent(Microsoft.Build.Framework.BuildWarningEventArgs e); } + public partial interface IBuildEngine10 : Microsoft.Build.Framework.IBuildEngine, Microsoft.Build.Framework.IBuildEngine2, Microsoft.Build.Framework.IBuildEngine3, Microsoft.Build.Framework.IBuildEngine4, Microsoft.Build.Framework.IBuildEngine5, Microsoft.Build.Framework.IBuildEngine6, Microsoft.Build.Framework.IBuildEngine7, Microsoft.Build.Framework.IBuildEngine8, Microsoft.Build.Framework.IBuildEngine9 + { + Microsoft.Build.Framework.EngineServices EngineServices { get; } + } public partial interface IBuildEngine2 : Microsoft.Build.Framework.IBuildEngine { bool IsRunningMultipleNodes { get; } @@ -580,11 +593,21 @@ public TargetSkippedEventArgs(string message, params object[] messageArgs) { } public string Condition { get { throw null; } set { } } public string EvaluatedCondition { get { throw null; } set { } } public override string Message { get { throw null; } } + public Microsoft.Build.Framework.BuildEventContext OriginalBuildEventContext { get { throw null; } set { } } public bool OriginallySucceeded { get { throw null; } set { } } public string ParentTarget { get { throw null; } set { } } + public Microsoft.Build.Framework.TargetSkipReason SkipReason { get { throw null; } set { } } public string TargetFile { get { throw null; } set { } } public string TargetName { get { throw null; } set { } } } + public enum TargetSkipReason + { + None = 0, + PreviouslyBuiltSuccessfully = 1, + PreviouslyBuiltUnsuccessfully = 2, + OutputsUpToDate = 3, + ConditionWasFalse = 4, + } public partial class TargetStartedEventArgs : Microsoft.Build.Framework.BuildStatusEventArgs { protected TargetStartedEventArgs() { } @@ -652,6 +675,8 @@ public partial class TaskStartedEventArgs : Microsoft.Build.Framework.BuildStatu protected TaskStartedEventArgs() { } public TaskStartedEventArgs(string message, string helpKeyword, string projectFile, string taskFile, string taskName) { } public TaskStartedEventArgs(string message, string helpKeyword, string projectFile, string taskFile, string taskName, System.DateTime eventTimestamp) { } + public int ColumnNumber { get { throw null; } } + public int LineNumber { get { throw null; } } public override string Message { get { throw null; } } public string ProjectFile { get { throw null; } } public string TaskFile { get { throw null; } } diff --git a/ref/Microsoft.Build.Framework/netstandard/Microsoft.Build.Framework.cs b/ref/Microsoft.Build.Framework/netstandard/Microsoft.Build.Framework.cs index 2fd1301abb1..f4dc90e663c 100644 --- a/ref/Microsoft.Build.Framework/netstandard/Microsoft.Build.Framework.cs +++ b/ref/Microsoft.Build.Framework/netstandard/Microsoft.Build.Framework.cs @@ -70,6 +70,7 @@ public BuildEventContext(int submissionId, int nodeId, int evaluationId, int pro public override int GetHashCode() { throw null; } public static bool operator ==(Microsoft.Build.Framework.BuildEventContext left, Microsoft.Build.Framework.BuildEventContext right) { throw null; } public static bool operator !=(Microsoft.Build.Framework.BuildEventContext left, Microsoft.Build.Framework.BuildEventContext right) { throw null; } + public override string ToString() { throw null; } } public partial class BuildFinishedEventArgs : Microsoft.Build.Framework.BuildStatusEventArgs { @@ -151,6 +152,14 @@ protected CustomBuildEventArgs(string message, string helpKeyword, string sender protected CustomBuildEventArgs(string message, string helpKeyword, string senderName, System.DateTime eventTimestamp, params object[] messageArgs) { } } public delegate void CustomBuildEventHandler(object sender, Microsoft.Build.Framework.CustomBuildEventArgs e); + public abstract partial class EngineServices + { + public const int Version1 = 1; + protected EngineServices() { } + public virtual bool IsTaskInputLoggingEnabled { get { throw null; } } + public virtual int Version { get { throw null; } } + public virtual bool LogsMessagesOfImportance(Microsoft.Build.Framework.MessageImportance importance) { throw null; } + } public partial class EnvironmentVariableReadEventArgs : Microsoft.Build.Framework.BuildMessageEventArgs { public EnvironmentVariableReadEventArgs() { } @@ -185,6 +194,10 @@ public partial interface IBuildEngine void LogMessageEvent(Microsoft.Build.Framework.BuildMessageEventArgs e); void LogWarningEvent(Microsoft.Build.Framework.BuildWarningEventArgs e); } + public partial interface IBuildEngine10 : Microsoft.Build.Framework.IBuildEngine, Microsoft.Build.Framework.IBuildEngine2, Microsoft.Build.Framework.IBuildEngine3, Microsoft.Build.Framework.IBuildEngine4, Microsoft.Build.Framework.IBuildEngine5, Microsoft.Build.Framework.IBuildEngine6, Microsoft.Build.Framework.IBuildEngine7, Microsoft.Build.Framework.IBuildEngine8, Microsoft.Build.Framework.IBuildEngine9 + { + Microsoft.Build.Framework.EngineServices EngineServices { get; } + } public partial interface IBuildEngine2 : Microsoft.Build.Framework.IBuildEngine { bool IsRunningMultipleNodes { get; } @@ -579,11 +592,21 @@ public TargetSkippedEventArgs(string message, params object[] messageArgs) { } public string Condition { get { throw null; } set { } } public string EvaluatedCondition { get { throw null; } set { } } public override string Message { get { throw null; } } + public Microsoft.Build.Framework.BuildEventContext OriginalBuildEventContext { get { throw null; } set { } } public bool OriginallySucceeded { get { throw null; } set { } } public string ParentTarget { get { throw null; } set { } } + public Microsoft.Build.Framework.TargetSkipReason SkipReason { get { throw null; } set { } } public string TargetFile { get { throw null; } set { } } public string TargetName { get { throw null; } set { } } } + public enum TargetSkipReason + { + None = 0, + PreviouslyBuiltSuccessfully = 1, + PreviouslyBuiltUnsuccessfully = 2, + OutputsUpToDate = 3, + ConditionWasFalse = 4, + } public partial class TargetStartedEventArgs : Microsoft.Build.Framework.BuildStatusEventArgs { protected TargetStartedEventArgs() { } @@ -651,6 +674,8 @@ public partial class TaskStartedEventArgs : Microsoft.Build.Framework.BuildStatu protected TaskStartedEventArgs() { } public TaskStartedEventArgs(string message, string helpKeyword, string projectFile, string taskFile, string taskName) { } public TaskStartedEventArgs(string message, string helpKeyword, string projectFile, string taskFile, string taskName, System.DateTime eventTimestamp) { } + public int ColumnNumber { get { throw null; } } + public int LineNumber { get { throw null; } } public override string Message { get { throw null; } } public string ProjectFile { get { throw null; } } public string TaskFile { get { throw null; } } diff --git a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs index c94de3e3688..0c71f5d7390 100644 --- a/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs @@ -164,6 +164,7 @@ public CombineTargetFrameworkInfoProperties() { } [Microsoft.Build.Framework.OutputAttribute] public string Result { get { throw null; } set { } } public string RootElementName { get { throw null; } set { } } + public bool UseAttributeForTargetFrameworkInfoPropertyNames { get { throw null; } set { } } public override bool Execute() { throw null; } } public partial class CombineXmlElements : Microsoft.Build.Tasks.TaskExtension @@ -207,6 +208,8 @@ public Copy() { } public Microsoft.Build.Framework.ITaskItem[] SourceFiles { get { throw null; } set { } } public bool UseHardlinksIfPossible { get { throw null; } set { } } public bool UseSymboliclinksIfPossible { get { throw null; } set { } } + [Microsoft.Build.Framework.OutputAttribute] + public bool WroteAtLeastOneFile { get { throw null; } } public void Cancel() { } public override bool Execute() { throw null; } } @@ -611,6 +614,18 @@ public GetAssemblyIdentity() { } public Microsoft.Build.Framework.ITaskItem[] AssemblyFiles { get { throw null; } set { } } public override bool Execute() { throw null; } } + public partial class GetCompatiblePlatform : Microsoft.Build.Tasks.TaskExtension + { + public GetCompatiblePlatform() { } + [Microsoft.Build.Framework.RequiredAttribute] + public Microsoft.Build.Framework.ITaskItem[] AnnotatedProjects { get { throw null; } set { } } + [Microsoft.Build.Framework.OutputAttribute] + public Microsoft.Build.Framework.ITaskItem[] AssignedProjectsWithPlatform { get { throw null; } set { } } + [Microsoft.Build.Framework.RequiredAttribute] + public string CurrentProjectPlatform { get { throw null; } set { } } + public string PlatformLookupTable { get { throw null; } set { } } + public override bool Execute() { throw null; } + } public sealed partial class GetFileHash : Microsoft.Build.Tasks.TaskExtension { public GetFileHash() { } diff --git a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs index f8c26c263dd..032e84fecf9 100644 --- a/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs +++ b/ref/Microsoft.Build.Tasks.Core/netstandard/Microsoft.Build.Tasks.Core.cs @@ -94,6 +94,7 @@ public CombineTargetFrameworkInfoProperties() { } [Microsoft.Build.Framework.OutputAttribute] public string Result { get { throw null; } set { } } public string RootElementName { get { throw null; } set { } } + public bool UseAttributeForTargetFrameworkInfoPropertyNames { get { throw null; } set { } } public override bool Execute() { throw null; } } public partial class CombineXmlElements : Microsoft.Build.Tasks.TaskExtension @@ -137,6 +138,8 @@ public Copy() { } public Microsoft.Build.Framework.ITaskItem[] SourceFiles { get { throw null; } set { } } public bool UseHardlinksIfPossible { get { throw null; } set { } } public bool UseSymboliclinksIfPossible { get { throw null; } set { } } + [Microsoft.Build.Framework.OutputAttribute] + public bool WroteAtLeastOneFile { get { throw null; } } public void Cancel() { } public override bool Execute() { throw null; } } @@ -504,6 +507,18 @@ public GetAssemblyIdentity() { } public Microsoft.Build.Framework.ITaskItem[] AssemblyFiles { get { throw null; } set { } } public override bool Execute() { throw null; } } + public partial class GetCompatiblePlatform : Microsoft.Build.Tasks.TaskExtension + { + public GetCompatiblePlatform() { } + [Microsoft.Build.Framework.RequiredAttribute] + public Microsoft.Build.Framework.ITaskItem[] AnnotatedProjects { get { throw null; } set { } } + [Microsoft.Build.Framework.OutputAttribute] + public Microsoft.Build.Framework.ITaskItem[] AssignedProjectsWithPlatform { get { throw null; } set { } } + [Microsoft.Build.Framework.RequiredAttribute] + public string CurrentProjectPlatform { get { throw null; } set { } } + public string PlatformLookupTable { get { throw null; } set { } } + public override bool Execute() { throw null; } + } public sealed partial class GetFileHash : Microsoft.Build.Tasks.TaskExtension { public GetFileHash() { } @@ -875,6 +890,18 @@ public SGen() { } public override bool Execute() { throw null; } protected override string GenerateFullPathToTool() { throw null; } } + public sealed partial class SignFile : Microsoft.Build.Utilities.Task + { + public SignFile() { } + [Microsoft.Build.Framework.RequiredAttribute] + public string CertificateThumbprint { get { throw null; } set { } } + [Microsoft.Build.Framework.RequiredAttribute] + public Microsoft.Build.Framework.ITaskItem SigningTarget { get { throw null; } set { } } + public string TargetFrameworkIdentifier { get { throw null; } set { } } + public string TargetFrameworkVersion { get { throw null; } set { } } + public string TimestampUrl { get { throw null; } set { } } + public override bool Execute() { throw null; } + } public abstract partial class TaskExtension : Microsoft.Build.Utilities.Task { internal TaskExtension() { } diff --git a/ref/Microsoft.Build.Utilities.Core/net/Microsoft.Build.Utilities.Core.cs b/ref/Microsoft.Build.Utilities.Core/net/Microsoft.Build.Utilities.Core.cs index a8564cf317b..7c0a1e077c3 100644 --- a/ref/Microsoft.Build.Utilities.Core/net/Microsoft.Build.Utilities.Core.cs +++ b/ref/Microsoft.Build.Utilities.Core/net/Microsoft.Build.Utilities.Core.cs @@ -321,11 +321,11 @@ public enum TargetDotNetFrameworkVersion Version461 = 8, Version452 = 9, Version462 = 10, - VersionLatest = 10, Version47 = 11, Version471 = 12, Version472 = 13, Version48 = 14, + VersionLatest = 14, Latest = 9999, } public partial class TargetPlatformSDK : System.IEquatable @@ -392,6 +392,7 @@ public TaskLoggingHelper(Microsoft.Build.Framework.ITask taskInstance) { } protected Microsoft.Build.Framework.IBuildEngine BuildEngine { get { throw null; } } public bool HasLoggedErrors { get { throw null; } } public string HelpKeywordPrefix { get { throw null; } set { } } + public bool IsTaskInputLoggingEnabled { get { throw null; } } protected string TaskName { get { throw null; } } public System.Resources.ResourceManager TaskResources { get { throw null; } set { } } public string ExtractMessageCode(string message, out string messageWithoutCodePrefix) { throw null; } @@ -423,6 +424,7 @@ public void LogMessageFromResources(string messageResourceName, params object[] public bool LogMessagesFromFile(string fileName) { throw null; } public bool LogMessagesFromFile(string fileName, Microsoft.Build.Framework.MessageImportance messageImportance) { throw null; } public bool LogMessagesFromStream(System.IO.TextReader stream, Microsoft.Build.Framework.MessageImportance messageImportance) { throw null; } + public bool LogsMessagesOfImportance(Microsoft.Build.Framework.MessageImportance importance) { throw null; } public void LogTelemetry(string eventName, System.Collections.Generic.IDictionary properties) { } public void LogWarning(string message, params object[] messageArgs) { } public void LogWarning(string subcategory, string warningCode, string helpKeyword, string file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, string message, params object[] messageArgs) { } @@ -599,8 +601,8 @@ public enum VisualStudioVersion Version120 = 2, Version140 = 3, Version150 = 4, - VersionLatest = 4, Version160 = 5, Version170 = 6, + VersionLatest = 6, } } diff --git a/ref/Microsoft.Build.Utilities.Core/netstandard/Microsoft.Build.Utilities.Core.cs b/ref/Microsoft.Build.Utilities.Core/netstandard/Microsoft.Build.Utilities.Core.cs index 9d7111dfe72..6cc7b96d758 100644 --- a/ref/Microsoft.Build.Utilities.Core/netstandard/Microsoft.Build.Utilities.Core.cs +++ b/ref/Microsoft.Build.Utilities.Core/netstandard/Microsoft.Build.Utilities.Core.cs @@ -166,11 +166,11 @@ public enum TargetDotNetFrameworkVersion Version461 = 8, Version452 = 9, Version462 = 10, - VersionLatest = 10, Version47 = 11, Version471 = 12, Version472 = 13, Version48 = 14, + VersionLatest = 14, Latest = 9999, } public partial class TargetPlatformSDK : System.IEquatable @@ -235,6 +235,7 @@ public TaskLoggingHelper(Microsoft.Build.Framework.ITask taskInstance) { } protected Microsoft.Build.Framework.IBuildEngine BuildEngine { get { throw null; } } public bool HasLoggedErrors { get { throw null; } } public string HelpKeywordPrefix { get { throw null; } set { } } + public bool IsTaskInputLoggingEnabled { get { throw null; } } protected string TaskName { get { throw null; } } public System.Resources.ResourceManager TaskResources { get { throw null; } set { } } public string ExtractMessageCode(string message, out string messageWithoutCodePrefix) { throw null; } @@ -265,6 +266,7 @@ public void LogMessageFromResources(string messageResourceName, params object[] public bool LogMessagesFromFile(string fileName) { throw null; } public bool LogMessagesFromFile(string fileName, Microsoft.Build.Framework.MessageImportance messageImportance) { throw null; } public bool LogMessagesFromStream(System.IO.TextReader stream, Microsoft.Build.Framework.MessageImportance messageImportance) { throw null; } + public bool LogsMessagesOfImportance(Microsoft.Build.Framework.MessageImportance importance) { throw null; } public void LogTelemetry(string eventName, System.Collections.Generic.IDictionary properties) { } public void LogWarning(string message, params object[] messageArgs) { } public void LogWarning(string subcategory, string warningCode, string helpKeyword, string file, int lineNumber, int columnNumber, int endLineNumber, int endColumnNumber, string message, params object[] messageArgs) { } @@ -433,8 +435,8 @@ public enum VisualStudioVersion Version120 = 2, Version140 = 3, Version150 = 4, - VersionLatest = 4, Version160 = 5, Version170 = 6, + VersionLatest = 6, } } diff --git a/ref/Microsoft.Build/net/Microsoft.Build.cs b/ref/Microsoft.Build/net/Microsoft.Build.cs index d59f83ba636..fb5b4b8da5e 100644 --- a/ref/Microsoft.Build/net/Microsoft.Build.cs +++ b/ref/Microsoft.Build/net/Microsoft.Build.cs @@ -1,5 +1,14 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +namespace Microsoft.Build.BackEnd.SdkResolution +{ + public partial class SdkResolverException : System.Exception + { + public SdkResolverException(string resourceName, Microsoft.Build.Framework.SdkResolver resolver, Microsoft.Build.Framework.SdkReference sdk, System.Exception innerException, params string[] args) { } + public Microsoft.Build.Framework.SdkResolver Resolver { get { throw null; } } + public Microsoft.Build.Framework.SdkReference Sdk { get { throw null; } } + } +} namespace Microsoft.Build.Construction { public abstract partial class ElementLocation @@ -1498,18 +1507,18 @@ namespace Microsoft.Build.FileSystem public abstract partial class MSBuildFileSystemBase { protected MSBuildFileSystemBase() { } - public abstract bool DirectoryExists(string path); - public abstract System.Collections.Generic.IEnumerable EnumerateDirectories(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly); - public abstract System.Collections.Generic.IEnumerable EnumerateFiles(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly); - public abstract System.Collections.Generic.IEnumerable EnumerateFileSystemEntries(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly); - public abstract bool FileExists(string path); - public abstract bool FileOrDirectoryExists(string path); - public abstract System.IO.FileAttributes GetAttributes(string path); - public abstract System.IO.Stream GetFileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share); - public abstract System.DateTime GetLastWriteTimeUtc(string path); - public abstract System.IO.TextReader ReadFile(string path); - public abstract byte[] ReadFileAllBytes(string path); - public abstract string ReadFileAllText(string path); + public virtual bool DirectoryExists(string path) { throw null; } + public virtual System.Collections.Generic.IEnumerable EnumerateDirectories(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly) { throw null; } + public virtual System.Collections.Generic.IEnumerable EnumerateFiles(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly) { throw null; } + public virtual System.Collections.Generic.IEnumerable EnumerateFileSystemEntries(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly) { throw null; } + public virtual bool FileExists(string path) { throw null; } + public virtual bool FileOrDirectoryExists(string path) { throw null; } + public virtual System.IO.FileAttributes GetAttributes(string path) { throw null; } + public virtual System.IO.Stream GetFileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) { throw null; } + public virtual System.DateTime GetLastWriteTimeUtc(string path) { throw null; } + public virtual System.IO.TextReader ReadFile(string path) { throw null; } + public virtual byte[] ReadFileAllBytes(string path) { throw null; } + public virtual string ReadFileAllText(string path) { throw null; } } } namespace Microsoft.Build.Globbing @@ -1574,8 +1583,8 @@ protected GraphBuildOptions(Microsoft.Build.Graph.GraphBuildOptions original) { public virtual bool Equals(Microsoft.Build.Graph.GraphBuildOptions other) { throw null; } public override bool Equals(object obj) { throw null; } public override int GetHashCode() { throw null; } - public static bool operator ==(Microsoft.Build.Graph.GraphBuildOptions r1, Microsoft.Build.Graph.GraphBuildOptions r2) { throw null; } - public static bool operator !=(Microsoft.Build.Graph.GraphBuildOptions r1, Microsoft.Build.Graph.GraphBuildOptions r2) { throw null; } + public static bool operator ==(Microsoft.Build.Graph.GraphBuildOptions left, Microsoft.Build.Graph.GraphBuildOptions right) { throw null; } + public static bool operator !=(Microsoft.Build.Graph.GraphBuildOptions left, Microsoft.Build.Graph.GraphBuildOptions right) { throw null; } protected virtual bool PrintMembers(System.Text.StringBuilder builder) { throw null; } public override string ToString() { throw null; } public virtual Microsoft.Build.Graph.GraphBuildOptions $() { throw null; } diff --git a/ref/Microsoft.Build/netstandard/Microsoft.Build.cs b/ref/Microsoft.Build/netstandard/Microsoft.Build.cs index ab3acb3d087..b387429467c 100644 --- a/ref/Microsoft.Build/netstandard/Microsoft.Build.cs +++ b/ref/Microsoft.Build/netstandard/Microsoft.Build.cs @@ -1,5 +1,14 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +namespace Microsoft.Build.BackEnd.SdkResolution +{ + public partial class SdkResolverException : System.Exception + { + public SdkResolverException(string resourceName, Microsoft.Build.Framework.SdkResolver resolver, Microsoft.Build.Framework.SdkReference sdk, System.Exception innerException, params string[] args) { } + public Microsoft.Build.Framework.SdkResolver Resolver { get { throw null; } } + public Microsoft.Build.Framework.SdkReference Sdk { get { throw null; } } + } +} namespace Microsoft.Build.Construction { public abstract partial class ElementLocation @@ -1492,18 +1501,18 @@ namespace Microsoft.Build.FileSystem public abstract partial class MSBuildFileSystemBase { protected MSBuildFileSystemBase() { } - public abstract bool DirectoryExists(string path); - public abstract System.Collections.Generic.IEnumerable EnumerateDirectories(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly); - public abstract System.Collections.Generic.IEnumerable EnumerateFiles(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly); - public abstract System.Collections.Generic.IEnumerable EnumerateFileSystemEntries(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly); - public abstract bool FileExists(string path); - public abstract bool FileOrDirectoryExists(string path); - public abstract System.IO.FileAttributes GetAttributes(string path); - public abstract System.IO.Stream GetFileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share); - public abstract System.DateTime GetLastWriteTimeUtc(string path); - public abstract System.IO.TextReader ReadFile(string path); - public abstract byte[] ReadFileAllBytes(string path); - public abstract string ReadFileAllText(string path); + public virtual bool DirectoryExists(string path) { throw null; } + public virtual System.Collections.Generic.IEnumerable EnumerateDirectories(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly) { throw null; } + public virtual System.Collections.Generic.IEnumerable EnumerateFiles(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly) { throw null; } + public virtual System.Collections.Generic.IEnumerable EnumerateFileSystemEntries(string path, string searchPattern = "*", System.IO.SearchOption searchOption = System.IO.SearchOption.TopDirectoryOnly) { throw null; } + public virtual bool FileExists(string path) { throw null; } + public virtual bool FileOrDirectoryExists(string path) { throw null; } + public virtual System.IO.FileAttributes GetAttributes(string path) { throw null; } + public virtual System.IO.Stream GetFileStream(string path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) { throw null; } + public virtual System.DateTime GetLastWriteTimeUtc(string path) { throw null; } + public virtual System.IO.TextReader ReadFile(string path) { throw null; } + public virtual byte[] ReadFileAllBytes(string path) { throw null; } + public virtual string ReadFileAllText(string path) { throw null; } } } namespace Microsoft.Build.Globbing @@ -1568,8 +1577,8 @@ protected GraphBuildOptions(Microsoft.Build.Graph.GraphBuildOptions original) { public virtual bool Equals(Microsoft.Build.Graph.GraphBuildOptions other) { throw null; } public override bool Equals(object obj) { throw null; } public override int GetHashCode() { throw null; } - public static bool operator ==(Microsoft.Build.Graph.GraphBuildOptions r1, Microsoft.Build.Graph.GraphBuildOptions r2) { throw null; } - public static bool operator !=(Microsoft.Build.Graph.GraphBuildOptions r1, Microsoft.Build.Graph.GraphBuildOptions r2) { throw null; } + public static bool operator ==(Microsoft.Build.Graph.GraphBuildOptions left, Microsoft.Build.Graph.GraphBuildOptions right) { throw null; } + public static bool operator !=(Microsoft.Build.Graph.GraphBuildOptions left, Microsoft.Build.Graph.GraphBuildOptions right) { throw null; } protected virtual bool PrintMembers(System.Text.StringBuilder builder) { throw null; } public override string ToString() { throw null; } public virtual Microsoft.Build.Graph.GraphBuildOptions $() { throw null; } diff --git a/scripts/Deploy-MSBuild.ps1 b/scripts/Deploy-MSBuild.ps1 index c9bc90217fe..f27baefae70 100644 --- a/scripts/Deploy-MSBuild.ps1 +++ b/scripts/Deploy-MSBuild.ps1 @@ -11,13 +11,18 @@ Param( Set-StrictMode -Version "Latest" $ErrorActionPreference = "Stop" -function Copy-WithBackup ($origin) { - $directoryPart = Join-Path -Path $destination $origin.IntermediaryDirectories +function Copy-WithBackup ($origin, $destinationSubFolder = "") { + $directoryPart = [IO.Path]::Combine($destination, $destinationSubFolder, $origin.IntermediaryDirectories) $destinationPath = Join-Path -Path $directoryPart (Split-Path $origin.SourceFile -leaf) + $backupInto = [IO.Path]::Combine($BackupFolder, $destinationSubFolder) + if (Test-Path $destinationPath -PathType Leaf) { # Back up previous copy of the file - Copy-Item $destinationPath $BackupFolder -ErrorAction Stop + if (!(Test-Path $backupInto)) { + [system.io.directory]::CreateDirectory($backupInto) + } + Copy-Item $destinationPath $backupInto -ErrorAction Stop } if (!(Test-Path $directoryPart)) { @@ -48,7 +53,7 @@ Write-Host "Existing MSBuild assemblies backed up to $BackupFolder" if ($runtime -eq "Desktop") { $targetFramework = "net472" } else { - $targetFramework = "net5.0" + $targetFramework = "net6.0" } $bootstrapBinDirectory = "artifacts\bin\MSBuild.Bootstrap\$configuration\$targetFramework" @@ -60,11 +65,6 @@ $filesToCopyToBin = @( FileToCopy "$bootstrapBinDirectory\Microsoft.Build.Utilities.Core.dll" FileToCopy "$bootstrapBinDirectory\Microsoft.NET.StringTools.dll" - FileToCopy "$bootstrapBinDirectory\en\Microsoft.Build.resources.dll" "en" - FileToCopy "$bootstrapBinDirectory\en\Microsoft.Build.Tasks.Core.resources.dll" "en" - FileToCopy "$bootstrapBinDirectory\en\Microsoft.Build.Utilities.Core.resources.dll" "en" - FileToCopy "$bootstrapBinDirectory\en\MSBuild.resources.dll" "en" - FileToCopy "$bootstrapBinDirectory\Microsoft.Common.CrossTargeting.targets" FileToCopy "$bootstrapBinDirectory\Microsoft.Common.CurrentVersion.targets" FileToCopy "$bootstrapBinDirectory\Microsoft.Common.targets" @@ -82,17 +82,16 @@ $filesToCopyToBin = @( FileToCopy "$bootstrapBinDirectory\Microsoft.VisualBasic.CrossTargeting.targets" FileToCopy "$bootstrapBinDirectory\Microsoft.VisualBasic.CurrentVersion.targets" FileToCopy "$bootstrapBinDirectory\Microsoft.VisualBasic.targets" + + FileToCopy "$bootstrapBinDirectory\Microsoft.Common.tasks" ) if ($runtime -eq "Desktop") { $runtimeSpecificFiles = @( - FileToCopy "$bootstrapBinDirectory\MSBuild.exe" FileToCopy "artifacts\bin\Microsoft.Build.Conversion\$configuration\$targetFramework\Microsoft.Build.Conversion.Core.dll" FileToCopy "artifacts\bin\Microsoft.Build.Engine\$configuration\$targetFramework\Microsoft.Build.Engine.dll" - FileToCopy "artifacts\bin\MSBuildTaskHost\$configuration\net35\MSBuildTaskHost.exe" - FileToCopy "artifacts\bin\MSBuildTaskHost\$configuration\net35\MSBuildTaskHost.pdb" - + FileToCopy "$bootstrapBinDirectory\Microsoft.Bcl.AsyncInterfaces.dll" FileToCopy "$bootstrapBinDirectory\Microsoft.Data.Entity.targets" FileToCopy "$bootstrapBinDirectory\Microsoft.ServiceModel.targets" FileToCopy "$bootstrapBinDirectory\Microsoft.WinFx.targets" @@ -100,6 +99,18 @@ if ($runtime -eq "Desktop") { FileToCopy "$bootstrapBinDirectory\Microsoft.Xaml.targets" FileToCopy "$bootstrapBinDirectory\Workflow.targets" FileToCopy "$bootstrapBinDirectory\Workflow.VisualBasic.targets" + + FileToCopy "$bootstrapBinDirectory\System.Buffers.dll" + FileToCopy "$bootstrapBinDirectory\System.Collections.Immutable.dll" + FileToCopy "$bootstrapBinDirectory\System.Memory.dll" + FileToCopy "$bootstrapBinDirectory\System.Numerics.Vectors.dll" + FileToCopy "$bootstrapBinDirectory\System.Resources.Extensions.dll" + FileToCopy "$bootstrapBinDirectory\System.Runtime.CompilerServices.Unsafe.dll" + FileToCopy "$bootstrapBinDirectory\System.Text.Encodings.Web.dll" + FileToCopy "$bootstrapBinDirectory\System.Text.Json.dll" + FileToCopy "$bootstrapBinDirectory\System.Threading.Tasks.Dataflow.dll" + FileToCopy "$bootstrapBinDirectory\System.Threading.Tasks.Extensions.dll" + FileToCopy "$bootstrapBinDirectory\System.ValueTuple.dll" ) } else { $runtimeSpecificFiles = @( @@ -107,11 +118,40 @@ if ($runtime -eq "Desktop") { ) } +if ($runtime -eq "Desktop") { + $x86files = @( + FileToCopy "$bootstrapBinDirectory\MSBuild.exe" + FileToCopy "$bootstrapBinDirectory\MSBuild.exe.config" + FileToCopy "artifacts\bin\MSBuildTaskHost\$configuration\net35\MSBuildTaskHost.exe" + FileToCopy "artifacts\bin\MSBuildTaskHost\$configuration\net35\MSBuildTaskHost.pdb" + ) + $amd64files = @( + FileToCopy "artifacts\bin\MSBuild\x64\$configuration\$targetFramework\MSBuild.exe" + FileToCopy "artifacts\bin\MSBuild\x64\$configuration\$targetFramework\MSBuild.exe.config" + FileToCopy "artifacts\bin\MSBuildTaskHost\x64\$configuration\net35\MSBuildTaskHost.exe" + FileToCopy "artifacts\bin\MSBuildTaskHost\x64\$configuration\net35\MSBuildTaskHost.pdb" + ) +} + $filesToCopyToBin += $runtimeSpecificFiles foreach ($file in $filesToCopyToBin) { Copy-WithBackup $file } +if ($runtime -eq "Desktop") { + foreach ($file in $x86files) { + Copy-WithBackup $file + } + + foreach ($file in $filesToCopyToBin) { + Copy-WithBackup $file "amd64" + } + + foreach ($file in $amd64files) { + Copy-WithBackup $file "amd64" + } +} + Write-Host -ForegroundColor Green "Copy succeeded" Write-Verbose "Run $destination\MSBuild.exe" diff --git a/scripts/EnumerateMSBuild.ps1 b/scripts/EnumerateMSBuild.ps1 index d8ac8f262e6..115cdd8f108 100644 --- a/scripts/EnumerateMSBuild.ps1 +++ b/scripts/EnumerateMSBuild.ps1 @@ -33,7 +33,7 @@ foreach ($instance in $vsInstances) $instancePath = $instance.installationPath Write-Log "********************" -LogToConsole $False Write-Log "Found VS Instance: $instanceName" - + # Look at each dll/exe in the MSBuild bin folder and get their ProductVersion ls -File -Recurse -Include ('*.dll', '*.exe') -Path "$instancePath\MSBuild\15.0\Bin" | % VersionInfo | Format-Table -AutoSize InternalName, ProductVersion, FileName | Out-File $logFile -Width 1000 -Append unicode ls -File -Recurse -Include ('*.dll', '*.exe') -Path "$instancePath\MSBuild\Current\Bin" | % VersionInfo | Format-Table -AutoSize InternalName, ProductVersion, FileName | Out-File $logFile -Width 1000 -Append unicode @@ -68,4 +68,4 @@ Write-Log "********************" -LogToConsole $False $logFile = Get-ChildItem -File -Path $logFile Write-Host -Write-Host "Output saved to $logFile" \ No newline at end of file +Write-Host "Output saved to $logFile" diff --git a/src/BannedSymbols.txt b/src/BannedSymbols.txt new file mode 100644 index 00000000000..80f588d4b83 --- /dev/null +++ b/src/BannedSymbols.txt @@ -0,0 +1 @@ +M:System.Globalization.CompareInfo.IndexOf(System.String,System.Char);CompareInfo.IndexOf can unexpectedly allocate strings--use string.IndexOf diff --git a/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs b/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs index 1ff0a918ab2..fb4e9d1afdc 100644 --- a/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs +++ b/src/Build.OM.UnitTests/Construction/ProjectRootElement_Tests.cs @@ -9,6 +9,7 @@ using System.Security.AccessControl; using System.Security.Principal; #endif +using System.Reflection; using System.Text; using System.Threading; using System.Xml; @@ -18,6 +19,7 @@ using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException; using ProjectCollection = Microsoft.Build.Evaluation.ProjectCollection; +using Shouldly; using Xunit; namespace Microsoft.Build.UnitTests.OM.Construction @@ -393,7 +395,7 @@ public void ValidXmlInvalidSyntaxInChildElement() - + "; @@ -414,7 +416,7 @@ public void ValidXmlInvalidSyntaxOpenFromDiskTwice() - + "; @@ -1390,7 +1392,7 @@ public void ReloadCanSpecifyPreserveFormatting(bool initialPreserveFormatting, b

property value

- + @@ -1410,7 +1412,7 @@ public void ReloadCanSpecifyPreserveFormatting(bool initialPreserveFormatting, b

property value

- + @@ -1426,7 +1428,7 @@ public void ReloadCanSpecifyPreserveFormatting(bool initialPreserveFormatting, b

property value

- + @@ -1446,7 +1448,7 @@ public void ReloadCanSpecifyPreserveFormatting(bool initialPreserveFormatting, b

property value

- + @@ -1461,7 +1463,7 @@ public void ReloadCanSpecifyPreserveFormatting(bool initialPreserveFormatting, b

property value

- + metadata value @@ -1560,7 +1562,7 @@ public void ReloadedStateIsResilientToChangesAndDiskRoundtrip() @" - + @@ -1578,7 +1580,7 @@ public void ReloadedStateIsResilientToChangesAndDiskRoundtrip() @" - +

v

@@ -1854,6 +1856,31 @@ public void ReloadCanOverwriteUnsavedChanges() AssertReload(SimpleProject, ComplexProject, true, true, true, act); } + [Fact] + public void ReloadDoesNotLeakCachedXmlDocuments() + { + using var env = TestEnvironment.Create(); + var testFiles = env.CreateTestProjectWithFiles("", new[] { "build.proj" }); + var projectFile = testFiles.CreatedFiles.First(); + + var projectElement = ObjectModelHelpers.CreateInMemoryProjectRootElement(SimpleProject); + projectElement.Save(projectFile); + + int originalDocumentCount = GetNumberOfDocumentsInProjectStringCache(projectElement); + + // Test successful reload. + projectElement.Reload(false); + GetNumberOfDocumentsInProjectStringCache(projectElement).ShouldBe(originalDocumentCount); + + // Test failed reload. + using (StreamWriter sw = new StreamWriter(projectFile)) + { + sw.WriteLine(""); // Invalid root element + } + Should.Throw(() => projectElement.Reload(false)); + GetNumberOfDocumentsInProjectStringCache(projectElement).ShouldBe(originalDocumentCount); + } + private void AssertReload( string initialContents, string changedContents, @@ -1986,5 +2013,17 @@ private void VerifyAssertLineByLine(string expected, string actual) { Helpers.VerifyAssertLineByLine(expected, actual, false); } + + /// + /// Returns the number of documents retained by the project string cache. + /// Peeks at it via reflection since internals are not visible to these tests. + /// + private int GetNumberOfDocumentsInProjectStringCache(ProjectRootElement project) + { + var bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty; + object document = typeof(ProjectRootElement).InvokeMember("XmlDocument", bindingFlags, null, project, Array.Empty()); + object cache = document.GetType().InvokeMember("StringCache", bindingFlags, null, document, Array.Empty()); + return (int)cache.GetType().InvokeMember("DocumentCount", bindingFlags, null, cache, Array.Empty()); + } } } diff --git a/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs b/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs index ec4dd997eb7..d4322c9410f 100644 --- a/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/ProjectItem_Tests.cs @@ -20,7 +20,7 @@ namespace Microsoft.Build.UnitTests.OM.Definition /// /// Tests for ProjectItem /// - public class ProjectItem_Tests + public class ProjectItem_Tests : IDisposable { internal const string ItemWithIncludeAndExclude = @" @@ -43,6 +43,18 @@ public class ProjectItem_Tests "; + protected TestEnvironment _env; + + public ProjectItem_Tests() + { + _env = TestEnvironment.Create(); + } + + public void Dispose() + { + _env.Dispose(); + } + /// /// Project getter /// @@ -2094,6 +2106,19 @@ public void Remove() Assert.Equal("a", items[0].EvaluatedInclude); } + [Fact] + public void RemoveAllMatchingItems() + { + IList items = ObjectModelHelpers.GetItemsFromFragment( + "" + + "" + + "" + ); + + Assert.Equal(2, items.Count); + Assert.Equal(@"a;a", string.Join(";", items.Select(i => i.EvaluatedInclude))); + } + [Fact] public void RemoveGlob() { @@ -3529,4 +3554,13 @@ public void FileNameMetadataEvaluationShouldNotDependsFromPlatformSpecificSlashe } } } + + public class ProjectItemWithOptimizations_Tests : ProjectItem_Tests + { + public ProjectItemWithOptimizations_Tests() + { + // Make sure we always use the dictionary-based Remove logic. + _env.SetEnvironmentVariable("MSBUILDDICTIONARYBASEDITEMREMOVETHRESHOLD", "0"); + } + } } diff --git a/src/Build.OM.UnitTests/Definition/Project_Tests.cs b/src/Build.OM.UnitTests/Definition/Project_Tests.cs index a7be71e809a..5323c0fd85e 100644 --- a/src/Build.OM.UnitTests/Definition/Project_Tests.cs +++ b/src/Build.OM.UnitTests/Definition/Project_Tests.cs @@ -1839,11 +1839,11 @@ public void ChooseWhenTrue()

v1

-
+ -
+ "); @@ -1866,19 +1866,19 @@ public void ChooseSecondWhenTrue()

v1

-
+ -
+

v2

-
+ -
+ "); @@ -1901,19 +1901,19 @@ public void ChooseOtherwise()

v1

-
+ -
+

v2

-
+ -
+ "); @@ -1940,13 +1940,13 @@ public void ChooseTwoPasses()

@(i);v1

-
- + + v2 - + @@ -1956,13 +1956,13 @@ public void ChooseTwoPasses() @(j);v1 - + v2 - + "); @@ -2019,14 +2019,14 @@ public void ChooseSeesItemDefinitions() %(m);m1
- + m0 - + "); @@ -3078,7 +3078,7 @@ public void GetItemProvenanceWhenExcludeHasIndirectReferences() -

1;2;3;@(B)

+

1;2;3;@(B)

"; @@ -3104,7 +3104,7 @@ public void GetItemProvenanceWhenIncludeHasIndirectReferences() -

1;2;3;@(B)

+

1;2;3;@(B)

"; @@ -3130,7 +3130,7 @@ public void GetItemProvenanceWhenIncludeHasIndirectItemReferencesAndOnlyGlobsExi -

@(B)

+

@(B)

"; @@ -3158,7 +3158,7 @@ public void GetItemProvenanceShouldReturnInconclusiveWhenIndirectPropertyDoesNot -

+

"; @@ -3918,7 +3918,7 @@ public void ProjectImportedEventFalseCondition() eventArgs.LineNumber.ShouldBe(6); eventArgs.ColumnNumber.ShouldBe(3); - logger.AssertLogContains($"Project \"{import.Project}\" was not imported by \"{pre.FullPath}\" at ({eventArgs.LineNumber},{eventArgs.ColumnNumber}), due to false condition; ( \'$(Something)\' == \'nothing\' ) was evaluated as ( \'\' == \'nothing\' )."); + logger.AssertLogContains($"Project \"{import.Project}\" was not imported by \"{pre.FullPath}\" at ({eventArgs.LineNumber},{eventArgs.ColumnNumber}), due to false condition; ( \'$(Something)\' == \'nothing\' ) was evaluated as ( \'\' == \'nothing\' )."); } } } diff --git a/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj b/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj index 18ad711bac4..7c478d2a782 100644 --- a/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj +++ b/src/Build.OM.UnitTests/Microsoft.Build.Engine.OM.UnitTests.csproj @@ -1,4 +1,4 @@ - + @@ -21,6 +21,7 @@ + diff --git a/src/Build.OM.UnitTests/NugetRestoreTests.cs b/src/Build.OM.UnitTests/NugetRestoreTests.cs index 2ba75effddc..93d56ae6aaf 100644 --- a/src/Build.OM.UnitTests/NugetRestoreTests.cs +++ b/src/Build.OM.UnitTests/NugetRestoreTests.cs @@ -23,7 +23,7 @@ public NugetRestoreTests(ITestOutputHelper output) // This NuGet version cannot locate other assemblies when parsing solutions at restore time. This includes localized strings required in debug mode. // NuGet version 4.1.0 was somewhat arbitrarily chosen. 3.5 breaks with an unrelated error, and 4.8.2 does not fail when a new dependency is introduced. This is a safe middle point. #if !DEBUG - [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp | TargetFrameworkMonikers.Mono)] + [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp)] [Fact] public void TestOldNuget() { diff --git a/src/Build.UnitTests/BackEnd/BinaryTranslator_Tests.cs b/src/Build.UnitTests/BackEnd/BinaryTranslator_Tests.cs index 34b73c37ac7..e14afa3919a 100644 --- a/src/Build.UnitTests/BackEnd/BinaryTranslator_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BinaryTranslator_Tests.cs @@ -25,11 +25,11 @@ public class BinaryTranslator_Tests public void TestSerializationMode() { MemoryStream stream = new MemoryStream(); - ITranslator translator = BinaryTranslator.GetReadTranslator(stream, null); - Assert.Equal(TranslationDirection.ReadFromStream, translator.Mode); + using ITranslator readTranslator = BinaryTranslator.GetReadTranslator(stream, null); + Assert.Equal(TranslationDirection.ReadFromStream, readTranslator.Mode); - translator = BinaryTranslator.GetWriteTranslator(stream); - Assert.Equal(TranslationDirection.WriteToStream, translator.Mode); + using ITranslator writeTranslator = BinaryTranslator.GetWriteTranslator(stream); + Assert.Equal(TranslationDirection.WriteToStream, writeTranslator.Mode); } /// @@ -547,7 +547,6 @@ public void AssemblyNameWithAllFields() HashAlgorithm = System.Configuration.Assemblies.AssemblyHashAlgorithm.SHA256, VersionCompatibility = AssemblyVersionCompatibility.SameMachine, CodeBase = "C:\\src", - KeyPair = new StrongNameKeyPair(new byte[] { 4, 3, 2, 1 }), ContentType = AssemblyContentType.WindowsRuntime, CultureName = "zh-HK", }; diff --git a/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs b/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs index b09519f9f71..1587123afc0 100644 --- a/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BuildManager_Tests.cs @@ -8,6 +8,7 @@ using System.Globalization; using System.IO; using System.Linq; +using System.Reflection; using System.Threading; using System.Xml; @@ -277,10 +278,10 @@ public void VerifyEnvironmentSavedBetweenCalls() - + - + "); @@ -311,7 +312,7 @@ public void VerifyEnvironmentSavedBetweenCalls() /// /// Verify if idle nodes are shutdown when BuildManager.ShutdownAllNodes is evoked. - /// The final number of nodes has to be less or equal the number of nodes already in + /// The final number of nodes has to be less or equal the number of nodes already in /// the system before this method was called. /// #if RUNTIME_TYPE_NETCORE @@ -344,11 +345,11 @@ public void ShutdownNodesAfterParallelBuild(int numberOfParallelProjectsToBuild, // Generate a theoretically unique directory to put our dummy projects in. string shutdownProjectDirectory = Path.Combine(Path.GetTempPath(), String.Format(CultureInfo.InvariantCulture, "VSNodeShutdown_{0}_UnitTest", Process.GetCurrentProcess().Id)); - // Create the dummy projects we'll be "building" as our excuse to connect to and shut down - // all the nodes. + // Create the dummy projects we'll be "building" as our excuse to connect to and shut down + // all the nodes. ProjectInstance rootProject = GenerateDummyProjects(shutdownProjectDirectory, numberOfParallelProjectsToBuild, projectCollection); - // Build the projects. + // Build the projects. var buildParameters = new BuildParameters(projectCollection) { OnlyLogCriticalEvents = true, @@ -362,9 +363,9 @@ public void ShutdownNodesAfterParallelBuild(int numberOfParallelProjectsToBuild, // Tell the build manager to not disturb process wide state var requestData = new BuildRequestData(rootProject, new[] { "Build" }, null); - // Use a separate BuildManager for the node shutdown build, so that we don't have - // to worry about taking dependencies on whether or not the existing ones have already - // disappeared. + // Use a separate BuildManager for the node shutdown build, so that we don't have + // to worry about taking dependencies on whether or not the existing ones have already + // disappeared. var shutdownManager = new BuildManager("IdleNodeShutdown"); shutdownManager.Build(buildParameters, requestData); @@ -551,7 +552,7 @@ public void RequestedResultsAreSatisfied() } /// - /// Make sure when we are doing an in-process build that even if the environment variable MSBUILDFORWARDPROPERTIESFROMCHILD is set that we still + /// Make sure when we are doing an in-process build that even if the environment variable MSBUILDFORWARDPROPERTIESFROMCHILD is set that we still /// get all of the initial properties. /// [Fact] @@ -631,7 +632,7 @@ public void InProcMsBuildForwardAllPropertiesFromChild() } /// - /// Make sure when we launch a child node and set MsBuildForwardAllPropertiesFromChild that we get all of our properties. This needs to happen + /// Make sure when we launch a child node and set MsBuildForwardAllPropertiesFromChild that we get all of our properties. This needs to happen /// even if the msbuildforwardpropertiesfromchild is set to something. /// [Fact] @@ -703,6 +704,11 @@ public void OutOfProcNodeForwardCertainproperties() _env.SetEnvironmentVariable("MsBuildForwardPropertiesFromChild", "InitialProperty3;IAMNOTREAL"); _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1"); + // ProjectEvaluationFinished automatically and always forwards all properties, so we'd + // end up with all ~136 properties. Since this test is explicitly testing forwarding specific + // properties on ProjectStarted, turn off the new behavior. + _env.SetEnvironmentVariable("MSBUILDLOGPROPERTIESANDITEMSAFTEREVALUATION", "0"); + var project = CreateProject(contents, null, _projectCollection, false); var data = new BuildRequestData(project.FullPath, new Dictionary(), MSBuildDefaultToolsVersion, new string[] { }, null); @@ -835,7 +841,7 @@ public void ForwardNoPropertiesLaunchChildNode() } /// - /// We want to pass the toolsets from the parent to the child nodes so that any custom toolsets + /// We want to pass the toolsets from the parent to the child nodes so that any custom toolsets /// defined on the parent are also available on the child nodes for tasks which use the global project /// collection /// @@ -858,7 +864,7 @@ public void VerifyCustomToolSetsPropagated() - + "); @@ -1019,7 +1025,7 @@ public void DeferredMessageShouldBeLogged() } /// - /// A build with a message, error and warning, verify that + /// A build with a message, error and warning, verify that /// we only get errors, warnings, and project started and finished when OnlyLogCriticalEvents is true /// [Fact] @@ -1029,7 +1035,7 @@ public void SimpleBuildWithFailureAndWarningOnlyLogCriticalEventsTrue() - + @@ -1053,7 +1059,7 @@ public void SimpleBuildWithFailureAndWarningOnlyLogCriticalEventsTrue() } /// - /// A build with a message, error and warning, verify that + /// A build with a message, error and warning, verify that /// we only get errors, warnings, messages, task and target messages OnlyLogCriticalEvents is false /// [Fact] @@ -1063,7 +1069,7 @@ public void SimpleBuildWithFailureAndWarningOnlyLogCriticalEventsFalse() - + @@ -1386,9 +1392,9 @@ public void OverlappingBuildSubmissions() } /// - /// If two overlapping submissions are executed against the same project, with at least one - /// target involved that skipped, make sure that the second one successfully completes - /// (retrieved from the cache). + /// If two overlapping submissions are executed against the same project, with at least one + /// target involved that skipped, make sure that the second one successfully completes + /// (retrieved from the cache). /// [Fact] public void OverlappingIdenticalBuildSubmissions() @@ -1419,9 +1425,9 @@ public void OverlappingIdenticalBuildSubmissions() } /// - /// With two overlapping submissions, the first of which skips a target and the second - /// of which should not, ensure that the second submission does not, in fact, skip - /// the target. (E.g. despite the fact that the target results are in the cache already + /// With two overlapping submissions, the first of which skips a target and the second + /// of which should not, ensure that the second submission does not, in fact, skip + /// the target. (E.g. despite the fact that the target results are in the cache already /// as 'skipped', ensure that we retry execution in case conditions have changed.) /// [Fact] @@ -1516,6 +1522,7 @@ public void CancelledBuildWithUnexecutedSubmission() [Fact(Timeout = 20_000)] public void CancelledBuild() { + Console.WriteLine("Starting CancelledBuild test that is known to hang."); string contents = CleanupFileContents(@" @@ -1524,18 +1531,36 @@ public void CancelledBuild() "); + + BuildParameters parameters = new () + { + ShutdownInProcNodeOnBuildFinish = true, + Loggers = new ILogger[] { _logger, new MockLogger(printEventsToStdout: true) }, + EnableNodeReuse = false + }; + BuildRequestData data = GetBuildRequestData(contents, new string[] { }, MSBuildDefaultToolsVersion); + + Console.WriteLine("CancelledBuild: beginning build"); _buildManager.BeginBuild(_parameters); + Console.WriteLine("CancelledBuild: build begun"); + BuildSubmission asyncResult = _buildManager.PendBuildRequest(data); + Console.WriteLine("CancelledBuild: pend build returned"); + asyncResult.ExecuteAsync(null, null); + Console.WriteLine("CancelledBuild: ExecuteAsync called"); _buildManager.CancelAllSubmissions(); + Console.WriteLine("CancelledBuild: submissions cancelled"); + // This test intermittently hangs. This timeout is designed to prevent that, turning a hang into a failure. // Todo: Investigate why this test sometimes hangs. - asyncResult.WaitHandle.WaitOne(TimeSpan.FromSeconds(10)); + asyncResult.WaitHandle.WaitOne(TimeSpan.FromSeconds(10)).ShouldBeTrue(); asyncResult.IsCompleted.ShouldBeTrue("Failing to complete by this point indicates a hang."); BuildResult result = asyncResult.BuildResult; _buildManager.EndBuild(); + Console.WriteLine("CancelledBuild: build ended"); Assert.Equal(BuildResultCode.Failure, result.OverallResult); // "Build should have failed." _logger.AssertLogDoesntContain("[errormessage]"); @@ -1619,7 +1644,7 @@ public void CancelledBuildInTaskHostWithDelay20() /// /// A canceled build which waits for the task to get started before canceling. Because it is a 12.. task, we should - /// cancel the task and exit out after a short period wherein we wait for the task to exit cleanly. + /// cancel the task and exit out after a short period wherein we wait for the task to exit cleanly. /// [Fact] public void CancelledBuildWithDelay40() @@ -1650,7 +1675,7 @@ public void CancelledBuildWithDelay40() #if FEATURE_TASKHOST /// /// A canceled build which waits for the task to get started before canceling. Because it is a 12.0 task, we should - /// cancel the task and exit out after a short period wherein we wait for the task to exit cleanly. + /// cancel the task and exit out after a short period wherein we wait for the task to exit cleanly. /// [Fact] public void CancelledBuildInTaskHostWithDelay40() @@ -2122,7 +2147,7 @@ public void Regress239661() "); - + string fileName = _env.CreateFile(".proj").Path; File.WriteAllText(fileName, contents); var data = new BuildRequestData(fileName, _projectCollection.GlobalProperties, MSBuildDefaultToolsVersion, new string[0], null); @@ -2133,10 +2158,10 @@ public void Regress239661() } /// - /// Verify that disabling the in-proc node when a project requires it will cause the build to fail, but not crash. + /// Verify that disabling the in-proc node when a project requires it will cause the project to build on the out of proc node. /// [Fact] - public void Regress239661_NodeUnavailable() + public void ExplicitInprocAffinityGetsOverruledByDisableInprocNode() { string contents = CleanupFileContents(@" @@ -2151,14 +2176,15 @@ public void Regress239661_NodeUnavailable() "); BuildRequestData data = GetBuildRequestData(contents); + _env.CreateFile(data.ProjectFullPath, data.ProjectInstance.ToProjectRootElement().RawXml); _parameters.DisableInProcNode = true; // Require that this project build on the in-proc node, which will not be available. data.HostServices.SetNodeAffinity(data.ProjectFullPath, NodeAffinity.InProc); BuildResult result = _buildManager.Build(_parameters, data); - Assert.Equal(BuildResultCode.Failure, result.OverallResult); - _logger.AssertLogDoesntContain("[success]"); - _logger.AssertLogContains("MSB4223"); + Assert.Equal(BuildResultCode.Success, result.OverallResult); + _logger.AssertLogContains("[success]"); + _logger.AssertLogDoesntContain("MSB4223"); } /// @@ -2180,9 +2206,9 @@ public void ProjectInstanceTransfersToOOPNode() - + - + @@ -2242,7 +2268,7 @@ public void ProjectInstanceLimitedTransferToOOPNode() unmodified original - + @@ -2293,7 +2319,7 @@ public void CacheLifetime() { innerBuildCacheDirectory = BuildAndCheckCache(innerBuildManager, new[] { outerBuildCacheDirectory }); - // Force the cache for this build manager (and only this build manager) to be cleared. It should leave + // Force the cache for this build manager (and only this build manager) to be cleared. It should leave // behind the results from the other one. innerBuildManager.ResetCaches(); } @@ -2309,9 +2335,9 @@ public void CacheLifetime() } /// - /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the + /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the /// overall build result -- and thus the return value of the MSBuild task -- should reflect - /// that failure. + /// that failure. /// [Theory] [InlineData(false)] @@ -2325,9 +2351,9 @@ public void FailedAfterTargetInP2PShouldCauseOverallBuildFailure(bool disableInP - + - + "; @@ -2357,10 +2383,10 @@ public void FailedAfterTargetInP2PShouldCauseOverallBuildFailure(bool disableInP } /// - /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the + /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the /// overall build result -- and thus the return value of the MSBuild task -- should reflect - /// that failure. Specifically tests where there are multiple entrypoint targets with - /// AfterTargets, only one of which fails. + /// that failure. Specifically tests where there are multiple entrypoint targets with + /// AfterTargets, only one of which fails. /// [Theory] [InlineData(false)] @@ -2374,9 +2400,9 @@ public void FailedAfterTargetInP2PShouldCauseOverallBuildFailure_MultipleEntrypo - + - + "; @@ -2423,9 +2449,9 @@ public void FailedAfterTargetInP2PShouldCauseOverallBuildFailure_MultipleEntrypo } /// - /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the + /// If there's a P2P that otherwise succeeds, but has an AfterTarget that errors out, the /// overall build result -- and thus the return value of the MSBuild task -- should reflect - /// that failure. This should also be true if the AfterTarget is an AfterTarget of the + /// that failure. This should also be true if the AfterTarget is an AfterTarget of the /// entrypoint target. /// [Theory] @@ -2440,9 +2466,9 @@ public void FailedNestedAfterTargetInP2PShouldCauseOverallBuildFailure(bool disa - + - + "; @@ -2476,9 +2502,9 @@ public void FailedNestedAfterTargetInP2PShouldCauseOverallBuildFailure(bool disa } /// - /// If a project is called into twice, with two different entrypoint targets that - /// depend on non-overlapping sets of targets, and the first fails, the second - /// should not inherit that failure if all the targets it calls succeed. + /// If a project is called into twice, with two different entrypoint targets that + /// depend on non-overlapping sets of targets, and the first fails, the second + /// should not inherit that failure if all the targets it calls succeed. /// [Fact] public void NonOverlappingEnusingTrypointTargetsShouldNotInfluenceEachOthersResults() @@ -2489,7 +2515,7 @@ public void NonOverlappingEnusingTrypointTargetsShouldNotInfluenceEachOthersResu string contentsA = @" - + @@ -2503,8 +2529,8 @@ public void NonOverlappingEnusingTrypointTargetsShouldNotInfluenceEachOthersResu - - + + @@ -2524,9 +2550,9 @@ public void NonOverlappingEnusingTrypointTargetsShouldNotInfluenceEachOthersResu } /// - /// In a situation where we have two requests calling into the same project, with different entry point - /// targets, one of which depends on "A;B", the other of which depends on "B", which has a dependency of - /// its own on "A", that we still properly build. + /// In a situation where we have two requests calling into the same project, with different entry point + /// targets, one of which depends on "A;B", the other of which depends on "B", which has a dependency of + /// its own on "A", that we still properly build. /// #if RUNTIME_TYPE_NETCORE [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/933")] @@ -2545,7 +2571,7 @@ public void Regress473114() string contentsA = @" - + @@ -2615,19 +2641,19 @@ public void Regress473114() } /// - /// If two requests are made for the same project, and they call in with - /// just the right timing such that: + /// If two requests are made for the same project, and they call in with + /// just the right timing such that: /// - request 1 builds for a while, reaches a P2P, and blocks - /// - request 2 starts building, skips for a while, reaches the above P2P, and - /// blocks waiting for request 1's results + /// - request 2 starts building, skips for a while, reaches the above P2P, and + /// blocks waiting for request 1's results /// - request 1 resumes building, errors, and exits /// - request 2 resumes building - /// - /// Then request 2 should end up exiting in the same fashion. - /// - /// This simple test verifies that if there are two error targets in a row, the - /// second request will bail out where the first request did, as though it had - /// executed the target, rather than skipping and continuing. + /// + /// Then request 2 should end up exiting in the same fashion. + /// + /// This simple test verifies that if there are two error targets in a row, the + /// second request will bail out where the first request did, as though it had + /// executed the target, rather than skipping and continuing. /// #if MONO [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] @@ -2704,9 +2730,9 @@ public void VerifyMultipleRequestForSameProjectWithErrors_Simple() /// blocks waiting for request 1's results /// - request 1 resumes building, errors, and exits /// - request 2 resumes building - /// + /// /// Then request 2 should end up exiting in the same fashion. - /// + /// /// This simple test verifies that if there are two error targets in a row, and the /// first has a chain of OnError targets, the OnError targets will all execute as /// expected in the first request, but be skipped by the second (since if it's "skipping @@ -2824,9 +2850,9 @@ public void VerifyMultipleRequestForSameProjectWithErrors_OnErrorChain() /// blocks waiting for request 1's results /// - request 1 resumes building, errors, and exits /// - request 2 resumes building - /// + /// /// Then request 2 should end up exiting in the same fashion. - /// + /// /// This simple test verifies that if there are two error targets in a row, AND /// they're marked as ContinueOnError=ErrorAndContinue, then we won't bail, but /// will continue executing (on the first request) or skipping (on the second) @@ -2908,18 +2934,18 @@ public void VerifyMultipleRequestForSameProjectWithErrors_ErrorAndContinue() } /// - /// If two requests are made for the same project, and they call in with - /// just the right timing such that: + /// If two requests are made for the same project, and they call in with + /// just the right timing such that: /// - request 1 builds for a while, reaches a P2P, and blocks - /// - request 2 starts building, skips for a while, reaches the above P2P, and - /// blocks waiting for request 1's results + /// - request 2 starts building, skips for a while, reaches the above P2P, and + /// blocks waiting for request 1's results /// - request 1 resumes building, errors, and exits /// - request 2 resumes building - /// - /// Then request 2 should end up exiting in the same fashion. - /// - /// This test verifies that if the errors are in AfterTargets, we still - /// exit as though the target that those targets run after has already run. + /// + /// Then request 2 should end up exiting in the same fashion. + /// + /// This test verifies that if the errors are in AfterTargets, we still + /// exit as though the target that those targets run after has already run. /// #if MONO [Fact(Skip = "https://github.com/Microsoft/msbuild/issues/1240")] @@ -2989,10 +3015,10 @@ public void VerifyMultipleRequestForSameProjectWithErrors_AfterTargets() } /// - /// Related to the two tests above, if two requests are made for the same project, but - /// for different entry targets, and a target fails in the first request, if the second - /// request also runs that target, its skip-unsuccessful should behave in the same - /// way as if the target had actually errored. + /// Related to the two tests above, if two requests are made for the same project, but + /// for different entry targets, and a target fails in the first request, if the second + /// request also runs that target, its skip-unsuccessful should behave in the same + /// way as if the target had actually errored. /// [Fact] public void VerifyMultipleRequestForSameProjectWithErrors_DifferentEntrypoints() @@ -3002,7 +3028,7 @@ public void VerifyMultipleRequestForSameProjectWithErrors_DifferentEntrypoints() string contentsA = @" - + Build @@ -3119,7 +3145,7 @@ public void TestSimultaneousSubmissionsWithLegacyThreadingData() } /// - /// Verify that we can submit multiple simultaneous submissions with + /// Verify that we can submit multiple simultaneous submissions with /// legacy threading mode active and successfully build, and that one of those /// submissions can P2P to the other. /// @@ -3200,12 +3226,12 @@ public void TestSimultaneousSubmissionsWithLegacyThreadingData_P2P() } /// - /// Verify that we can submit multiple simultaneous submissions with + /// Verify that we can submit multiple simultaneous submissions with /// legacy threading mode active and successfully build, and that one of those /// submissions can P2P to the other. - /// - /// A variation of the above test, where multiple nodes are available, so the - /// submissions aren't restricted to running strictly serially by the single in-proc + /// + /// A variation of the above test, where multiple nodes are available, so the + /// submissions aren't restricted to running strictly serially by the single in-proc /// node. /// #if MONO @@ -3308,7 +3334,7 @@ public void Regress265010() - + @@ -3319,7 +3345,7 @@ public void Regress265010() - + @@ -3452,13 +3478,13 @@ private static string BuildAndCheckCache(BuildManager localBuildManager, IEnumer { string contents = CleanupFileContents(@" - + - + - + "); @@ -3580,8 +3606,8 @@ private static ProjectInstance GenerateDummyProjects(string shutdownProjectDirec Directory.CreateDirectory(shutdownProjectDirectory); // Generate the project. It will have the following format. Setting the AdditionalProperties - // causes the projects to be built to be separate configs, which allows us to build the same project - // a bunch of times in parallel. + // causes the projects to be built to be separate configs, which allows us to build the same project + // a bunch of times in parallel. // // // @@ -3934,7 +3960,7 @@ public void OutOfProcEvaluationIdsUnique() /// Regression test for https://github.com/Microsoft/msbuild/issues/3047 /// [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "out-of-proc nodes not working on mono yet")] + [SkipOnMono("out-of-proc nodes not working on mono yet")] public void MultiProcReentrantProjectWithCallTargetDoesNotFail() { var a = @@ -4317,5 +4343,52 @@ public void GraphBuildShouldBeAbleToConstructGraphButSkipBuild() logger.FullLog.ShouldContain("Static graph loaded in"); logger.FullLog.ShouldContain("3 nodes, 2 edges"); } + + /// + /// Helper task used by to verify . + /// + public class LogTaskInputsCheckingTask : Task + { + public bool ExpectedTaskInputLoggingEnabled { get; set; } + + public override bool Execute() + { + return Log.IsTaskInputLoggingEnabled == ExpectedTaskInputLoggingEnabled; + } + } + + [Theory] + [InlineData("", false)] // regular task host, input logging disabled + [InlineData("", true)] // regular task host, input logging enabled +#if NETFRAMEWORK // https://github.com/microsoft/msbuild/issues/5158 + [InlineData("TaskHostFactory", false)] // OOP task host, input logging disabled + [InlineData("TaskHostFactory", true)] // OOP task host, input logging enabled +#endif + public void TaskInputLoggingIsExposedToTasks(string taskFactory, bool taskInputLoggingEnabled) + { + string projectContents = ObjectModelHelpers.CleanupFileContents(@" + + + + + + + +"); + + _parameters.LogTaskInputs = taskInputLoggingEnabled; + + Project project = CreateProject(projectContents, MSBuildDefaultToolsVersion, _projectCollection, true); + ProjectInstance instance = _buildManager.GetProjectInstanceForBuild(project); + _buildManager.BeginBuild(_parameters); + BuildResult result = _buildManager.BuildRequest(new BuildRequestData(instance, new[] { "target1" })); + _buildManager.EndBuild(); + + Assert.Equal(BuildResultCode.Success, result.OverallResult); + } } } diff --git a/src/Build.UnitTests/BackEnd/BuildRequestConfiguration_Tests.cs b/src/Build.UnitTests/BackEnd/BuildRequestConfiguration_Tests.cs index 5be64a17d58..eb37f60b029 100644 --- a/src/Build.UnitTests/BackEnd/BuildRequestConfiguration_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BuildRequestConfiguration_Tests.cs @@ -26,7 +26,6 @@ public class BuildRequestConfiguration_Tests : IDisposable public BuildRequestConfiguration_Tests(ITestOutputHelper testOutput) { _env = TestEnvironment.Create(testOutput); - _env.DoNotLaunchDebugger(); } public void Dispose() @@ -260,6 +259,51 @@ public void TestTranslation() Assert.Equal(config, deserializedConfig); } + [Fact] + public void TestTranslationWithEntireProjectState() + { + string projectBody = ObjectModelHelpers.CleanupFileContents(@" + + + 1 + 2 + $(ThreeIn) + + + + +"); + + Dictionary globalProperties = new (StringComparer.OrdinalIgnoreCase); + globalProperties["ThreeIn"] = "3"; + + Project project = new Project( + XmlReader.Create(new StringReader(projectBody)), + globalProperties, + ObjectModelHelpers.MSBuildDefaultToolsVersion, + new ProjectCollection()); + project.FullPath = "foo"; + ProjectInstance instance = project.CreateProjectInstance(); + + instance.TranslateEntireState = true; + + BuildRequestConfiguration configuration = new BuildRequestConfiguration(new BuildRequestData(instance, new string[] { }, null), "2.0"); + configuration.ConfigurationId = 1; + + ((ITranslatable)configuration).Translate(TranslationHelpers.GetWriteTranslator()); + INodePacket packet = BuildRequestConfiguration.FactoryForDeserialization(TranslationHelpers.GetReadTranslator()); + + BuildRequestConfiguration deserializedConfig = packet as BuildRequestConfiguration; + + deserializedConfig.ShouldNotBeNull(); + deserializedConfig.ShouldBe(configuration); + deserializedConfig.Project.ShouldNotBeNull(); + + // Verify that at least some data from 'entire project state' has been deserialized. + deserializedConfig.Project.Directory.ShouldNotBeEmpty(); + deserializedConfig.Project.Directory.ShouldBe(configuration.Project.Directory); + } + [Fact] public void TestProperties() { diff --git a/src/Build.UnitTests/BackEnd/BuildRequest_Tests.cs b/src/Build.UnitTests/BackEnd/BuildRequest_Tests.cs index 75e018b7cdc..9cede0f7298 100644 --- a/src/Build.UnitTests/BackEnd/BuildRequest_Tests.cs +++ b/src/Build.UnitTests/BackEnd/BuildRequest_Tests.cs @@ -138,7 +138,7 @@ public void TestTranslation() #if FEATURE_COM_INTEROP [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "disable com tests on mono")] + [SkipOnMono("disable com tests on mono")] public void TestTranslationRemoteHostObjects() { var stateInHostObject = 3; diff --git a/src/Build.UnitTests/BackEnd/DebugUtils_tests.cs b/src/Build.UnitTests/BackEnd/DebugUtils_tests.cs new file mode 100644 index 00000000000..07c9c392b41 --- /dev/null +++ b/src/Build.UnitTests/BackEnd/DebugUtils_tests.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft. 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.Linq; +using Microsoft.Build.Shared; +using Shouldly; +using Xunit; + +namespace Microsoft.Build.UnitTests +{ + public class DebugUtils_Tests + { + [Fact] + public void DumpExceptionToFileShouldWriteInTempPathByDefault() + { + Directory.GetFiles(Path.GetTempPath(), "MSBuild_*failure.txt").ShouldBeEmpty(); + + string[] exceptionFiles = null; + + try + { + ExceptionHandling.DumpExceptionToFile(new Exception("hello world")); + exceptionFiles = Directory.GetFiles(Path.GetTempPath(), "MSBuild_*failure.txt"); + } + finally + { + exceptionFiles.ShouldNotBeNull(); + exceptionFiles.ShouldHaveSingleItem(); + + var exceptionFile = exceptionFiles.First(); + File.ReadAllText(exceptionFile).ShouldContain("hello world"); + File.Delete(exceptionFile); + } + } + } +} diff --git a/src/Build.UnitTests/BackEnd/LoggingService_Tests.cs b/src/Build.UnitTests/BackEnd/LoggingService_Tests.cs index 9a485cb0d49..4d84c33b60f 100644 --- a/src/Build.UnitTests/BackEnd/LoggingService_Tests.cs +++ b/src/Build.UnitTests/BackEnd/LoggingService_Tests.cs @@ -665,6 +665,11 @@ public void Properties() Assert.Equal(1, loggingService.MaxCPUCount); loggingService.MaxCPUCount = 5; Assert.Equal(5, loggingService.MaxCPUCount); + + // Test MinimumRequiredMessageImportance + Assert.Equal(MessageImportance.Low, loggingService.MinimumRequiredMessageImportance); + loggingService.RegisterLogger(new ConsoleLogger(LoggerVerbosity.Normal)); + Assert.Equal(MessageImportance.Normal, loggingService.MinimumRequiredMessageImportance); } #endregion @@ -718,6 +723,8 @@ public void LoggingPacketReceived() #endregion + #region WarningsAsErrors Tests + private static readonly BuildWarningEventArgs BuildWarningEventForTreatAsErrorOrMessageTests = new BuildWarningEventArgs("subcategory", "C94A41A90FFB4EF592BF98BA59BEE8AF", "file", 1, 2, 3, 4, "message", "helpKeyword", "senderName"); /// @@ -1000,6 +1007,76 @@ private MockLogger GetLoggedEventsWithWarningsAsErrorsOrMessages( return logger; } + #endregion + + #region MinimumRequiredMessageImportance Tests + + [Fact] + public void ImportanceReflectsConsoleLoggerVerbosity() + { + _initializedService.RegisterLogger(new ConsoleLogger(LoggerVerbosity.Quiet)); + _initializedService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.High - 1); + _initializedService.RegisterLogger(new ConsoleLogger(LoggerVerbosity.Minimal)); + _initializedService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.High); + _initializedService.RegisterLogger(new ConsoleLogger(LoggerVerbosity.Normal)); + _initializedService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.Normal); + _initializedService.RegisterLogger(new ConsoleLogger(LoggerVerbosity.Detailed)); + _initializedService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.Low); + _initializedService.RegisterLogger(new ConsoleLogger(LoggerVerbosity.Diagnostic)); + _initializedService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.Low); + } + + [Fact] + public void ImportanceReflectsConfigurableForwardingLoggerVerbosity() + { + _initializedService.RegisterLogger(CreateConfigurableForwardingLogger(LoggerVerbosity.Quiet)); + _initializedService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.High - 1); + _initializedService.RegisterLogger(CreateConfigurableForwardingLogger(LoggerVerbosity.Minimal)); + _initializedService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.High); + _initializedService.RegisterLogger(CreateConfigurableForwardingLogger(LoggerVerbosity.Normal)); + _initializedService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.Normal); + _initializedService.RegisterLogger(CreateConfigurableForwardingLogger(LoggerVerbosity.Detailed)); + _initializedService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.Low); + _initializedService.RegisterLogger(CreateConfigurableForwardingLogger(LoggerVerbosity.Diagnostic)); + _initializedService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.Low); + } + + [Fact] + public void ImportanceReflectsCentralForwardingLoggerVerbosity() + { + MockHost mockHost = new MockHost(); + ILoggingService node1LoggingService = LoggingService.CreateLoggingService(LoggerMode.Synchronous, 1); + ((IBuildComponent)node1LoggingService).InitializeComponent(mockHost); + ILoggingService node2LoggingService = LoggingService.CreateLoggingService(LoggerMode.Synchronous, 2); + ((IBuildComponent)node2LoggingService).InitializeComponent(mockHost); + + // CentralForwardingLogger is always registered in in-proc nodes and it does not affect minimum importance. + node1LoggingService.RegisterLogger(CreateConfigurableForwardingLogger(LoggerVerbosity.Minimal)); + node1LoggingService.RegisterLogger(new CentralForwardingLogger()); + node1LoggingService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.High); + + // CentralForwardingLogger in out-of-proc nodes means that we are forwarding everything and the minimum importance + // is Low regardless of what other loggers are registered. + node2LoggingService.RegisterLogger(CreateConfigurableForwardingLogger(LoggerVerbosity.Minimal)); + node2LoggingService.RegisterLogger(new CentralForwardingLogger()); + node2LoggingService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.Low); + // Register another ConsoleLogger and verify that minimum importance hasn't changed. + node2LoggingService.RegisterLogger(CreateConfigurableForwardingLogger(LoggerVerbosity.Minimal)); + node2LoggingService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.Low); + } + + [Fact] + public void ImportanceReflectsUnknownLoggerVerbosity() + { + // Minimum message importance is Low (i.e. we're logging everything) even when all registered loggers have + // Normal verbosity if at least of one them is not on our whitelist. + _initializedService.RegisterLogger(new ConsoleLogger(LoggerVerbosity.Normal)); + _initializedService.RegisterLogger(new MockLogger() { Verbosity = LoggerVerbosity.Normal }); + _initializedService.RegisterLogger(CreateConfigurableForwardingLogger(LoggerVerbosity.Normal)); + _initializedService.MinimumRequiredMessageImportance.ShouldBe(MessageImportance.Low); + } + #endregion + #region PrivateMethods /// @@ -1084,6 +1161,17 @@ private LoggerDescription CreateLoggerDescription(string loggerClassName, string ); return centralLoggerDescrption; } + + /// + /// Creates a new with the given verbosity. + /// + private ConfigurableForwardingLogger CreateConfigurableForwardingLogger(LoggerVerbosity verbosity) + { + return new ConfigurableForwardingLogger() + { + Verbosity = verbosity + }; + } #endregion #region HelperClasses diff --git a/src/Build.UnitTests/BackEnd/MockLoggingService.cs b/src/Build.UnitTests/BackEnd/MockLoggingService.cs index d428e3fdac6..2d4ed73b93d 100644 --- a/src/Build.UnitTests/BackEnd/MockLoggingService.cs +++ b/src/Build.UnitTests/BackEnd/MockLoggingService.cs @@ -223,6 +223,11 @@ public bool IncludeTaskInputs set { } } + public MessageImportance MinimumRequiredMessageImportance + { + get => MessageImportance.Low; + } + public void AddWarningsAsMessages(BuildEventContext buildEventContext, ISet codes) { throw new NotImplementedException(); @@ -553,7 +558,7 @@ public void LogTaskStarted(BuildEventContext targetBuildEventContext, string tas /// The project file /// The project file containing the task node. /// The task logging context - public BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode) + public BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, int line, int column) { return new BuildEventContext(0, 0, 0, 0); } diff --git a/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs b/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs index a3137f5b399..78d73604056 100644 --- a/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs +++ b/src/Build.UnitTests/BackEnd/NodePackets_Tests.cs @@ -52,6 +52,7 @@ public void VerifyEventType() BuildErrorEventArgs error = new BuildErrorEventArgs("SubCategoryForSchemaValidationErrors", "MSB4000", "file", 1, 2, 3, 4, "message", "help", "sender"); TargetStartedEventArgs targetStarted = new TargetStartedEventArgs("message", "help", "targetName", "ProjectFile", "targetFile"); TargetFinishedEventArgs targetFinished = new TargetFinishedEventArgs("message", "help", "targetName", "ProjectFile", "targetFile", true); + TargetSkippedEventArgs targetSkipped = CreateTargetSkipped(); ProjectStartedEventArgs projectStarted = new ProjectStartedEventArgs(-1, "message", "help", "ProjectFile", "targetNames", null, null, null); ProjectFinishedEventArgs projectFinished = new ProjectFinishedEventArgs("message", "help", "ProjectFile", true); ExternalProjectStartedEventArgs externalStartedEvent = new ExternalProjectStartedEventArgs("message", "help", "senderName", "projectFile", "targetNames"); @@ -69,6 +70,7 @@ public void VerifyEventType() VerifyLoggingPacket(error, LoggingEventType.BuildErrorEvent); VerifyLoggingPacket(targetStarted, LoggingEventType.TargetStartedEvent); VerifyLoggingPacket(targetFinished, LoggingEventType.TargetFinishedEvent); + VerifyLoggingPacket(targetSkipped, LoggingEventType.TargetSkipped); VerifyLoggingPacket(projectStarted, LoggingEventType.ProjectStartedEvent); VerifyLoggingPacket(projectFinished, LoggingEventType.ProjectFinishedEvent); VerifyLoggingPacket(evaluationStarted, LoggingEventType.ProjectEvaluationStartedEvent); @@ -158,6 +160,8 @@ private static TaskParameterEventArgs CreateTaskParameter() items, logItemMetadata: true, DateTime.MinValue); + result.LineNumber = 30000; + result.ColumnNumber = 50; // normalize line endings as we can't rely on the line endings of NodePackets_Tests.cs Assert.Equal(@"Task Parameter: @@ -174,6 +178,26 @@ private static TaskParameterEventArgs CreateTaskParameter() return result; } + private static TargetSkippedEventArgs CreateTargetSkipped() + { + var result = new TargetSkippedEventArgs(message: null) + { + BuildReason = TargetBuiltReason.DependsOn, + SkipReason = TargetSkipReason.PreviouslyBuiltSuccessfully, + BuildEventContext = CreateBuildEventContext(), + OriginalBuildEventContext = CreateBuildEventContext(), + Condition = "$(Condition) == 'true'", + EvaluatedCondition = "'true' == 'true'", + Importance = MessageImportance.Normal, + OriginallySucceeded = true, + ProjectFile = "1.proj", + TargetFile = "1.proj", + TargetName = "Build", + ParentTarget = "ParentTarget" + }; + return result; + } + /// /// Tests serialization of LogMessagePacket with each kind of event type. /// @@ -195,7 +219,11 @@ public void TestTranslation() new BuildFinishedEventArgs("Message", "Keyword", true), new BuildStartedEventArgs("Message", "Help"), new BuildMessageEventArgs("Message", "help", "sender", MessageImportance.Low), - new TaskStartedEventArgs("message", "help", "projectFile", "taskFile", "taskName"), + new TaskStartedEventArgs("message", "help", "projectFile", "taskFile", "taskName") + { + LineNumber = 345, + ColumnNumber = 123 + }, new TaskFinishedEventArgs("message", "help", "projectFile", "taskFile", "taskName", true), new TaskCommandLineEventArgs("commandLine", "taskName", MessageImportance.Low), CreateTaskParameter(), @@ -207,7 +235,8 @@ public void TestTranslation() new ProjectFinishedEventArgs("message", "help", "ProjectFile", true), new ExternalProjectStartedEventArgs("message", "help", "senderName", "projectFile", "targetNames"), CreateProjectEvaluationStarted(), - CreateProjectEvaluationFinished() + CreateProjectEvaluationFinished(), + CreateTargetSkipped() }; foreach (BuildEventArgs arg in testArgs) @@ -412,6 +441,23 @@ private void CompareLogMessagePackets(LogMessagePacket left, LogMessagePacket ri Assert.Equal(leftTargetStarted.TargetName, rightTargetStarted.TargetName); break; + case LoggingEventType.TargetSkipped: + TargetSkippedEventArgs leftTargetSkipped = left.NodeBuildEvent.Value.Value as TargetSkippedEventArgs; + TargetSkippedEventArgs rightTargetSkipped = right.NodeBuildEvent.Value.Value as TargetSkippedEventArgs; + Assert.Equal(leftTargetSkipped.BuildReason, rightTargetSkipped.BuildReason); + Assert.Equal(leftTargetSkipped.SkipReason, rightTargetSkipped.SkipReason); + Assert.Equal(leftTargetSkipped.BuildEventContext, rightTargetSkipped.BuildEventContext); + Assert.Equal(leftTargetSkipped.OriginalBuildEventContext, rightTargetSkipped.OriginalBuildEventContext); + Assert.Equal(leftTargetSkipped.Condition, rightTargetSkipped.Condition); + Assert.Equal(leftTargetSkipped.EvaluatedCondition, rightTargetSkipped.EvaluatedCondition); + Assert.Equal(leftTargetSkipped.Importance, rightTargetSkipped.Importance); + Assert.Equal(leftTargetSkipped.OriginallySucceeded, rightTargetSkipped.OriginallySucceeded); + Assert.Equal(leftTargetSkipped.ProjectFile, rightTargetSkipped.ProjectFile); + Assert.Equal(leftTargetSkipped.TargetFile, rightTargetSkipped.TargetFile); + Assert.Equal(leftTargetSkipped.TargetName, rightTargetSkipped.TargetName); + Assert.Equal(leftTargetSkipped.ParentTarget, rightTargetSkipped.ParentTarget); + break; + case LoggingEventType.TaskCommandLineEvent: TaskCommandLineEventArgs leftCommand = left.NodeBuildEvent.Value.Value as TaskCommandLineEventArgs; TaskCommandLineEventArgs rightCommand = right.NodeBuildEvent.Value.Value as TaskCommandLineEventArgs; @@ -433,6 +479,8 @@ private void CompareLogMessagePackets(LogMessagePacket left, LogMessagePacket ri Assert.Equal(leftTaskParameter.Message, rightTaskParameter.Message); Assert.Equal(leftTaskParameter.BuildEventContext, rightTaskParameter.BuildEventContext); Assert.Equal(leftTaskParameter.Timestamp, rightTaskParameter.Timestamp); + Assert.Equal(leftTaskParameter.LineNumber, rightTaskParameter.LineNumber); + Assert.Equal(leftTaskParameter.ColumnNumber, rightTaskParameter.ColumnNumber); break; case LoggingEventType.TaskFinishedEvent: @@ -454,6 +502,8 @@ private void CompareLogMessagePackets(LogMessagePacket left, LogMessagePacket ri Assert.Equal(leftTaskStarted.ProjectFile, rightTaskStarted.ProjectFile); Assert.Equal(leftTaskStarted.TaskFile, rightTaskStarted.TaskFile); Assert.Equal(leftTaskStarted.TaskName, rightTaskStarted.TaskName); + Assert.Equal(leftTaskStarted.LineNumber, rightTaskStarted.LineNumber); + Assert.Equal(leftTaskStarted.ColumnNumber, rightTaskStarted.ColumnNumber); break; default: diff --git a/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs b/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs index dc86010e269..ddb926d7d7b 100644 --- a/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs +++ b/src/Build.UnitTests/BackEnd/Scheduler_Tests.cs @@ -10,6 +10,8 @@ using Microsoft.Build.Shared; using Microsoft.Build.Execution; using Microsoft.Build.Evaluation; +using Microsoft.Build.Experimental.ProjectCache; +using Shouldly; using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem; using Xunit; @@ -496,6 +498,14 @@ public void TestMaxNodeCountNodesNotExceededWithSomeOOPRequests2() Assert.Equal(2, response[1].NumberOfNodesToCreate); } + [Fact] + public void SchedulerShouldHonorDisableInprocNode() + { + var s = new Scheduler(); + s.InitializeComponent(new MockHost(new BuildParameters {DisableInProcNode = true})); + s.ForceAffinityOutOfProc.ShouldBeTrue(); + } + /// /// Make sure that traversal projects are marked with an affinity of "InProc", which means that /// even if multiple are available, we should still only have the single inproc node. @@ -520,6 +530,31 @@ public void TestTraversalAffinityIsInProc() Assert.Equal(request1, response[0].BuildRequest); } + /// + /// Make sure that traversal projects are marked with an affinity of "InProc", which means that + /// even if multiple are available, we should still only have the single inproc node. + /// + [Fact] + public void TestProxyAffinityIsInProc() + { + _host.BuildParameters.MaxNodeCount = 4; + ReportDefaultParentRequestIsFinished(); + + CreateConfiguration(1, "foo.csproj"); + + BuildRequest request1 = CreateProxyBuildRequest(1, 1, new ProxyTargets(new Dictionary {{"foo", "bar"}}), null); + + BuildRequestBlocker blocker = new BuildRequestBlocker(-1, new string[] { }, new[] { request1 }); + List response = new List(_scheduler.ReportRequestBlocked(1, blocker)); + + // There will be no request to create a new node, because both of the above requests are proxy build requests, + // which have an affinity of "inproc", and the inproc node already exists. + Assert.Single(response); + Assert.Equal(ScheduleActionType.ScheduleWithConfiguration, response[0].Action); + Assert.Equal(request1, response[0].BuildRequest); + Assert.Equal(Scheduler.InProcNodeId, response[0].NodeId); + } + /// /// With something approximating the BuildManager's build loop, make sure that we don't end up /// trying to create more nodes than we can actually support. @@ -729,8 +764,10 @@ private BuildRequest CreateBuildRequest(int nodeRequestId, int configId, string[ /// /// Creates a build request. /// - private BuildRequest CreateBuildRequest(int nodeRequestId, int configId, string[] targets, NodeAffinity nodeAffinity, BuildRequest parentRequest) + private BuildRequest CreateBuildRequest(int nodeRequestId, int configId, string[] targets, NodeAffinity nodeAffinity, BuildRequest parentRequest, ProxyTargets proxyTargets = null) { + (targets == null ^ proxyTargets == null).ShouldBeTrue(); + HostServices hostServices = null; if (nodeAffinity != NodeAffinity.Any) @@ -739,8 +776,36 @@ private BuildRequest CreateBuildRequest(int nodeRequestId, int configId, string[ hostServices.SetNodeAffinity(String.Empty, nodeAffinity); } - BuildRequest request = new BuildRequest(1 /* submissionId */, nodeRequestId, configId, targets, hostServices, BuildEventContext.Invalid, parentRequest); - return request; + if (targets != null) + { + return new BuildRequest( + submissionId: 1, + nodeRequestId, + configId, + targets, + hostServices, + BuildEventContext.Invalid, + parentRequest); + } + + parentRequest.ShouldBeNull(); + return new BuildRequest( + submissionId: 1, + nodeRequestId, + configId, + proxyTargets, + hostServices); + } + + private BuildRequest CreateProxyBuildRequest(int nodeRequestId, int configId, ProxyTargets proxyTargets, BuildRequest parentRequest) + { + return CreateBuildRequest( + nodeRequestId, + configId, + null, + NodeAffinity.Any, + parentRequest, + proxyTargets); } /// @@ -778,5 +843,11 @@ private void MockPerformSchedulingActions(IEnumerable response MockPerformSchedulingActions(moreResponses, ref nodeId, ref inProcNodeExists); } } + + private void ReportDefaultParentRequestIsFinished() + { + var buildResult = new BuildResult(_defaultParentRequest); + _scheduler.ReportResult(_defaultParentRequest.NodeRequestId, buildResult); + } } } diff --git a/src/Build.UnitTests/BackEnd/SdkResolverService_Tests.cs b/src/Build.UnitTests/BackEnd/SdkResolverService_Tests.cs index c90d6e8a60c..cbfd97c5f83 100644 --- a/src/Build.UnitTests/BackEnd/SdkResolverService_Tests.cs +++ b/src/Build.UnitTests/BackEnd/SdkResolverService_Tests.cs @@ -85,16 +85,16 @@ public void AssertResolutionWarnsIfResolvedVersionIsDifferentFromReferencedVersi } [Fact] - public void AssertErrorLoggedWhenResolverThrows() + public void AssertResolverThrows() { SdkResolverService.Instance.InitializeForTests(new MockLoaderStrategy(includeErrorResolver: true)); SdkReference sdk = new SdkReference("1sdkName", "version1", "minimumVersion"); - var result = SdkResolverService.Instance.ResolveSdk(BuildEventContext.InvalidSubmissionId, sdk, _loggingContext, new MockElementLocation("file"), "sln", "projectPath", interactive: false, isRunningInVisualStudio: false); - - result.Path.ShouldBe("resolverpath1"); - _logger.Warnings.Select(i => i.Message).ShouldBe(new [] { "The SDK resolver \"MockSdkResolverThrows\" failed to run. EXMESSAGE" }); + // When an SDK resolver throws, the expander will catch it and stop the build. + SdkResolverException e = Should.Throw(() => SdkResolverService.Instance.ResolveSdk(BuildEventContext.InvalidSubmissionId, sdk, _loggingContext, new MockElementLocation("file"), "sln", "projectPath", interactive: false, isRunningInVisualStudio: false)); + e.Resolver.Name.ShouldBe("MockSdkResolverThrows"); + e.Sdk.Name.ShouldBe("1sdkName"); } [Fact] diff --git a/src/Build.UnitTests/BackEnd/TargetResult_Tests.cs b/src/Build.UnitTests/BackEnd/TargetResult_Tests.cs index 346da6846b9..67950cf5716 100644 --- a/src/Build.UnitTests/BackEnd/TargetResult_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TargetResult_Tests.cs @@ -89,8 +89,12 @@ public void TestTranslationNoException() { TaskItem item = new TaskItem("foo", "bar.proj"); item.SetMetadata("a", "b"); + var buildEventContext = new Framework.BuildEventContext(1, 2, 3, 4, 5, 6, 7); - TargetResult result = new TargetResult(new TaskItem[] { item }, BuildResultUtilities.GetStopWithErrorResult()); + TargetResult result = new TargetResult( + new TaskItem[] { item }, + BuildResultUtilities.GetStopWithErrorResult(), + buildEventContext); ((ITranslatable)result).Translate(TranslationHelpers.GetWriteTranslator()); TargetResult deserializedResult = TargetResult.FactoryForDeserialization(TranslationHelpers.GetReadTranslator()); @@ -98,6 +102,7 @@ public void TestTranslationNoException() Assert.Equal(result.ResultCode, deserializedResult.ResultCode); Assert.True(TranslationHelpers.CompareCollections(result.Items, deserializedResult.Items, TaskItemComparer.Instance)); Assert.True(TranslationHelpers.CompareExceptions(result.Exception, deserializedResult.Exception)); + Assert.Equal(result.OriginalBuildEventContext, deserializedResult.OriginalBuildEventContext); } /// diff --git a/src/Build.UnitTests/BackEnd/TaskBuilder_Tests.cs b/src/Build.UnitTests/BackEnd/TaskBuilder_Tests.cs index 2520bc41bb6..486352af3f2 100644 --- a/src/Build.UnitTests/BackEnd/TaskBuilder_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskBuilder_Tests.cs @@ -589,7 +589,7 @@ public void NullMetadataOnOutputItems_InlineTask() /// /// If an item being output from a task has null metadata, we shouldn't crash. /// - [Fact] + [Fact(Skip = "https://github.com/dotnet/msbuild/issues/6521")] [Trait("Category", "non-mono-tests")] public void NullMetadataOnLegacyOutputItems_InlineTask() { diff --git a/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs b/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs index 144f30bea1a..94c7c23f23d 100644 --- a/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs +++ b/src/Build.UnitTests/BackEnd/TaskHostConfiguration_Tests.cs @@ -55,6 +55,7 @@ public void ConstructorWithNullName() continueOnError: _continueOnErrorDefault, taskName: null, taskLocation: @"c:\my tasks\mytask.dll", + isTaskInputLoggingEnabled: false, taskParameters: null, globalParameters: null, warningsAsErrors: null, @@ -89,6 +90,7 @@ public void ConstructorWithEmptyName() continueOnError: _continueOnErrorDefault, taskName: String.Empty, taskLocation: @"c:\my tasks\mytask.dll", + isTaskInputLoggingEnabled: false, taskParameters: null, globalParameters: null, warningsAsErrors: null, @@ -123,6 +125,7 @@ public void ConstructorWithNullLocation() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: null, + isTaskInputLoggingEnabled: false, taskParameters: null, globalParameters: null, warningsAsErrors: null, @@ -159,6 +162,7 @@ public void ConstructorWithEmptyLocation() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: String.Empty, + isTaskInputLoggingEnabled: false, taskParameters: null, globalParameters: null, warningsAsErrors: null, @@ -193,6 +197,7 @@ public void TestValidConstructors() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: null, globalParameters: null, warningsAsErrors: null, @@ -217,6 +222,7 @@ public void TestValidConstructors() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: null, globalParameters: null, warningsAsErrors: null, @@ -242,6 +248,7 @@ public void TestValidConstructors() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: parameters, globalParameters: null, warningsAsErrors: null, @@ -272,6 +279,7 @@ public void TestValidConstructors() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: parameters2, globalParameters: null, warningsAsErrors: null, @@ -302,6 +310,7 @@ public void TestValidConstructors() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: parameters2, globalParameters: null, warningsAsErrors: WarningsAsErrors, @@ -339,6 +348,7 @@ public void TestTranslationWithNullDictionary() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: null, globalParameters: expectedGlobalProperties, warningsAsErrors: null, @@ -383,6 +393,7 @@ public void TestTranslationWithEmptyDictionary() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: new Dictionary(), globalParameters: new Dictionary(), warningsAsErrors: null, @@ -432,6 +443,7 @@ public void TestTranslationWithValueTypesInDictionary() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: parameters, globalParameters: null, warningsAsErrors: null, @@ -479,6 +491,7 @@ public void TestTranslationWithITaskItemInDictionary() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: parameters, globalParameters: null, warningsAsErrors: null, @@ -525,6 +538,7 @@ public void TestTranslationWithITaskItemArrayInDictionary() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: parameters, globalParameters: null, warningsAsErrors: null, @@ -578,6 +592,7 @@ public void TestTranslationWithWarningsAsErrors() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: null, globalParameters: null, warningsAsErrors: WarningsAsErrors, @@ -627,6 +642,7 @@ public void TestTranslationWithWarningsAsMessages() continueOnError: _continueOnErrorDefault, taskName: "TaskName", taskLocation: @"c:\MyTasks\MyTask.dll", + isTaskInputLoggingEnabled: false, taskParameters: null, globalParameters: null, warningsAsErrors: null, diff --git a/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs b/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs index 8382f53d95d..c6f59eea74f 100644 --- a/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs +++ b/src/Build.UnitTests/BuildEnvironmentHelper_Tests.cs @@ -57,51 +57,14 @@ public void FindBuildEnvironmentByEnvironmentVariable() /// If MSBUILD_EXE_PATH is explicitly set, we should detect it as a VisualStudio instance even in older scenarios /// (for example when the install path is under 15.0). /// - /// When true, run the test pointing to amd64 msbuild.exe. - [Theory] - [InlineData(true)] - [InlineData(false)] + [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "No Visual Studio install for netcore")] [PlatformSpecific(TestPlatforms.Windows)] - public void FindVisualStudioEnvironmentByEnvironmentVariable(bool is64BitMSbuild) + public void FindVisualStudioEnvironmentByEnvironmentVariable() { using (var env = new EmptyVSEnviroment()) { - var msbuildBinDirectory = is64BitMSbuild - ? Path.Combine(env.BuildDirectory, "amd64") - : env.BuildDirectory; - - var msBuildPath = Path.Combine(msbuildBinDirectory, MSBuildExeName); - var msBuildConfig = Path.Combine(msbuildBinDirectory, $"{MSBuildExeName}.config"); - var vsMSBuildDirectory = Path.Combine(env.TempFolderRoot, "MSBuild"); - - env.WithEnvironment("MSBUILD_EXE_PATH", msBuildPath); - BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(ReturnNull, ReturnNull, ReturnNull, env.VsInstanceMock, env.EnvironmentMock, () => false); - - BuildEnvironmentHelper.Instance.Mode.ShouldBe(BuildEnvironmentMode.VisualStudio); - BuildEnvironmentHelper.Instance.MSBuildExtensionsPath.ShouldBe(vsMSBuildDirectory); - BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory.ShouldBe(msbuildBinDirectory); - BuildEnvironmentHelper.Instance.CurrentMSBuildExePath.ShouldBe(msBuildPath); - BuildEnvironmentHelper.Instance.CurrentMSBuildConfigurationFile.ShouldBe(msBuildConfig); - // This code is not running inside the Visual Studio devenv.exe process - BuildEnvironmentHelper.Instance.RunningInVisualStudio.ShouldBeFalse(); - BuildEnvironmentHelper.Instance.VisualStudioInstallRootDirectory.ShouldBe(env.TempFolderRoot); - BuildEnvironmentHelper.Instance.RunningTests.ShouldBeFalse(); - } - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "No Visual Studio install for netcore")] - [PlatformSpecific(TestPlatforms.Windows)] - public void FindOlderVisualStudioEnvironmentByEnvironmentVariable(bool is64BitMSbuild) - { - using (var env = new EmptyVSEnviroment("15.0")) - { - var msbuildBinDirectory = is64BitMSbuild - ? Path.Combine(env.BuildDirectory, "amd64") - : env.BuildDirectory; + var msbuildBinDirectory = env.BuildDirectory; var msBuildPath = Path.Combine(msbuildBinDirectory, MSBuildExeName); var msBuildConfig = Path.Combine(msbuildBinDirectory, $"{MSBuildExeName}.config"); @@ -307,9 +270,9 @@ public void BuildEnvironmentDetectsVisualStudioByMSBuildProcessAmd64() [Theory] [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "No Visual Studio install for netcore")] [PlatformSpecific(TestPlatforms.Windows)] - [InlineData("16.0", true)] - [InlineData("16.3", true)] - [InlineData("15.0", false)] + [InlineData("17.0", true)] + [InlineData("17.3", true)] + [InlineData("16.0", false)] public void BuildEnvironmentDetectsVisualStudioFromSetupInstance(string visualStudioVersion, bool shouldBeValid) { using (var env = new EmptyVSEnviroment()) diff --git a/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs b/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs index f1b59b48e1f..1d06b86c43b 100644 --- a/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs +++ b/src/Build.UnitTests/BuildEventArgsSerialization_Tests.cs @@ -160,11 +160,15 @@ public void RoundtripTaskStartedEventArgs() projectFile: "C:\\project.proj", taskFile: "C:\\common.targets", taskName: "Csc"); + args.LineNumber = 42; + args.ColumnNumber = 999; Roundtrip(args, e => e.ProjectFile, e => e.TaskFile, - e => e.TaskName); + e => e.TaskName, + e => e.LineNumber.ToString(), + e => e.ColumnNumber.ToString()); } [Fact] @@ -185,8 +189,10 @@ public void RoundtripTaskFinishedEventArgs() e => e.ThreadId.ToString()); } - [Fact] - public void RoundtripBuildErrorEventArgs() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RoundtripBuildErrorEventArgs(bool useArguments) { var args = new BuildErrorEventArgs( "Subcategory", @@ -196,9 +202,11 @@ public void RoundtripBuildErrorEventArgs() 2, 3, 4, - "Message", + "Message with arguments: '{0}'", "Help", - "SenderName"); + "SenderName", + DateTime.Parse("9/1/2021 12:02:07 PM"), + useArguments ? new object[] { "argument0" } : null); Roundtrip(args, e => e.Code, @@ -209,11 +217,14 @@ public void RoundtripBuildErrorEventArgs() e => e.LineNumber.ToString(), e => e.Message, e => e.ProjectFile, - e => e.Subcategory); + e => e.Subcategory, + e => string.Join(", ", e.RawArguments ?? Array.Empty())); } - [Fact] - public void RoundtripBuildWarningEventArgs() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RoundtripBuildWarningEventArgs(bool useArguments) { var args = new BuildWarningEventArgs( "Subcategory", @@ -223,9 +234,11 @@ public void RoundtripBuildWarningEventArgs() 2, 3, 4, - "Message", + "Message with arguments: '{0}'", "Help", - "SenderName"); + "SenderName", + DateTime.Parse("9/1/2021 12:02:07 PM"), + useArguments ? new object[] { "argument0" } : null); Roundtrip(args, e => e.Code, @@ -236,11 +249,14 @@ public void RoundtripBuildWarningEventArgs() e => e.LineNumber.ToString(), e => e.Message, e => e.ProjectFile, - e => e.Subcategory); + e => e.Subcategory, + e => string.Join(", ", e.RawArguments ?? Array.Empty())); } - [Fact] - public void RoundtripBuildMessageEventArgs() + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RoundtripBuildMessageEventArgs(bool useArguments) { var args = new BuildMessageEventArgs( "Subcategory", @@ -254,7 +270,8 @@ public void RoundtripBuildMessageEventArgs() "Help", "SenderName", MessageImportance.High, - DateTime.Parse("12/12/2015 06:11:56 PM")); + DateTime.Parse("12/12/2015 06:11:56 PM"), + useArguments ? new object[] { "argument0" } : null); Roundtrip(args, e => e.Code, @@ -266,7 +283,8 @@ public void RoundtripBuildMessageEventArgs() e => e.Message, e => e.Importance.ToString(), e => e.ProjectFile, - e => e.Subcategory); + e => e.Subcategory, + e => string.Join(", ", e.RawArguments ?? Array.Empty())); } [Fact] @@ -327,11 +345,15 @@ public void RoundtripTaskParameterEventArgs() new TaskItemData("ItemSpec2", Enumerable.Range(1,3).ToDictionary(i => i.ToString(), i => i.ToString() + "value")) }; var args = new TaskParameterEventArgs(TaskParameterMessageKind.TaskOutput, "ItemName", items, true, DateTime.MinValue); + args.LineNumber = 265; + args.ColumnNumber = 6; Roundtrip(args, e => e.Kind.ToString(), e => e.ItemType, e => e.LogItemMetadata.ToString(), + e => e.LineNumber.ToString(), + e => e.ColumnNumber.ToString(), e => TranslationHelpers.GetItemsString(e.Items)); } @@ -443,20 +465,31 @@ public void RoundtripTargetSkippedEventArgs() ProjectFile = "foo.csproj", TargetName = "target", ParentTarget = "bar", - BuildReason = TargetBuiltReason.DependsOn + BuildReason = TargetBuiltReason.DependsOn, + SkipReason = TargetSkipReason.PreviouslyBuiltSuccessfully, + Condition = "$(condition) == true", + EvaluatedCondition = "true == true", + OriginalBuildEventContext = new BuildEventContext(1, 2, 3, 4, 5, 6, 7), + OriginallySucceeded = false, + TargetFile = "foo.csproj" }; Roundtrip(args, + e => e.BuildEventContext.ToString(), e => e.ParentTarget, e => e.Importance.ToString(), e => e.LineNumber.ToString(), e => e.ColumnNumber.ToString(), - e => e.LineNumber.ToString(), e => e.Message, e => e.ProjectFile, e => e.TargetFile, e => e.TargetName, - e => e.BuildReason.ToString()); + e => e.BuildReason.ToString(), + e => e.SkipReason.ToString(), + e => e.Condition, + e => e.EvaluatedCondition, + e => e.OriginalBuildEventContext.ToString(), + e => e.OriginallySucceeded.ToString()); } [Fact] diff --git a/src/Build.UnitTests/ConsoleLogger_Tests.cs b/src/Build.UnitTests/ConsoleLogger_Tests.cs index 31c8209404d..023d77e74b8 100644 --- a/src/Build.UnitTests/ConsoleLogger_Tests.cs +++ b/src/Build.UnitTests/ConsoleLogger_Tests.cs @@ -318,9 +318,9 @@ public void ErrorMessageWithMultiplePropertiesInMessage(bool includeEvaluationPr output.ShouldContain("source_of_error : error : Hello from project 2 [" + project.ProjectFile + ":: Number=2 TargetFramework=netcoreapp2.1]"); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/msbuild/issues/6518")] [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "Minimal path validation in Core allows expanding path containing quoted slashes.")] - [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "Minimal path validation in Mono allows expanding path containing quoted slashes.")] + [SkipOnMono("Minimal path validation in Mono allows expanding path containing quoted slashes.")] public void TestItemsWithUnexpandableMetadata() { SimulatedConsole sc = new SimulatedConsole(); @@ -338,7 +338,8 @@ public void TestItemsWithUnexpandableMetadata() ", logger); - sc.ToString().ShouldContain("\"a\\b\\%(Filename).c\""); + var text = sc.ToString(); + text.ShouldContain("\"a\\b\\%(Filename).c\""); } /// diff --git a/src/Build.UnitTests/Definition/ItemDataCollectionValue_Tests.cs b/src/Build.UnitTests/Definition/ItemDataCollectionValue_Tests.cs new file mode 100644 index 00000000000..b0699e3ab43 --- /dev/null +++ b/src/Build.UnitTests/Definition/ItemDataCollectionValue_Tests.cs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft. 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 Microsoft.Build.Evaluation; +using Shouldly; +using Xunit; + +namespace Microsoft.Build.UnitTests.OM.Definition +{ + /// + /// Tests the data type. + /// + public class ItemDataCollectionValue_Tests + { + private int[] MakeArray(ItemDataCollectionValue value) + { + List result = new List(); + foreach (int i in value) + { + result.Add(i); + } + return result.ToArray(); + } + + [Fact] + public void RepresentsSingleItem() + { + var value = new ItemDataCollectionValue(1); + value.IsEmpty.ShouldBeFalse(); + MakeArray(value).ShouldBe(new[] { 1 }); + } + + [Fact] + public void AddsSecondItem() + { + var value = new ItemDataCollectionValue(1); + value.Add(2); + value.IsEmpty.ShouldBeFalse(); + MakeArray(value).ShouldBe(new[] { 1, 2 }); + } + + [Fact] + public void DeletesSingleItem() + { + var value = new ItemDataCollectionValue(1); + value.Delete(1); + value.IsEmpty.ShouldBeTrue(); + MakeArray(value).ShouldBe(Array.Empty()); + } + + [Fact] + public void DeletesFirstItem() + { + var value = new ItemDataCollectionValue(1); + value.Add(2); + value.Delete(1); + value.IsEmpty.ShouldBeFalse(); + MakeArray(value).ShouldBe(new[] { 2 }); + } + + [Fact] + public void DeletesSecondItem() + { + var value = new ItemDataCollectionValue(1); + value.Add(2); + value.Delete(2); + value.IsEmpty.ShouldBeFalse(); + MakeArray(value).ShouldBe(new[] { 1 }); + } + + [Fact] + public void DeletesNonExistentItem() + { + var value = new ItemDataCollectionValue(1); + value.Add(2); + value.Delete(3); + value.IsEmpty.ShouldBeFalse(); + MakeArray(value).ShouldBe(new[] { 1, 2 }); + } + + [Fact] + public void ReplacesSingleItem() + { + var value = new ItemDataCollectionValue(1); + value.Replace(1, 11); + value.IsEmpty.ShouldBeFalse(); + MakeArray(value).ShouldBe(new[] { 11 }); + } + + [Fact] + public void ReplacesFirstItem() + { + var value = new ItemDataCollectionValue(1); + value.Add(2); + value.Replace(1, 11); + value.IsEmpty.ShouldBeFalse(); + MakeArray(value).ShouldBe(new[] { 11, 2 }); + } + + [Fact] + public void ReplacesSecondItem() + { + var value = new ItemDataCollectionValue(1); + value.Add(2); + value.Replace(2, 22); + value.IsEmpty.ShouldBeFalse(); + MakeArray(value).ShouldBe(new[] { 1, 22 }); + } + + [Fact] + public void ReplacesNonExistentItem() + { + var value = new ItemDataCollectionValue(1); + value.Add(2); + value.Replace(3, 33); + value.IsEmpty.ShouldBeFalse(); + MakeArray(value).ShouldBe(new[] { 1, 2 }); + } + } +} diff --git a/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs b/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs index 00dc1bb6f61..232d22c62dd 100644 --- a/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs +++ b/src/Build.UnitTests/Definition/ProjectEvaluationContext_Tests.cs @@ -119,14 +119,12 @@ public void PassedInFileSystemShouldBeReusedInSharedContext() {Path.Combine(_env.DefaultTestDirectory.Path, "2.file"), 1} }.OrderBy(kvp => kvp.Key)); - fileSystem.DirectoryEntryExistsCalls.ShouldBe(2); + fileSystem.FileOrDirectoryExistsCalls.ShouldBe(2); } [Fact] public void IsolatedContextShouldNotSupportBeingPassedAFileSystem() { - _env.DoNotLaunchDebugger(); - var fileSystem = new Helpers.LoggingFileSystem(); Should.Throw(() => EvaluationContext.Create(EvaluationContext.SharingPolicy.Isolated, fileSystem)); } diff --git a/src/Build.UnitTests/Definition/ToolsetRegistryReader_Tests.cs b/src/Build.UnitTests/Definition/ToolsetRegistryReader_Tests.cs index e365cb9265f..41e2d74ef72 100644 --- a/src/Build.UnitTests/Definition/ToolsetRegistryReader_Tests.cs +++ b/src/Build.UnitTests/Definition/ToolsetRegistryReader_Tests.cs @@ -36,7 +36,7 @@ public class ToolsetRegistryReader_Tests : IDisposable private const string testRegistryPath = @"msbuildUnitTests"; /// - /// Store the value of the "VisualStudioVersion" environment variable here so that + /// Store the value of the "VisualStudioVersion" environment variable here so that /// we can unset it for the duration of the test. /// private string _oldVisualStudioVersion; @@ -341,8 +341,8 @@ public void ReadRegistry_IgnoreSubToolsetSubKeys() } /// - /// Verifies that if a value is defined in both the base toolset and the - /// selected subtoolset, the subtoolset value overrides -- even if that + /// Verifies that if a value is defined in both the base toolset and the + /// selected subtoolset, the subtoolset value overrides -- even if that /// value is empty. /// [Fact] @@ -381,8 +381,8 @@ public void ReadRegistry_SubToolsetOverridesBaseToolsetEntries() } /// - /// Verifies that if a value is defined in both the base toolset and the - /// selected subtoolset, the subtoolset value overrides -- even if that + /// Verifies that if a value is defined in both the base toolset and the + /// selected subtoolset, the subtoolset value overrides -- even if that /// value is empty. /// [Fact] diff --git a/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs b/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs index ca20d9c66cb..8ac6ba52f14 100644 --- a/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs +++ b/src/Build.UnitTests/Evaluation/Evaluator_Tests.cs @@ -629,7 +629,7 @@ public void UsePropertyBeforeSet() $(baz) $(bar) Something - Something + Something @@ -675,7 +675,7 @@ public void UsePropertyBeforeSetDuplicates() $(baz) $(bar) $(baz) $(bar) Something - Something + Something @@ -1860,12 +1860,12 @@ public void AllEvaluatedItems() - + - + m2 @@ -1876,7 +1876,7 @@ public void AllEvaluatedItems() - + @@ -2587,6 +2587,8 @@ public void MSBuildVersion() Project project = new Project(xml); string msbuildVersionProperty = project.GetPropertyValue("MSBuildVersion"); + string msbuildFileVersionProperty = project.GetPropertyValue("MSBuildFileVersion"); + string msbuildSemanticVersionProperty = project.GetPropertyValue("MSBuildSemanticVersion"); Version.TryParse(msbuildVersionProperty, out Version msbuildVersionAsVersion).ShouldBeTrue(); @@ -2596,9 +2598,11 @@ public void MSBuildVersion() // Version parses missing elements into -1, and this property should be Major.Minor.Patch only msbuildVersionAsVersion.Revision.ShouldBe(-1); + msbuildFileVersionProperty.ShouldBe(ProjectCollection.Version.ToString()); ProjectCollection.Version.ToString().ShouldStartWith(msbuildVersionProperty, "ProjectCollection.Version should match the property MSBuildVersion, but can contain another version part"); + msbuildSemanticVersionProperty.ShouldBe(ProjectCollection.DisplayVersion); ProjectCollection.DisplayVersion.ShouldStartWith(msbuildVersionProperty, "DisplayVersion is semver2 while MSBuildVersion is Major.Minor.Build but should be a prefix match"); } @@ -4340,7 +4344,7 @@ public void ThrownInvalidProjectExceptionProperlyHandled() { string projectContents = ObjectModelHelpers.CleanupFileContents(@" - + "); @@ -4392,9 +4396,9 @@ public void ThrownInvalidProjectExceptionProperlyHandled() /// /// Tests that an import, target, or task with a condition that contains an error but is short-circuited does not fail the build. This can happen when you have a condition like: /// 'true' == 'false' AND '$([MSBuild]::GetDirectoryNameOfFileAbove($(NonExistentProperty), init.props))' != '' - /// + /// /// The first condition is false so the second condition is not evaluated. But in some cases we double evaluate the condition to log it. The second evaluation will fail because it evaluates the whole string. - /// + /// /// https://github.com/Microsoft/msbuild/issues/2259 /// [Theory] @@ -4765,6 +4769,23 @@ public void VerifyPropertyTrackingLoggingAll() }); } + [Fact] + public void VerifyGetTypeEvaluationBlocked() + { + string projectContents = ObjectModelHelpers.CleanupFileContents(@" + + + $(MSBuildRuntimeType.GetType()) + + "); + + ProjectCollection fakeProjectCollection = + GetProjectCollectionWithFakeToolset(null /* no global properties */); + + Should.Throw(() => + new Project(XmlReader.Create(new StringReader(projectContents)), null, "Fake", fakeProjectCollection)); + } + private void VerifyPropertyTrackingLoggingScenario(string envVarValue, Action loggerEvaluatorAction) { // The default is that only reassignments are logged. diff --git a/src/Build.UnitTests/Evaluation/Expander_Tests.cs b/src/Build.UnitTests/Evaluation/Expander_Tests.cs index 71d15858f3c..b58a72d0843 100644 --- a/src/Build.UnitTests/Evaluation/Expander_Tests.cs +++ b/src/Build.UnitTests/Evaluation/Expander_Tests.cs @@ -1547,76 +1547,6 @@ public void ExpandAllIntoStringTruncated() Assert.Equal(expected, expander.ExpandIntoStringAndUnescape(xmlattribute.Value, ExpanderOptions.ExpandAll | ExpanderOptions.Truncate, MockElementLocation.Instance)); } - /// - /// Exercises ExpandIntoStringAndUnescape and ExpanderOptions.Truncate - /// - [Fact] - public void ExpandAllIntoStringNotTruncated() - { - using (TestEnvironment env = TestEnvironment.Create()) - { - ChangeWaves.ResetStateForTests(); - env.SetEnvironmentVariable("MSBUILDDISABLEFEATURESFROMVERSION", ChangeWaves.Wave16_8.ToString()); - BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(); - ProjectInstance project = ProjectHelpers.CreateEmptyProjectInstance(); - var manySpaces = "".PadLeft(2000); - var pg = new PropertyDictionary(); - pg.Set(ProjectPropertyInstance.Create("ManySpacesProperty", manySpaces)); - var itemMetadataTable = new Dictionary(StringComparer.OrdinalIgnoreCase) - { - { "ManySpacesMetadata", manySpaces } - }; - var itemMetadata = new StringMetadataTable(itemMetadataTable); - var projectItemGroups = new ItemDictionary(); - var itemGroup = new List(); - StringBuilder longFileName = new StringBuilder(); - StringBuilder longMetadataName = new StringBuilder(); - for (int i = 0; i < 50; i++) - { - var item = new ProjectItemInstance(project, "ManyItems", $"ThisIsAFairlyLongFileName_{i}.bmp", project.FullPath); - item.SetMetadata("Foo", $"ThisIsAFairlyLongMetadataValue_{i}"); - longFileName.Append($"ThisIsAFairlyLongFileName_{i}.bmp" + (i == 49 ? string.Empty : ";")); - longMetadataName.Append($"ThisIsAFairlyLongMetadataValue_{i}" + (i == 49 ? string.Empty : ";")); - itemGroup.Add(item); - } - var lookup = new Lookup(projectItemGroups, pg); - lookup.EnterScope("x"); - lookup.PopulateWithItems("ManySpacesItem", new[] - { - new ProjectItemInstance (project, "ManySpacesItem", "Foo", project.FullPath), - new ProjectItemInstance (project, "ManySpacesItem", manySpaces, project.FullPath), - new ProjectItemInstance (project, "ManySpacesItem", "Bar", project.FullPath), - }); - lookup.PopulateWithItems("Exactly1024", new[] - { - new ProjectItemInstance (project, "Exactly1024", "".PadLeft(1024), project.FullPath), - new ProjectItemInstance (project, "Exactly1024", "Foo", project.FullPath), - }); - lookup.PopulateWithItems("ManyItems", itemGroup); - - Expander expander = new Expander(lookup, lookup, itemMetadata, FileSystems.Default); - - XmlAttribute xmlattribute = (new XmlDocument()).CreateAttribute("dummy"); - xmlattribute.Value = "'%(ManySpacesMetadata)' != '' and '$(ManySpacesProperty)' != '' and '@(ManySpacesItem)' != '' and '@(Exactly1024)' != '' and '@(ManyItems)' != '' and '@(ManyItems->'%(Foo)')' != '' and '@(ManyItems->'%(Nonexistent)')' != ''"; - - var expected = - $"'{"",2000}' != '' and " + - $"'{"",2000}' != '' and " + - $"'Foo;{"",2000};Bar' != '' and " + - $"'{"",1024};Foo' != '' and " + - $"'{longFileName}' != '' and " + - $"'{longMetadataName}' != '' and " + - "';;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;' != ''"; - var actual = expander.ExpandIntoStringAndUnescape(xmlattribute.Value, ExpanderOptions.ExpandAll | ExpanderOptions.Truncate, MockElementLocation.Instance); - // NOTE: semicolons in the last part are *weird* because they don't actually mean anything and you get logging like - // Target "Build" skipped, due to false condition; ( '@(I->'%(nonexistent)')' == '' ) was evaluated as ( ';' == '' ). - // but that goes back to MSBuild 4.something so I'm codifying it in this test. If you're here because you cleaned it up - // and want to fix the test my current opinion is that's fine. - actual.ShouldBe(expected); - ChangeWaves.ResetStateForTests(); - } - } - /// /// Exercises ExpandAllIntoString with a string that does not need expanding. /// In this case the expanded string should be reference identical to the passed in string. @@ -1998,7 +1928,7 @@ public void PropertyFunctionNullArgument() Expander expander = new Expander(pg, FileSystems.Default); - string result = expander.ExpandIntoStringLeaveEscaped("$([System.Convert]::ChangeType('null',$(SomeStuff.GetType())))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); + string result = expander.ExpandIntoStringLeaveEscaped("$([System.Convert]::ChangeType('null',$(SomeStuff.GetTypeCode())))", ExpanderOptions.ExpandProperties, MockElementLocation.Instance); Assert.Equal("null", result); } diff --git a/src/Build.UnitTests/Evaluation/ItemEvaluation_Tests.cs b/src/Build.UnitTests/Evaluation/ItemEvaluation_Tests.cs index 73ba89b0e9b..2aa10111eeb 100644 --- a/src/Build.UnitTests/Evaluation/ItemEvaluation_Tests.cs +++ b/src/Build.UnitTests/Evaluation/ItemEvaluation_Tests.cs @@ -110,7 +110,7 @@ public void RemoveShouldPreserveIntermediaryReferences(string content) {"m1", "m1_contents"}, {"m2", "m2_contents"} }; - + var itemsForI = items.Where(i => i.ItemType == "i").ToList(); ObjectModelHelpers.AssertItems(new[] { "a", "b", "c" }, itemsForI, expectedMetadata); @@ -508,7 +508,7 @@ public void MultipleInterItemDependenciesOnSameItemOperation() new Dictionary { {"m", "i2"} - }, + }, i1BaseMetadata, i1BaseMetadata }; @@ -528,7 +528,7 @@ public void MultipleInterItemDependenciesOnSameItemOperation() public void LongIncludeChain() { const int INCLUDE_COUNT = 10000; - + // This was about the minimum count needed to repro a StackOverflowException //const int INCLUDE_COUNT = 4000; @@ -605,7 +605,7 @@ public void LazyWildcardExpansionDoesNotEvaluateWildCardsIfNotReferenced() } Assert.Equal(expectedItems, project.GetConcatenatedItemsOfType("i2")); - + var fullPathItems = project.GetConcatenatedItemsOfType("FullPath"); Assert.Contains("a.cs", fullPathItems); Assert.Contains("b.cs", fullPathItems); diff --git a/src/Build.UnitTests/Evaluation/SimpleProjectRootElementCache_Tests.cs b/src/Build.UnitTests/Evaluation/SimpleProjectRootElementCache_Tests.cs index 92794939793..f38192ebbb3 100644 --- a/src/Build.UnitTests/Evaluation/SimpleProjectRootElementCache_Tests.cs +++ b/src/Build.UnitTests/Evaluation/SimpleProjectRootElementCache_Tests.cs @@ -60,7 +60,6 @@ public void Get_GivenOpenFuncWhichAddsRootElement_ReturnsRootElement() ProjectRootElement rootElementToCache = ProjectRootElement.Create(projectFileToCache); ProjectRootElement OpenFunc(string pathArg, ProjectRootElementCacheBase cacheArg) { - cacheArg.AddEntry(rootElementToCache); return rootElementToCache; } @@ -79,7 +78,6 @@ public void Get_GivenOpenFuncWhichAddsRootElementWithDifferentCasing_ReturnsRoot ProjectRootElement rootElementToCache = ProjectRootElement.Create(projectFileToCache); ProjectRootElement OpenFunc(string pathArg, ProjectRootElementCacheBase cacheArg) { - cacheArg.AddEntry(rootElementToCache); return rootElementToCache; } @@ -112,7 +110,6 @@ public void Get_GivenOpenFuncWhichReturnsIncorrectProject_ThrowsInternalErrorExc ProjectRootElement rootElementToCache = ProjectRootElement.Create(projectFileToCache); ProjectRootElement OpenFunc(string pathArg, ProjectRootElementCacheBase cacheArg) { - cacheArg.AddEntry(rootElementToCache); return rootElementToCache; } @@ -123,21 +120,5 @@ ProjectRootElement OpenFunc(string pathArg, ProjectRootElementCacheBase cacheArg cache.Get(projectFile, OpenFunc, false, null); }); } - - [Fact] - public void Get_GivenOpenFuncWhichDoesNotAddToCache_ThrowsInternalErrorException() - { - string projectFile = NativeMethodsShared.IsUnixLike ? "/foo" : "c:\\foo"; - string openFuncPath = NativeMethodsShared.IsUnixLike ? "/foo" : "c:\\foo"; - ProjectRootElement openFuncElement = ProjectRootElement.Create(openFuncPath); - ProjectRootElement OpenFunc(string pathArg, ProjectRootElementCacheBase cacheArg) => openFuncElement; - - var cache = new SimpleProjectRootElementCache(); - - Should.Throw(() => - { - cache.Get(projectFile, OpenFunc, false, null); - }); - } } } diff --git a/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs b/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs index c4f8ab84bea..d357c472750 100644 --- a/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs +++ b/src/Build.UnitTests/Globbing/MSBuildGlob_Tests.cs @@ -326,20 +326,39 @@ public void GlobMatchingShouldWorkWithComplexRelativeLiterals() [InlineData( @"a/b\c", @"d/e\f/**\a.cs", - @"d\e/f\g/h\i/a.cs")] + @"d\e/f\g/h\i/a.cs", + @"d\e/f\", @"g/h\i/", @"a.cs")] [InlineData( @"a/b\c", @"d/e\f/*b*\*.cs", - @"d\e/f\abc/a.cs")] + @"d\e/f\abc/a.cs", + @"d\e/f\", @"abc/", @"a.cs")] [InlineData( @"a/b/\c", @"d/e\/*b*/\*.cs", - @"d\e\\abc/\a.cs")] - public void GlobMatchingIgnoresSlashOrientationAndRepetitions(string globRoot, string fileSpec, string stringToMatch) + @"d\e\\abc/\a.cs", + @"d\e\\", @"abc\\", @"a.cs")] + public void GlobMatchingIgnoresSlashOrientationAndRepetitions(string globRoot, string fileSpec, string stringToMatch, + string fixedDirectoryPart, string wildcardDirectoryPart, string filenamePart) { var glob = MSBuildGlob.Parse(globRoot, fileSpec); Assert.True(glob.IsMatch(stringToMatch)); + + MSBuildGlob.MatchInfoResult result = glob.MatchInfo(stringToMatch); + Assert.True(result.IsMatch); + + string NormalizeSlashes(string path) + { + string normalizedPath = path.Replace(Path.DirectorySeparatorChar == '/' ? '\\' : '/', Path.DirectorySeparatorChar); + return NativeMethodsShared.IsWindows ? normalizedPath.Replace("\\\\", "\\") : normalizedPath; + } + + var rootedFixedDirectoryPart = Path.Combine(FileUtilities.NormalizePath(globRoot), fixedDirectoryPart); + + Assert.Equal(FileUtilities.GetFullPathNoThrow(rootedFixedDirectoryPart), result.FixedDirectoryPartMatchGroup); + Assert.Equal(NormalizeSlashes(wildcardDirectoryPart), result.WildcardDirectoryPartMatchGroup); + Assert.Equal(NormalizeSlashes(filenamePart), result.FilenamePartMatchGroup); } } } diff --git a/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs b/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs index c1f0161e91d..bed09d043ec 100644 --- a/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs +++ b/src/Build.UnitTests/Graph/GraphLoadedFromSolution_tests.cs @@ -33,8 +33,6 @@ public GraphLoadedFromSolutionTests(ITestOutputHelper output) [InlineData("1.sln", "2.proj")] public void ASolutionShouldBeTheSingleEntryPoint(params string[] files) { - _env.DoNotLaunchDebugger(); - for (var i = 0; i < files.Length; i++) { files[i] = _env.CreateFile(files[i], string.Empty).Path; @@ -52,8 +50,6 @@ public void ASolutionShouldBeTheSingleEntryPoint(params string[] files) [Fact] public void GraphConstructionFailsOnNonExistentSolution() { - _env.DoNotLaunchDebugger(); - var exception = Should.Throw( () => { @@ -80,8 +76,6 @@ public void StaticGraphShouldNotSupportNestedSolutions() defaultTargets: null, extraContent: referenceToSolution); - _env.DoNotLaunchDebugger(); - var exception = Should.Throw( () => { @@ -621,8 +615,6 @@ IEnumerable GetIncomingEdgeItemsToNode(ProjectGraphNode nod [Fact] public void GraphConstructionShouldThrowOnMissingSolutionDependencies() { - _env.DoNotLaunchDebugger(); - var solutionContents = SolutionFileBuilder.FromGraphEdges( _env, new Dictionary {{1, null}, {2, null}}, diff --git a/src/Build.UnitTests/Graph/ProjectGraph_Tests.cs b/src/Build.UnitTests/Graph/ProjectGraph_Tests.cs index 548a25b3858..28af920a861 100644 --- a/src/Build.UnitTests/Graph/ProjectGraph_Tests.cs +++ b/src/Build.UnitTests/Graph/ProjectGraph_Tests.cs @@ -106,7 +106,6 @@ public void ConstructWithSingleNodeWithProjectInstanceFactory() [Fact] public void ProjectGraphNodeConstructorNoNullArguments() { - _env.DoNotLaunchDebugger(); Assert.Throws(() => new ProjectGraphNode(null)); } @@ -1477,12 +1476,21 @@ public void DotNotationShouldRepresentGraph(Dictionary edges) var graph = Helpers.CreateProjectGraph( _env, edges, - new Dictionary {{"a", "b"}}); + globalProperties: new Dictionary {{"a", "b"}}, + createProjectFile: (env, projectId, references, _, _, _) => Helpers.CreateProjectFile( + env, + projectId, + references, + projectReferenceTargets: new Dictionary + { + {"Build", new[] {$"TargetFrom{projectId}", "Build"}} + })); + var targetsPerNode = graph.GetTargetLists(new []{ "Build" }); Func nodeIdProvider = GetProjectFileName; - var dot = graph.ToDot(nodeIdProvider); + var dot = graph.ToDot(nodeIdProvider, targetsPerNode); var edgeCount = 0; @@ -1490,9 +1498,12 @@ public void DotNotationShouldRepresentGraph(Dictionary edges) { var nodeId = nodeIdProvider(node); + var targets = string.Join(".*", targetsPerNode[node]); + targets.ShouldNotBeNullOrEmpty(); + foreach (var globalProperty in node.ProjectInstance.GlobalProperties) { - dot.ShouldMatch($@"{nodeId}\s*\[.*{globalProperty.Key}.*{globalProperty.Value}.*\]"); + dot.ShouldMatch($@"{nodeId}\s*\[.*{targets}.*{globalProperty.Key}.*{globalProperty.Value}.*\]"); } foreach (var reference in node.ProjectReferences) diff --git a/src/Build.UnitTests/Instance/HostServices_Tests.cs b/src/Build.UnitTests/Instance/HostServices_Tests.cs index 0bd541928cd..318d56ef351 100644 --- a/src/Build.UnitTests/Instance/HostServices_Tests.cs +++ b/src/Build.UnitTests/Instance/HostServices_Tests.cs @@ -245,7 +245,7 @@ public void TestContradictoryAffinityCausesException_Any() /// Test which ensures that setting an Any affinity for a project with a remote host object does not throws. /// [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "disable com tests on mono")] + [SkipOnMono("disable com tests on mono")] public void TestNoContradictoryRemoteHostObjectAffinity() { HostServices hostServices = new HostServices(); @@ -301,7 +301,7 @@ public void TestNonContraditcoryHostObjectAllowed_Any() /// Test which ensures the remote host object cannot affect a project which has the Any affinity specifically set. /// [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "disable com tests on mono")] + [SkipOnMono("disable com tests on mono")] public void TestRegisterRemoteHostObjectNoAffect_Any2() { HostServices hostServices = new HostServices(); @@ -341,7 +341,7 @@ public void TestNonContraditcoryHostObjectAllowed_InProc() /// Test which ensures the affinity for a project can be changed once the in process host object is registered /// [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "disable com tests on mono")] + [SkipOnMono("disable com tests on mono")] public void TestAffinityChangeAfterRegisterInprocessHostObject() { HostServices hostServices = new HostServices(); @@ -452,7 +452,7 @@ public void UnloadedProjectDiscardsHostServices() /// Tests that register overrides existing reigsted remote host object. /// [Fact] - [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "disable com tests on mono")] + [SkipOnMono("disable com tests on mono")] public void TestRegisterOverrideExistingRegisted() { var hostServices = new HostServices(); diff --git a/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj b/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj index 52f699668fb..dc3a40ef3ce 100644 --- a/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj +++ b/src/Build.UnitTests/Microsoft.Build.Engine.UnitTests.csproj @@ -18,6 +18,7 @@ + all @@ -38,12 +39,12 @@ TargetFramework=$(FullFrameworkTFM) TargetFramework=netstandard2.0 - + TargetFramework=$(FullFrameworkTFM) TargetFramework=$(FullFrameworkTFM) - TargetFramework=net5.0 + TargetFramework=net6.0 diff --git a/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs b/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs index 8ccde2767a9..c994edab0ff 100644 --- a/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs +++ b/src/Build.UnitTests/ProjectCache/ProjectCacheTests.cs @@ -7,9 +7,11 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Microsoft.Build.Construction; using Microsoft.Build.Execution; using Microsoft.Build.Experimental.ProjectCache; using Microsoft.Build.Framework; @@ -62,6 +64,7 @@ public void Dispose() public class GraphCacheResponse { + private readonly IDictionary? _extraContentPerProjectNumber; public const string CacheHitByProxy = nameof(CacheHitByProxy); public const string CacheHitByTargetResult = nameof(CacheHitByTargetResult); @@ -95,8 +98,9 @@ public class GraphCacheResponse public Dictionary NonCacheMissResults { get; } - public GraphCacheResponse(Dictionary graphEdges, Dictionary? nonCacheMissResults = null) + public GraphCacheResponse(Dictionary graphEdges, Dictionary? nonCacheMissResults = null, IDictionary? extraContentPerProjectNumber = null) { + _extraContentPerProjectNumber = extraContentPerProjectNumber; GraphEdges = graphEdges; NonCacheMissResults = nonCacheMissResults ?? new Dictionary(); } @@ -106,7 +110,7 @@ public ProjectGraph CreateGraph(TestEnvironment env) return Helpers.CreateProjectGraph( env, GraphEdges, - null, + _extraContentPerProjectNumber, P2PTargets); } @@ -191,6 +195,31 @@ char Chr(int projectNumber) } } + public class DelegatingMockCache : ProjectCachePluginBase + { + private readonly Func> _getCacheResultDelegate; + + public DelegatingMockCache(Func> getCacheResultDelegate) + { + _getCacheResultDelegate = getCacheResultDelegate; + } + + public override Task BeginBuildAsync(CacheContext context, PluginLoggerBase logger, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + public override async Task GetCacheResultAsync(BuildRequestData buildRequest, PluginLoggerBase logger, CancellationToken cancellationToken) + { + return await _getCacheResultDelegate(buildRequest, logger, cancellationToken); + } + + public override Task EndBuildAsync(PluginLoggerBase logger, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } + [Flags] public enum ErrorLocations { @@ -206,17 +235,50 @@ public enum ErrorKind LoggedError } + public class ConfigurableMockCache : ProjectCachePluginBase + { + public Func? BeginBuildImplementation { get; set; } + public Func>? GetCacheResultImplementation { get; set; } + + public override Task BeginBuildAsync(CacheContext context, PluginLoggerBase logger, CancellationToken cancellationToken) + { + return BeginBuildImplementation != null + ? BeginBuildImplementation(context, logger, cancellationToken) + : Task.CompletedTask; + } + + public override Task GetCacheResultAsync( + BuildRequestData buildRequest, + PluginLoggerBase logger, + CancellationToken cancellationToken) + { + return GetCacheResultImplementation != null + ? GetCacheResultImplementation(buildRequest, logger, cancellationToken) + : Task.FromResult(CacheResult.IndicateNonCacheHit(CacheResultType.CacheNotApplicable)); + } + + public override Task EndBuildAsync(PluginLoggerBase logger, CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + } + public class InstanceMockCache : ProjectCachePluginBase { private readonly GraphCacheResponse? _testData; - public ConcurrentQueue Requests { get; } = new ConcurrentQueue(); + private readonly TimeSpan? _projectQuerySleepTime; + public ConcurrentQueue Requests { get; } = new(); public bool BeginBuildCalled { get; set; } public bool EndBuildCalled { get; set; } - public InstanceMockCache(GraphCacheResponse? testData = null) + private int _nextId; + public ConcurrentQueue QueryStartStops = new(); + + public InstanceMockCache(GraphCacheResponse? testData = null, TimeSpan? projectQuerySleepTime = null) { _testData = testData; + _projectQuerySleepTime = projectQuerySleepTime; } public override Task BeginBuildAsync(CacheContext context, PluginLoggerBase logger, CancellationToken cancellationToken) @@ -228,18 +290,29 @@ public override Task BeginBuildAsync(CacheContext context, PluginLoggerBase logg return Task.CompletedTask; } - public override Task GetCacheResultAsync( + public override async Task GetCacheResultAsync( BuildRequestData buildRequest, PluginLoggerBase logger, CancellationToken cancellationToken) { + var queryId = Interlocked.Increment(ref _nextId); + Requests.Enqueue(buildRequest); + QueryStartStops.Enqueue(queryId); + logger.LogMessage($"MockCache: GetCacheResultAsync for {buildRequest.ProjectFullPath}", MessageImportance.High); - return - Task.FromResult( - _testData?.GetExpectedCacheResultForProjectNumber(GetProjectNumber(buildRequest.ProjectFullPath)) - ?? CacheResult.IndicateNonCacheHit(CacheResultType.CacheMiss)); + buildRequest.ProjectInstance.ShouldNotBeNull("The cache plugin expects evaluated projects."); + + if (_projectQuerySleepTime is not null) + { + await Task.Delay(_projectQuerySleepTime.Value); + } + + QueryStartStops.Enqueue(queryId); + + return _testData?.GetExpectedCacheResultForProjectNumber(GetProjectNumber(buildRequest.ProjectFullPath)) + ?? CacheResult.IndicateNonCacheHit(CacheResultType.CacheMiss); } public override Task EndBuildAsync(PluginLoggerBase logger, CancellationToken cancellationToken) @@ -389,16 +462,20 @@ public void ProjectCacheByBuildParametersAndGraphBuildWorks(GraphCacheResponse t var graph = testData.CreateGraph(_env); var mockCache = new InstanceMockCache(testData); - buildParameters.ProjectCacheDescriptor = ProjectCacheDescriptor.FromInstance( - mockCache, - null, - graph); + // Reset the environment variables stored in the build params to take into account TestEnvironmentChanges. + buildParameters = new BuildParameters(buildParameters, resetEnvironment: true) + { + ProjectCacheDescriptor = ProjectCacheDescriptor.FromInstance( + mockCache, + null, + graph) + }; using var buildSession = new Helpers.BuildManagerSession(_env, buildParameters); var graphResult = buildSession.BuildGraph(graph); - graphResult.OverallResult.ShouldBe(BuildResultCode.Success); + graphResult.ShouldHaveSucceeded(); buildSession.Dispose(); @@ -414,18 +491,26 @@ public void ProjectCacheByBuildParametersAndBottomUpBuildWorks(GraphCacheRespons var graph = testData.CreateGraph(_env); var mockCache = new InstanceMockCache(testData); - buildParameters.ProjectCacheDescriptor = ProjectCacheDescriptor.FromInstance( + var projectCacheDescriptor = ProjectCacheDescriptor.FromInstance( mockCache, null, graph); + // Reset the environment variables stored in the build params to take into account TestEnvironmentChanges. + buildParameters = new BuildParameters(buildParameters, resetEnvironment: true) + { + ProjectCacheDescriptor = projectCacheDescriptor + }; + + using var buildSession = new Helpers.BuildManagerSession(_env, buildParameters); var nodesToBuildResults = new Dictionary(); foreach (var node in graph.ProjectNodesTopologicallySorted) { var buildResult = buildSession.BuildProjectFile(node.ProjectInstance.FullPath); - buildResult.OverallResult.ShouldBe(BuildResultCode.Success); + + buildResult.ShouldHaveSucceeded(); nodesToBuildResults[node] = buildResult; } @@ -439,8 +524,86 @@ public void ProjectCacheByBuildParametersAndBottomUpBuildWorks(GraphCacheRespons [Theory] [MemberData(nameof(SuccessfulGraphsWithBuildParameters))] - public void ProjectCacheByVSWorkaroundWorks(GraphCacheResponse testData, BuildParameters buildParameters) + public void ProjectCacheByVsWorkaroundWorks(GraphCacheResponse testData, BuildParameters buildParameters) + { + ProjectGraph? graph = null; + + var (logger, nodesToBuildResults) = BuildGraphByVsWorkaround( + () => + { + graph = testData.CreateGraph(_env); + return graph; + }, + buildParameters); + + graph.ShouldNotBeNull(); + + AssertCacheBuild(graph!, testData, null, logger, nodesToBuildResults); + } + + [Fact] + public void ProjectCacheByVsWorkaroundIgnoresSlnDisabledProjects() + { + var testData = new GraphCacheResponse( + new Dictionary + { + {1, new[] {2}} + }, + extraContentPerProjectNumber: new Dictionary() + { + {1, " false "} + }); + + ProjectGraph? graph = null; + + var (logger, nodesToBuildResults) = BuildGraphByVsWorkaround( + graphProducer: () => + { + graph = testData.CreateGraph(_env); + return graph; + }, + assertBuildResults: false + ); + + graph.ShouldNotBeNull(); + + logger.FullLog.ShouldNotContain($"EntryPoint: {graph!.GraphRoots.First().ProjectInstance.FullPath}"); + logger.FullLog.ShouldContain($"EntryPoint: {graph.GraphRoots.First().ProjectReferences.First().ProjectInstance.FullPath}"); + } + + [Fact] + public void ProjectCacheByVsWorkaroundShouldNotSupportSolutionOnlyDependencies() { + var testData = new GraphCacheResponse( + new Dictionary + { + {1, Array.Empty()} + }, + extraContentPerProjectNumber: new Dictionary() + { + {1, $" {Guid.NewGuid()} "} + }); + + var (logger, nodeResults) = BuildGraphByVsWorkaround( + graphProducer: () => testData.CreateGraph(_env), + assertBuildResults: false); + + nodeResults.ShouldHaveSingleItem(); + + var buildResult = nodeResults.First().Value; + buildResult.OverallResult.ShouldBe(BuildResultCode.Failure); + buildResult.Exception.Message.ShouldContain("Project cache service does not support solution only dependencies when running under Visual Studio."); + } + + private (MockLogger logger, Dictionary nodesToBuildResults) BuildGraphByVsWorkaround( + Func graphProducer, + BuildParameters? buildParameters = null, + bool assertBuildResults = true + ) + { + var nodesToBuildResults = new Dictionary(); + MockLogger? logger; + var currentBuildEnvironment = BuildEnvironmentHelper.Instance; try @@ -450,32 +613,174 @@ public void ProjectCacheByVSWorkaroundWorks(GraphCacheResponse testData, BuildPa currentBuildEnvironment.Mode, currentBuildEnvironment.CurrentMSBuildExePath, currentBuildEnvironment.RunningTests, - true, - currentBuildEnvironment.VisualStudioInstallRootDirectory)); + runningInVisualStudio: true, + visualStudioPath: currentBuildEnvironment.VisualStudioInstallRootDirectory)); + + // Reset the environment variables stored in the build params to take into account TestEnvironmentChanges. + buildParameters = buildParameters is null + ? new BuildParameters() + : new BuildParameters(buildParameters, resetEnvironment: true); BuildManager.ProjectCacheItems.ShouldBeEmpty(); - var graph = testData.CreateGraph(_env); + var graph = graphProducer.Invoke(); BuildManager.ProjectCacheItems.ShouldHaveSingleItem(); + var projectPaths = graph.ProjectNodes.Select(n => n.ProjectInstance.FullPath).ToArray(); + + // VS sets this global property on every project it builds. + var solutionConfigurationGlobalProperty = CreateSolutionConfigurationProperty(graph.ProjectNodes); + using var buildSession = new Helpers.BuildManagerSession(_env, buildParameters); - var nodesToBuildResults = new Dictionary(); foreach (var node in graph.ProjectNodesTopologicallySorted) { var buildResult = buildSession.BuildProjectFile( node.ProjectInstance.FullPath, globalProperties: - new Dictionary {{"SolutionPath", graph.GraphRoots.First().ProjectInstance.FullPath}}); - buildResult.OverallResult.ShouldBe(BuildResultCode.Success); + new Dictionary + { + { SolutionProjectGenerator.SolutionPathPropertyName, graph.GraphRoots.First().ProjectInstance.FullPath }, + { SolutionProjectGenerator.CurrentSolutionConfigurationContents, solutionConfigurationGlobalProperty }, + { PropertyNames.InnerBuildProperty, "TheInnerBuildProperty"}, + { "TheInnerBuildProperty", "FooBar"}, + }); + + if (assertBuildResults) + { + buildResult.ShouldHaveSucceeded(); + } nodesToBuildResults[node] = buildResult; } - buildSession.Logger.FullLog.ShouldContain("Graph entrypoint based"); + logger = buildSession.Logger; + + if (assertBuildResults) + { + logger.FullLog.ShouldContain("Visual Studio Workaround based"); + logger.FullLog.ShouldContain("Running project cache with Visual Studio workaround"); + + foreach (var node in graph.ProjectNodes) + { + var projectPath = node.ProjectInstance.FullPath; + var projectName = Path.GetFileNameWithoutExtension(projectPath); + + // Ensure MSBuild passes config / platform information set by VS. + logger.FullLog.ShouldContain($"EntryPoint: {projectPath}"); + logger.FullLog.ShouldContain($"Configuration:{projectName}Debug"); + logger.FullLog.ShouldContain($"Platform:{projectName}x64"); + + // Ensure MSBuild removes the inner build property if present. + logger.FullLog.ShouldContain($"{PropertyNames.InnerBuildProperty}:TheInnerBuildProperty"); + logger.FullLog.ShouldNotContain("TheInnerBuildProperty:FooBar"); + } + } + } + finally + { + BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(currentBuildEnvironment); + BuildManager.ProjectCacheItems.Clear(); + } + + return (logger, nodesToBuildResults); + } + + private static string CreateSolutionConfigurationProperty(IReadOnlyCollection projectNodes) + { + var sb = new StringBuilder(); + + sb.AppendLine(""); + + foreach (var node in projectNodes) + { + var projectPath = node.ProjectInstance.FullPath; + var projectName = Path.GetFileNameWithoutExtension(projectPath); + + var buildProjectInSolutionValue = node.ProjectInstance.GetPropertyValue("BuildProjectInSolution"); + var buildProjectInSolutionAttribute = string.IsNullOrWhiteSpace(buildProjectInSolutionValue) + ? string.Empty + : $"BuildProjectInSolution=\"{buildProjectInSolutionValue}\""; + + var projectDependencyValue = node.ProjectInstance.GetPropertyValue("ProjectDependency"); + var projectDependencyElement = string.IsNullOrWhiteSpace(projectDependencyValue) + ? string.Empty + : $""; + + sb.AppendLine($"{projectName}Debug|{projectName}x64{projectDependencyElement}"); + } + + sb.AppendLine(""); + + return sb.ToString(); + } + + [Fact] + public void DesignTimeBuildsDuringVsWorkaroundShouldDisableTheCache() + { + var currentBuildEnvironment = BuildEnvironmentHelper.Instance; + + var designTimeBuildProperty = $" <{DesignTimeProperties.DesignTimeBuild}>true "; + + // Use a few references to stress test the design time build workaround logic. + var referenceNumbers = Enumerable.Range(2, NativeMethodsShared.GetLogicalCoreCount()).ToArray(); + + var testData = new GraphCacheResponse( + graphEdges: new Dictionary + { + {1, referenceNumbers} + }, + nonCacheMissResults: null, + extraContentPerProjectNumber: referenceNumbers.ToDictionary(r => r, _ => designTimeBuildProperty)); + + try + { + BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly( + new BuildEnvironment( + currentBuildEnvironment.Mode, + currentBuildEnvironment.CurrentMSBuildExePath, + currentBuildEnvironment.RunningTests, + runningInVisualStudio: true, + visualStudioPath: currentBuildEnvironment.VisualStudioInstallRootDirectory)); + + var graph = testData.CreateGraph(_env); + + var rootNode = graph.GraphRoots.First(); + var globalProperties = new Dictionary { { "SolutionPath", rootNode.ProjectInstance.FullPath } }; + + using var buildSession = new Helpers.BuildManagerSession(_env); + + // Build references in parallel. + var referenceBuildTasks = rootNode.ProjectReferences.Select( + r => buildSession.BuildProjectFileAsync( + r.ProjectInstance.FullPath, + globalProperties: globalProperties)); + + foreach (var task in referenceBuildTasks) + { + var buildResult = task.Result; + buildResult.ShouldHaveSucceeded(); + } + + buildSession + .BuildProjectFile(rootNode.ProjectInstance.FullPath, globalProperties: globalProperties) + .ShouldHaveSucceeded(); + + buildSession.Dispose(); + + buildSession.Logger.FullLog.ShouldContain("Visual Studio Workaround based"); - AssertCacheBuild(graph, testData, null, buildSession.Logger, nodesToBuildResults); + // Design time builds should not initialize the plugin. + buildSession.Logger.FullLog.ShouldNotContain("Running project cache with Visual Studio workaround"); + + // Cache doesn't get initialized and queried. + buildSession.Logger.FullLog.ShouldNotContain("BeginBuildAsync"); + buildSession.Logger.FullLog.ShouldNotContain("GetCacheResultAsync for"); + buildSession.Logger.FullLog.ShouldNotContain("Querying project cache for project"); + + // Cache does get disposed. + StringShouldContainSubstring(buildSession.Logger.FullLog, "EndBuildAsync", 1); } finally { @@ -484,6 +789,57 @@ public void ProjectCacheByVSWorkaroundWorks(GraphCacheResponse testData, BuildPa } } + [Theory] + [InlineData(true)] + [InlineData(false)] + public void RunningProxyBuildsOnOutOfProcNodesShouldIssueWarning(bool disableInprocNodeViaEnvironmentVariable) + { + var testData = new GraphCacheResponse( + new Dictionary + { + {1, new[] {2}} + }, + new Dictionary + { + {1, GraphCacheResponse.SuccessfulProxyTargetResult()}, + {2, GraphCacheResponse.SuccessfulProxyTargetResult()} + }); + + var graph = testData.CreateGraph(_env); + var mockCache = new InstanceMockCache(testData); + + var buildParameters = new BuildParameters + { + MaxNodeCount = Environment.ProcessorCount, + ProjectCacheDescriptor = ProjectCacheDescriptor.FromInstance( + mockCache, + null, + graph) + }; + + if (disableInprocNodeViaEnvironmentVariable) + { + _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1"); + } + else + { + buildParameters.DisableInProcNode = true; + } + + using var buildSession = new Helpers.BuildManagerSession(_env, buildParameters); + + var graphResult = buildSession.BuildGraph(graph); + + graphResult.ShouldHaveSucceeded(); + + buildSession.Dispose(); + + buildSession.Logger.FullLog.ShouldContain("Static graph based"); + + buildSession.Logger.AssertMessageCount("MSB4274", 1); + + } + private void AssertCacheBuild( ProjectGraph graph, GraphCacheResponse testData, @@ -619,7 +975,7 @@ public void CacheShouldNotGetQueriedForNestedBuildRequests(BuildParameters build var buildResult = buildSession.BuildProjectFile(project1.Path); - buildResult.OverallResult.ShouldBe(BuildResultCode.Success); + buildResult.ShouldHaveSucceeded(); buildSession.Logger.ProjectStartedEvents.Count.ShouldBe(2); @@ -651,9 +1007,9 @@ public void CacheViaBuildParametersCanDiscoverAndLoadPluginFromAssembly() var graphResult = buildSession.BuildGraph(graph); - graphResult.OverallResult.ShouldBe(BuildResultCode.Success); + graphResult.ShouldHaveSucceeded(); - buildSession.Logger.FullLog.ShouldContain("Graph entrypoint based"); + buildSession.Logger.FullLog.ShouldContain("Explicit entry-point based"); AssertCacheBuild(graph, testData, null, buildSession.Logger, graphResult.ResultsByNode); } @@ -674,7 +1030,7 @@ public void GraphBuildCanDiscoverAndLoadPluginFromAssembly() var graphResult = buildSession.BuildGraph(graph); - graphResult.OverallResult.ShouldBe(BuildResultCode.Success); + graphResult.ShouldHaveSucceeded(); buildSession.Logger.FullLog.ShouldContain("Static graph based"); @@ -728,10 +1084,10 @@ public void BuildFailsWhenCacheBuildResultIsWrong() mockCache.Requests.Count.ShouldBe(2); - buildResult.ResultsByNode.First(r => GetProjectNumber(r.Key) == 2).Value.OverallResult.ShouldBe(BuildResultCode.Success); - buildResult.ResultsByNode.First(r => GetProjectNumber(r.Key) == 1).Value.OverallResult.ShouldBe(BuildResultCode.Failure); + buildResult.ResultsByNode.First(r => GetProjectNumber(r.Key) == 2).Value.ShouldHaveSucceeded(); + buildResult.ResultsByNode.First(r => GetProjectNumber(r.Key) == 1).Value.ShouldHaveFailed(); - buildResult.OverallResult.ShouldBe(BuildResultCode.Failure); + buildResult.ShouldHaveFailed(); buildSession.Logger.FullLog.ShouldContain("Reference file [Invalid file] does not exist"); } @@ -739,8 +1095,6 @@ public void BuildFailsWhenCacheBuildResultIsWrong() [Fact] public void GraphBuildErrorsIfMultiplePluginsAreFound() { - _env.DoNotLaunchDebugger(); - var graph = Helpers.CreateProjectGraph( _env, new Dictionary @@ -757,16 +1111,13 @@ public void GraphBuildErrorsIfMultiplePluginsAreFound() using var buildSession = new Helpers.BuildManagerSession(_env); var graphResult = buildSession.BuildGraph(graph); - - graphResult.OverallResult.ShouldBe(BuildResultCode.Failure); - graphResult.Exception.Message.ShouldContain("A single project cache plugin must be specified but multiple where found:"); + + graphResult.ShouldHaveFailed("A single project cache plugin must be specified but multiple where found:"); } [Fact] public void GraphBuildErrorsIfNotAllNodeDefineAPlugin() { - _env.DoNotLaunchDebugger(); - var graph = Helpers.CreateProjectGraph( _env, dependencyEdges: new Dictionary @@ -788,9 +1139,8 @@ public void GraphBuildErrorsIfNotAllNodeDefineAPlugin() using var buildSession = new Helpers.BuildManagerSession(_env); var graphResult = buildSession.BuildGraph(graph); - - graphResult.OverallResult.ShouldBe(BuildResultCode.Failure); - graphResult.Exception.Message.ShouldContain("When any static graph node defines a project cache, all nodes must define the same project cache."); + + graphResult.ShouldHaveFailed("When any static graph node defines a project cache, all nodes must define the same project cache."); } public static IEnumerable CacheExceptionLocationsTestData @@ -800,7 +1150,7 @@ public static IEnumerable CacheExceptionLocationsTestData // Plugin constructors cannot log errors, they can only throw exceptions. yield return new object[] { ErrorLocations.Constructor, ErrorKind.Exception }; - foreach (var errorKind in new[]{ErrorKind.Exception, ErrorKind.LoggedError}) + foreach (var errorKind in new[] { ErrorKind.Exception, ErrorKind.LoggedError }) { yield return new object[] { ErrorLocations.BeginBuildAsync, errorKind }; yield return new object[] { ErrorLocations.BeginBuildAsync | ErrorLocations.GetCacheResultAsync, errorKind }; @@ -819,8 +1169,6 @@ public static IEnumerable CacheExceptionLocationsTestData [MemberData(nameof(CacheExceptionLocationsTestData))] public void EngineShouldHandleExceptionsFromCachePluginViaBuildParameters(ErrorLocations errorLocations, ErrorKind errorKind) { - _env.DoNotLaunchDebugger(); - SetEnvironmentForErrorLocations(errorLocations, errorKind.ToString()); var project = _env.CreateFile("1.proj", @$" @@ -874,11 +1222,11 @@ public void EngineShouldHandleExceptionsFromCachePluginViaBuildParameters(ErrorL // so the build submission should be successful. if (errorLocations == ErrorLocations.EndBuildAsync) { - buildResult.OverallResult.ShouldBe(BuildResultCode.Success); + buildResult.ShouldHaveSucceeded(); } else { - buildResult.OverallResult.ShouldBe(BuildResultCode.Failure); + buildResult.ShouldHaveFailed(); } } finally @@ -940,8 +1288,6 @@ public void EngineShouldHandleExceptionsFromCachePluginViaBuildParameters(ErrorL [MemberData(nameof(CacheExceptionLocationsTestData))] public void EngineShouldHandleExceptionsFromCachePluginViaGraphBuild(ErrorLocations errorLocations, ErrorKind errorKind) { - _env.DoNotLaunchDebugger(); - SetEnvironmentForErrorLocations(errorLocations, errorKind.ToString()); var graph = Helpers.CreateProjectGraph( @@ -981,7 +1327,7 @@ public void EngineShouldHandleExceptionsFromCachePluginViaGraphBuild(ErrorLocati logger.FullLog.ShouldContain("Loading the following project cache plugin:"); // Static graph build initializes and tears down the cache plugin so all cache plugin exceptions should end up in the GraphBuildResult - buildResult.OverallResult.ShouldBe(BuildResultCode.Failure); + buildResult.ShouldHaveFailed(); buildResult.Exception.ShouldBeOfType(); @@ -1029,8 +1375,6 @@ public void EngineShouldHandleExceptionsFromCachePluginViaGraphBuild(ErrorLocati [Fact] public void EndBuildShouldGetCalledOnceWhenItThrowsExceptionsFromGraphBuilds() { - _env.DoNotLaunchDebugger(); - var project = _env.CreateFile( "1.proj", @$" @@ -1054,14 +1398,14 @@ public void EndBuildShouldGetCalledOnceWhenItThrowsExceptionsFromGraphBuilds() var logger = buildSession.Logger; - GraphBuildResult? buildResult = null; + GraphBuildResult buildResult = null!; Should.NotThrow( () => { buildResult = buildSession.BuildGraph(new ProjectGraph(project.Path)); }); - buildResult!.OverallResult.ShouldBe(BuildResultCode.Failure); + buildResult.ShouldHaveFailed(); buildResult.Exception.InnerException!.ShouldNotBeNull(); buildResult.Exception.InnerException!.Message.ShouldContain("Cache plugin exception from EndBuildAsync"); @@ -1070,6 +1414,296 @@ public void EndBuildShouldGetCalledOnceWhenItThrowsExceptionsFromGraphBuilds() StringShouldContainSubstring(logger.FullLog, $"{nameof(AssemblyMockCache)}: EndBuildAsync", expectedOccurrences: 1); } + [Theory] + [InlineData(false, false)] + [InlineData(true, true)] + public void CacheShouldBeQueriedInParallelDuringGraphBuilds(bool useSynchronousLogging, bool disableInprocNode) + { + var referenceNumbers = new []{2, 3, 4}; + + var testData = new GraphCacheResponse( + new Dictionary + { + {1, referenceNumbers} + }, + referenceNumbers.ToDictionary(k => k, k => GraphCacheResponse.SuccessfulProxyTargetResult()) + ); + + var graph = testData.CreateGraph(_env); + + var completedCacheRequests = new ConcurrentBag(); + var task2Completion = new TaskCompletionSource(); + task2Completion.Task.IsCompleted.ShouldBeFalse(); + + var cache = new DelegatingMockCache( + async (buildRequest, _, _) => + { + var projectNumber = GetProjectNumber(buildRequest.ProjectFullPath); + + try + { + if (projectNumber == 2) + { + await task2Completion.Task; + } + + return testData.GetExpectedCacheResultForProjectNumber(projectNumber); + } + finally + { + completedCacheRequests.Add(projectNumber); + } + }); + + using var buildSession = new Helpers.BuildManagerSession(_env, new BuildParameters() + { + MaxNodeCount = NativeMethodsShared.GetLogicalCoreCount(), + ProjectCacheDescriptor = ProjectCacheDescriptor.FromInstance( + cache, + entryPoints: null, + graph), + UseSynchronousLogging = useSynchronousLogging, + DisableInProcNode = disableInprocNode + }); + + var task2 = BuildProjectFileAsync(2); + var task3 = BuildProjectFileAsync(3); + var task4 = BuildProjectFileAsync(4); + + task3.Result.ShouldHaveSucceeded(); + completedCacheRequests.ShouldContain(3); + task4.Result.ShouldHaveSucceeded(); + completedCacheRequests.ShouldContain(4); + + // task 2 hasn't been instructed to finish yet + task2.IsCompleted.ShouldBeFalse(); + completedCacheRequests.ShouldNotContain(2); + + task2Completion.SetResult(true); + + task2.Result.ShouldHaveSucceeded(); + completedCacheRequests.ShouldContain(2); + + var task1 = BuildProjectFileAsync(1); + task1.Result.ShouldHaveSucceeded(); + completedCacheRequests.ShouldContain(1); + + Task BuildProjectFileAsync(int projectNumber) + { + return buildSession.BuildProjectFileAsync(graph.ProjectNodes.First(n => GetProjectNumber(n) == projectNumber).ProjectInstance.FullPath); + } + } + + [Theory] + [InlineData(false, false)] + // TODO: Reenable when this gets into the main branch. + //[InlineData(true, true)] + public void ParallelStressTestForVsWorkaround(bool useSynchronousLogging, bool disableInprocNode) + { + var currentBuildEnvironment = BuildEnvironmentHelper.Instance; + + try + { + BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly( + new BuildEnvironment( + currentBuildEnvironment.Mode, + currentBuildEnvironment.CurrentMSBuildExePath, + currentBuildEnvironment.RunningTests, + runningInVisualStudio: true, + visualStudioPath: currentBuildEnvironment.VisualStudioInstallRootDirectory)); + + BuildManager.ProjectCacheItems.ShouldBeEmpty(); + + var referenceNumbers = Enumerable.Range(2, NativeMethodsShared.GetLogicalCoreCount() * 2).ToArray(); + + var testData = new GraphCacheResponse( + new Dictionary + { + {1, referenceNumbers} + }, + referenceNumbers.ToDictionary(k => k, k => GraphCacheResponse.SuccessfulProxyTargetResult()) + ); + + var graph = testData.CreateGraph(_env); + + // Even though the assembly cache is discovered, we'll be overriding it with a descriptor based cache. + BuildManager.ProjectCacheItems.ShouldHaveSingleItem(); + + var solutionConfigurationGlobalProperty = + CreateSolutionConfigurationProperty(graph.ProjectNodes); + + using var buildSession = new Helpers.BuildManagerSession(_env, new BuildParameters + { + MaxNodeCount = NativeMethodsShared.GetLogicalCoreCount(), + UseSynchronousLogging = useSynchronousLogging, + DisableInProcNode = disableInprocNode + }); + + var buildResultTasks = new List>(); + + foreach (var node in graph.ProjectNodes.Where(n => referenceNumbers.Contains(GetProjectNumber(n)))) + { + var buildResultTask = buildSession.BuildProjectFileAsync( + node.ProjectInstance.FullPath, + globalProperties: + new Dictionary + { + { SolutionProjectGenerator.SolutionPathPropertyName, graph.GraphRoots.First().ProjectInstance.FullPath }, + { SolutionProjectGenerator.CurrentSolutionConfigurationContents, solutionConfigurationGlobalProperty } + }); + + buildResultTasks.Add(buildResultTask); + } + + foreach (var buildResultTask in buildResultTasks) + { + buildResultTask.Result.ShouldHaveSucceeded(); + } + + buildSession.BuildProjectFile( + graph.GraphRoots.First().ProjectInstance.FullPath, + globalProperties: + new Dictionary {{"SolutionPath", graph.GraphRoots.First().ProjectInstance.FullPath}}) + .ShouldHaveSucceeded(); + + StringShouldContainSubstring(buildSession.Logger.FullLog, $"{AssemblyMockCache}: GetCacheResultAsync for", graph.ProjectNodes.Count); + + buildSession.Logger.FullLog.ShouldContain("Visual Studio Workaround based"); + buildSession.Logger.FullLog.ShouldContain("Running project cache with Visual Studio workaround"); + } + finally + { + BuildEnvironmentHelper.ResetInstance_ForUnitTestsOnly(currentBuildEnvironment); + BuildManager.ProjectCacheItems.Clear(); + } + } + + [Theory] + [InlineData(false, false)] + [InlineData(true, true)] + public void ParallelStressTest(bool useSynchronousLogging, bool disableInprocNode) + { + var referenceNumbers = Enumerable.Range(2, NativeMethodsShared.GetLogicalCoreCount() * 2).ToArray(); + + var testData = new GraphCacheResponse( + new Dictionary + { + {1, referenceNumbers} + }, + referenceNumbers.ToDictionary(k => k, k => GraphCacheResponse.SuccessfulProxyTargetResult()) + ); + + var graph = testData.CreateGraph(_env); + var cache = new InstanceMockCache(testData, TimeSpan.FromMilliseconds(50)); + + using var buildSession = new Helpers.BuildManagerSession(_env, new BuildParameters() + { + MaxNodeCount = NativeMethodsShared.GetLogicalCoreCount(), + ProjectCacheDescriptor = ProjectCacheDescriptor.FromInstance( + cache, + entryPoints: null, + graph), + UseSynchronousLogging = useSynchronousLogging, + DisableInProcNode = disableInprocNode + }); + + var graphResult = buildSession.BuildGraph(graph); + + graphResult.ShouldHaveSucceeded(); + cache.QueryStartStops.Count.ShouldBe(graph.ProjectNodes.Count * 2); + } + + [Fact] + // Schedules different requests for the same BuildRequestConfiguration in parallel. + // The first batch of the requests are cache misses, the second batch are cache hits via proxy builds. + // The first batch is delayed so it starts intermingling with the second batch. + // This test ensures that scheduling proxy builds on the inproc node works nicely within the Scheduler + // if the BuildRequestConfigurations for those proxy builds have built before (or are still building) on + // the out of proc node. + // More details: https://github.com/dotnet/msbuild/pull/6635 + public void ProxyCacheHitsOnPreviousCacheMissesShouldWork() + { + var cacheNotApplicableTarget = "NATarget"; + var cacheHitTarget = "CacheHitTarget"; + var proxyTarget = "ProxyTarget"; + + var project = +@$" + + + + + + + + + + + + + + +".Cleanup(); + + var projectPaths = Enumerable.Range(0, NativeMethodsShared.GetLogicalCoreCount()) + .Select(i => _env.CreateFile($"project{i}.proj", project).Path) + .ToArray(); + + var cacheHitCount = 0; + var nonCacheHitCount = 0; + + var buildParameters = new BuildParameters + { + ProjectCacheDescriptor = ProjectCacheDescriptor.FromInstance( + new ConfigurableMockCache + { + GetCacheResultImplementation = (request, _, _) => + { + var projectFile = request.ProjectFullPath; + + if (request.TargetNames.Contains(cacheNotApplicableTarget)) + { + Interlocked.Increment(ref nonCacheHitCount); + return Task.FromResult(CacheResult.IndicateNonCacheHit(CacheResultType.CacheNotApplicable)); + } + else + { + Interlocked.Increment(ref cacheHitCount); + return Task.FromResult( + CacheResult.IndicateCacheHit( + new ProxyTargets(new Dictionary {{proxyTarget, cacheHitTarget}}))); + } + } + }, + projectPaths.Select(p => new ProjectGraphEntryPoint(p)).ToArray(), + projectGraph: null), + MaxNodeCount = NativeMethodsShared.GetLogicalCoreCount() + }; + + using var buildSession = new Helpers.BuildManagerSession(_env, buildParameters); + + var buildRequests = new List<(string, string)>(); + buildRequests.AddRange(projectPaths.Select(r => (r, cacheNotApplicableTarget))); + buildRequests.AddRange(projectPaths.Select(r => (r, cacheHitTarget))); + + var buildTasks = new List>(); + foreach (var (projectPath, target) in buildRequests) + { + buildTasks.Add(buildSession.BuildProjectFileAsync(projectPath, new[] {target})); + } + + foreach (var buildResult in buildTasks.Select(buildTask => buildTask.Result)) + { + buildResult.Exception.ShouldBeNull(); + buildResult.ShouldHaveSucceeded(); + } + + buildSession.Logger.ProjectStartedEvents.Count.ShouldBe(2 * projectPaths.Length); + + cacheHitCount.ShouldBe(projectPaths.Length); + nonCacheHitCount.ShouldBe(projectPaths.Length); + } + private static void StringShouldContainSubstring(string aString, string substring, int expectedOccurrences) { aString.ShouldContain(substring); diff --git a/src/Build/AssemblyInfo.cs b/src/Build/AssemblyInfo.cs index 5717eb380fb..21e9b159651 100644 --- a/src/Build/AssemblyInfo.cs +++ b/src/Build/AssemblyInfo.cs @@ -26,14 +26,7 @@ // so that we don't run into known security issues with loading libraries from unsafe locations [assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] -// Needed for the "hub-and-spoke model to locate and retrieve localized resources": https://msdn.microsoft.com/en-us/library/21a15yht(v=vs.110).aspx -// We want "en" to require a satellite assembly for debug builds in order to flush out localization -// issues, but we want release builds to work without it. Also, .net core does not have resource fallbacks -#if (DEBUG && !RUNTIME_TYPE_NETCORE) -[assembly: NeutralResourcesLanguage("en", UltimateResourceFallbackLocation.Satellite)] -#else [assembly: NeutralResourcesLanguage("en")] -#endif [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] diff --git a/src/Build/BackEnd/BuildManager/BuildManager.cs b/src/Build/BackEnd/BuildManager/BuildManager.cs index ef032ea7791..ddabf21013b 100644 --- a/src/Build/BackEnd/BuildManager/BuildManager.cs +++ b/src/Build/BackEnd/BuildManager/BuildManager.cs @@ -30,7 +30,9 @@ using Microsoft.Build.Internal; using Microsoft.Build.Logging; using Microsoft.Build.Shared; +using Microsoft.Build.Shared.Debugging; using Microsoft.Build.Shared.FileSystem; +using Microsoft.Build.Utilities; using ForwardingLoggerRecord = Microsoft.Build.Logging.ForwardingLoggerRecord; using LoggerDescription = Microsoft.Build.Logging.LoggerDescription; @@ -244,7 +246,6 @@ public class BuildManager : INodePacketHandler, IBuildComponentHost, IDisposable private IEnumerable _deferredBuildMessages; private Task _projectCacheService; - private bool _projectCacheServiceInstantiatedByVSWorkaround; #if DEBUG /// @@ -287,7 +288,7 @@ public BuildManager(string hostName) _projectStartedEventHandler = OnProjectStarted; _projectFinishedEventHandler = OnProjectFinished; - _loggingThreadExceptionEventHandler = OnThreadException; + _loggingThreadExceptionEventHandler = OnLoggingThreadException; _legacyThreadingData = new LegacyThreadingData(); _instantiationTimeUtc = DateTime.UtcNow; } @@ -410,6 +411,8 @@ public void BeginBuild(BuildParameters parameters) { lock (_syncLock) { + AttachDebugger(); + // Check for build in progress. RequireState(BuildManagerState.Idle, "BuildInProgress"); @@ -485,7 +488,7 @@ public void BeginBuild(BuildParameters parameters) ILoggingService InitializeLoggingService() { ILoggingService loggingService = CreateLoggingService( - _buildParameters.Loggers, + AppendDebuggingLoggers(_buildParameters.Loggers), _buildParameters.ForwardingLoggers, _buildParameters.WarningsAsErrors, _buildParameters.WarningsAsMessages); @@ -517,8 +520,26 @@ ILoggingService InitializeLoggingService() return loggingService; } + // VS builds discard many msbuild events so attach a binlogger to capture them all. + IEnumerable AppendDebuggingLoggers(IEnumerable loggers) + { + if (DebugUtils.ShouldDebugCurrentProcess is false || + Traits.Instance.DebugEngine is false) + { + return loggers; + } + + var binlogPath = DebugUtils.FindNextAvailableDebugFilePath($"{DebugUtils.ProcessInfoString}_BuildManager_{_hostName}.binlog"); + + var logger = new BinaryLogger { Parameters = binlogPath }; + + return (loggers ?? Enumerable.Empty()).Concat(new[] { logger }); + } + void InitializeCaches() { + Debug.Assert(Monitor.IsEntered(_syncLock)); + var usesInputCaches = _buildParameters.UsesInputCaches(); if (usesInputCaches) @@ -558,10 +579,40 @@ void InitializeCaches() } } + private static void AttachDebugger() + { + if (Debugger.IsAttached) + { + return; + } + + if (!DebugUtils.ShouldDebugCurrentProcess) + { + return; + } + + switch (Environment.GetEnvironmentVariable("MSBuildDebugBuildManagerOnStart")) + { +#if FEATURE_DEBUG_LAUNCH + case "1": + Debugger.Launch(); + break; +#endif + case "2": + // Sometimes easier to attach rather than deal with JIT prompt + Process currentProcess = Process.GetCurrentProcess(); + Console.WriteLine($"Waiting for debugger to attach ({currentProcess.MainModule.FileName} PID {currentProcess.Id}). Press enter to continue..."); + Console.ReadLine(); + break; + } + } + private void InitializeProjectCacheService( ProjectCacheDescriptor pluginDescriptor, CancellationToken cancellationToken) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + if (_projectCacheService != null) { ErrorUtilities.ThrowInternalError("Only one project cache plugin may be set on the BuildManager during a begin / end build session"); @@ -781,10 +832,15 @@ public void EndBuild() ShutdownConnectedNodes(false /* normal termination */); _noNodesActiveEvent.WaitOne(); - // Wait for all of the actions in the work queue to drain. Wait() could throw here if there was an unhandled exception - // in the work queue, but the top level exception handler there should catch everything and have forwarded it to the + // Wait for all of the actions in the work queue to drain. + // _workQueue.Completion.Wait() could throw here if there was an unhandled exception in the work queue, + // but the top level exception handler there should catch everything and have forwarded it to the // OnThreadException method in this class already. _workQueue.Complete(); + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0)) + { + _workQueue.Completion.Wait(); + } // Stop the graph scheduling thread(s) _graphSchedulingCancellationSource?.Cancel(); @@ -1018,30 +1074,10 @@ internal void ExecuteSubmission(BuildSubmission submission, bool allowMainThread ErrorUtilities.VerifyThrowArgumentNull(submission, nameof(submission)); ErrorUtilities.VerifyThrow(!submission.IsCompleted, "Submission already complete."); - bool thisMethodIsAsync = false; - - if (ProjectCacheIsPresent()) - { - thisMethodIsAsync = true; - - // Potential long running operations: - // - submission may need evaluation - // - project cache may need initializing - // - project cache will be queried - // Use separate thread to unblock calling thread. - Task.Factory.StartNew( - ExecuteSubmissionImpl, - CancellationToken.None, - TaskCreationOptions.LongRunning, - TaskScheduler.Default - ); - } - else - { - ExecuteSubmissionImpl(); - } + BuildRequestConfiguration resolvedConfiguration = null; + bool shuttingDown = false; - void ExecuteSubmissionImpl() + try { lock (_syncLock) { @@ -1070,271 +1106,181 @@ void ExecuteSubmissionImpl() VerifyStateInternal(BuildManagerState.Building); - try + // If we have an unnamed project, assign it a temporary name. + if (string.IsNullOrEmpty(submission.BuildRequestData.ProjectFullPath)) { - // If we have an unnamed project, assign it a temporary name. - if (string.IsNullOrEmpty(submission.BuildRequestData.ProjectFullPath)) - { - ErrorUtilities.VerifyThrow( - submission.BuildRequestData.ProjectInstance != null, - "Unexpected null path for a submission with no ProjectInstance."); - - // If we have already named this instance when it was submitted previously during this build, use the same - // name so that we get the same configuration (and thus don't cause it to rebuild.) - if (!_unnamedProjectInstanceToNames.TryGetValue(submission.BuildRequestData.ProjectInstance, out var tempName)) - { - tempName = "Unnamed_" + _nextUnnamedProjectId++; - _unnamedProjectInstanceToNames[submission.BuildRequestData.ProjectInstance] = tempName; - } - - submission.BuildRequestData.ProjectFullPath = Path.Combine( - submission.BuildRequestData.ProjectInstance.GetProperty(ReservedPropertyNames.projectDirectory).EvaluatedValue, - tempName); - } - - // Create/Retrieve a configuration for each request - var buildRequestConfiguration = new BuildRequestConfiguration(submission.BuildRequestData, _buildParameters.DefaultToolsVersion); - var matchingConfiguration = _configCache.GetMatchingConfiguration(buildRequestConfiguration); - var newConfiguration = ResolveConfiguration( - buildRequestConfiguration, - matchingConfiguration, - submission.BuildRequestData.Flags.HasFlag(BuildRequestDataFlags.ReplaceExistingProjectInstance)); - - newConfiguration.ExplicitlyLoaded = true; - - submission.BuildRequest = CreateRealBuildRequest(submission, newConfiguration.ConfigurationId); + ErrorUtilities.VerifyThrow( + submission.BuildRequestData.ProjectInstance != null, + "Unexpected null path for a submission with no ProjectInstance."); - // TODO: Remove this when VS gets updated to setup project cache plugins. - AutomaticallyDetectAndInstantiateProjectCacheServiceForVisualStudio(submission, newConfiguration); - - CacheResult cacheResult = null; - if (_projectCacheService != null) + // If we have already named this instance when it was submitted previously during this build, use the same + // name so that we get the same configuration (and thus don't cause it to rebuild.) + if (!_unnamedProjectInstanceToNames.TryGetValue(submission.BuildRequestData.ProjectInstance, out var tempName)) { - cacheResult = QueryCache(submission, newConfiguration); + tempName = "Unnamed_" + _nextUnnamedProjectId++; + _unnamedProjectInstanceToNames[submission.BuildRequestData.ProjectInstance] = tempName; } - if (cacheResult == null || cacheResult.ResultType != CacheResultType.CacheHit) - { - // Issue the real build request. - SubmitBuildRequest(); - } - else if (cacheResult?.ResultType == CacheResultType.CacheHit && cacheResult.ProxyTargets != null) - { - // Setup submission.BuildRequest with proxy targets. The proxy request is built on the inproc node (to avoid ProjectInstance serialization). - // The proxy target results are used as results for the real targets. + submission.BuildRequestData.ProjectFullPath = Path.Combine( + submission.BuildRequestData.ProjectInstance.GetProperty(ReservedPropertyNames.projectDirectory).EvaluatedValue, + tempName); + } - submission.BuildRequest = CreateProxyBuildRequest( - submission, - newConfiguration.ConfigurationId, - cacheResult.ProxyTargets); + // Create/Retrieve a configuration for each request + var buildRequestConfiguration = new BuildRequestConfiguration(submission.BuildRequestData, _buildParameters.DefaultToolsVersion); + var matchingConfiguration = _configCache.GetMatchingConfiguration(buildRequestConfiguration); + resolvedConfiguration = ResolveConfiguration( + buildRequestConfiguration, + matchingConfiguration, + submission.BuildRequestData.Flags.HasFlag(BuildRequestDataFlags.ReplaceExistingProjectInstance)); - SubmitBuildRequest(); - } - else if (cacheResult?.ResultType == CacheResultType.CacheHit && cacheResult.BuildResult != null) - { - // Mark the build submission as complete with the provided results and return. - var result = new BuildResult(submission.BuildRequest); + resolvedConfiguration.ExplicitlyLoaded = true; - foreach (var targetResult in cacheResult.BuildResult.ResultsByTarget) - { - result.AddResultsForTarget(targetResult.Key, targetResult.Value); - } - - _resultsCache.AddResult(result); - submission.CompleteLogging(false); - ReportResultsToSubmission(result); - } - } - // This catch should always be the first one because when this method runs in a separate thread - // and throws an exception there is nobody there to observe the exception. - catch (Exception ex) when (thisMethodIsAsync) - { - HandleExecuteSubmissionException(submission, ex); - } - catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex)) - { - HandleExecuteSubmissionException(submission, ex); - throw; - } - void SubmitBuildRequest() + // assign shutting down to local variable to avoid race condition: "setting _shuttingDown after this point during this method execution" + shuttingDown = _shuttingDown; + if (!shuttingDown) { - if (CheckForShutdown()) + if (ProjectCacheIsPresent()) { - return; + IssueCacheRequestForBuildSubmission(new CacheRequest(submission, resolvedConfiguration)); + } + else + { + AddBuildRequestToSubmission(submission, resolvedConfiguration.ConfigurationId); + IssueBuildRequestForBuildSubmission(submission, resolvedConfiguration, allowMainThreadBuild); } - - _workQueue.Post( - () => - { - try - { - IssueBuildSubmissionToScheduler(submission, allowMainThreadBuild); - } - catch (BuildAbortedException bae) - { - // We were canceled before we got issued by the work queue. - var result = new BuildResult(submission.BuildRequest, bae); - submission.CompleteResults(result); - submission.CompleteLogging(true); - CheckSubmissionCompletenessAndRemove(submission); - } - catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex)) - { - HandleExecuteSubmissionException(submission, ex); - } - }); } } } - - bool ProjectCacheIsPresent() + catch (ProjectCacheException ex) { - return _projectCacheService != null || - _buildParameters.ProjectCacheDescriptor != null || - (BuildEnvironmentHelper.Instance.RunningInVisualStudio && ProjectCacheItems.Count > 0); + ErrorUtilities.VerifyThrow(resolvedConfiguration is not null, "Cannot call project cache without having BuildRequestConfiguration"); + CompleteSubmissionWithException(submission, resolvedConfiguration, ex); } - - bool CheckForShutdown() + catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex)) { - if (!_shuttingDown) + if (resolvedConfiguration is not null) { - return false; + CompleteSubmissionWithException(submission, resolvedConfiguration, ex); } + else + { + HandleSubmissionException(submission, ex); + throw; + } + } + // We are shutting down so submission has to be completed with BuildAbortedException + Debug.Assert(!Monitor.IsEntered(_syncLock)); + if (shuttingDown) + { + ErrorUtilities.VerifyThrow(resolvedConfiguration is not null, "Cannot call project cache without having BuildRequestConfiguration"); // We were already canceled! - var result = new BuildResult(submission.BuildRequest, new BuildAbortedException()); - submission.CompleteResults(result); - submission.CompleteLogging(true); - CheckSubmissionCompletenessAndRemove(submission); - - return true; + CompleteSubmissionWithException(submission, resolvedConfiguration, new BuildAbortedException()); } + } + + bool ProjectCacheIsPresent() + { + // TODO: remove after we change VS to set the cache descriptor via build parameters. + // TODO: no need to access the service when there's no design time builds. + var projectCacheService = GetProjectCacheService(); - CacheResult QueryCache(BuildSubmission buildSubmission, BuildRequestConfiguration newConfiguration) + if (projectCacheService != null && projectCacheService.DesignTimeBuildsDetected) { - ProjectCacheService cacheService = null; + return false; + } + + return + projectCacheService != null || + _buildParameters.ProjectCacheDescriptor != null || + ProjectCachePresentViaVisualStudioWorkaround(); + } + + private static bool ProjectCachePresentViaVisualStudioWorkaround() + { + return BuildEnvironmentHelper.Instance.RunningInVisualStudio && ProjectCacheItems.Count > 0; + } + + // Cache requests on configuration N do not block future build submissions depending on configuration N. + // It is assumed that the higher level build orchestrator (static graph scheduler, VS, quickbuild) submits a + // project build request only when its references have finished building. + private void IssueCacheRequestForBuildSubmission(CacheRequest cacheRequest) + { + Debug.Assert(Monitor.IsEntered(_syncLock)); + _workQueue.Post(() => + { try { - cacheService = _projectCacheService.Result; + var projectCacheService = GetProjectCacheService(); + + ErrorUtilities.VerifyThrow( + projectCacheService != null, + "This method should not get called if there's no project cache."); + + ErrorUtilities.VerifyThrow( + !projectCacheService.DesignTimeBuildsDetected, + "This method should not get called if design time builds are detected."); + + projectCacheService.PostCacheRequest(cacheRequest); } - catch + catch (Exception e) { - // Set to null so that EndBuild does not try to shut it down and thus rethrow the exception. - _projectCacheService = null; - throw; + CompleteSubmissionWithException(cacheRequest.Submission, cacheRequest.Configuration, e); } + }); + } - // Project cache plugins require an evaluated project. Evaluate the submission if it's by path. - LoadSubmissionProjectIntoConfiguration(buildSubmission, newConfiguration); - - var cacheResult = cacheService.GetCacheResultAsync( - new BuildRequestData( - newConfiguration.Project, - buildSubmission.BuildRequestData.TargetNames.ToArray())) - .GetAwaiter() - .GetResult(); - - return cacheResult; - } + private ProjectCacheService GetProjectCacheService() + { + // TODO: remove after we change VS to set the cache descriptor via build parameters. + AutomaticallyDetectAndInstantiateProjectCacheServiceForVisualStudio(); - static BuildRequest CreateRealBuildRequest(BuildSubmission submission, int configurationId) + try { - return new BuildRequest( - submission.SubmissionId, - BackEnd.BuildRequest.InvalidNodeRequestId, - configurationId, - submission.BuildRequestData.TargetNames, - submission.BuildRequestData.HostServices, - BuildEventContext.Invalid, - null, - submission.BuildRequestData.Flags, - submission.BuildRequestData.RequestedProjectState); + return _projectCacheService?.Result; } - - static BuildRequest CreateProxyBuildRequest( - BuildSubmission submission, - int configurationId, - ProxyTargets proxyTargets) + catch(Exception ex) { - return new BuildRequest( - submission.SubmissionId, - BackEnd.BuildRequest.InvalidNodeRequestId, - configurationId, - proxyTargets, - submission.BuildRequestData.HostServices, - submission.BuildRequestData.Flags, - submission.BuildRequestData.RequestedProjectState); + if (ex is AggregateException ae && ae.InnerExceptions.Count == 1) + { + ex = ae.InnerExceptions.First(); + } + + // These are exceptions thrown during project cache startup (assembly load issues or cache BeginBuild exceptions). + // Set to null so that EndBuild does not try to shut it down and thus rethrow the exception. + Interlocked.Exchange(ref _projectCacheService, null); + throw ex; } } - private void AutomaticallyDetectAndInstantiateProjectCacheServiceForVisualStudio( - BuildSubmission submission, - BuildRequestConfiguration config) + private void AutomaticallyDetectAndInstantiateProjectCacheServiceForVisualStudio() { if (BuildEnvironmentHelper.Instance.RunningInVisualStudio && ProjectCacheItems.Count > 0 && - !_projectCacheServiceInstantiatedByVSWorkaround && _projectCacheService == null && _buildParameters.ProjectCacheDescriptor == null) { - _projectCacheServiceInstantiatedByVSWorkaround = true; - - if (ProjectCacheItems.Count != 1) - { - ProjectCacheException.ThrowForMSBuildIssueWithTheProjectCache( - "OnlyOneCachePluginMustBeSpecified", - string.Join("; ", ProjectCacheItems.Values.Select(c => c.PluginPath))); - } - - // Plugin needs the graph root (aka top BuildSubmission path, aka the solution path when in VS) which, under VS, is accessible - // only by evaluating the submission and retrieving the 'SolutionPath' property set by VS. This is also the reason why - // this method cannot be called from BeginBuild, because no build submissions are available there to extract the solution path from. - LoadSubmissionProjectIntoConfiguration(submission, config); - - if (IsDesignTimeBuild(config.Project)) + lock (_syncLock) { - // Design time builds do not use the project cache. - return; - } - - var solutionPath = config.Project.GetPropertyValue(SolutionProjectGenerator.SolutionPathPropertyName); - - ErrorUtilities.VerifyThrow( - solutionPath != null && !string.IsNullOrWhiteSpace(solutionPath) && solutionPath != "*Undefined*", - $"Expected VS to set a valid SolutionPath property but got: {solutionPath}"); - - ErrorUtilities.VerifyThrow( - FileSystems.Default.FileExists(solutionPath), - $"Solution file does not exist: {solutionPath}"); - - var projectCacheItem = ProjectCacheItems.First().Value; + if (_projectCacheService != null) + { + return; + } - InitializeProjectCacheService( - ProjectCacheDescriptor.FromAssemblyPath( - projectCacheItem.PluginPath, - new[] - { - new ProjectGraphEntryPoint( - solutionPath, - config.Project.GlobalProperties) - }, - null, - projectCacheItem.PluginSettings), - CancellationToken.None); - } + if (ProjectCacheItems.Count != 1) + { + ProjectCacheException.ThrowForMSBuildIssueWithTheProjectCache( + "OnlyOneCachePluginMustBeSpecified", + string.Join("; ", ProjectCacheItems.Values.Select(c => c.PluginPath))); + } - static bool IsDesignTimeBuild(ProjectInstance project) - { - var designTimeBuild = project.GetPropertyValue(DesignTimeProperties.DesignTimeBuild); - var buildingProject = project.GlobalPropertiesDictionary[DesignTimeProperties.BuildingProject]?.EvaluatedValue; + var projectCacheItem = ProjectCacheItems.First().Value; - return MSBuildStringIsTrue(designTimeBuild) || - buildingProject != null && !MSBuildStringIsTrue(buildingProject); + InitializeProjectCacheService(ProjectCacheDescriptor.FromVisualStudioWorkaround(projectCacheItem), CancellationToken.None); + } } - - static bool MSBuildStringIsTrue(string msbuildString) => - ConversionUtilities.ConvertStringToBool(msbuildString, nullOrWhitespaceIsFalse: true); } /// @@ -1375,7 +1321,7 @@ internal void ExecuteSubmission(GraphBuildSubmission submission) } catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex)) { - HandleExecuteSubmissionException(submission, ex); + HandleSubmissionException(submission, ex); } }, _graphSchedulingCancellationSource.Token, @@ -1384,7 +1330,7 @@ internal void ExecuteSubmission(GraphBuildSubmission submission) } catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex)) { - HandleExecuteSubmissionException(submission, ex); + HandleSubmissionException(submission, ex); throw; } } @@ -1412,6 +1358,8 @@ private void LoadSubmissionProjectIntoConfiguration(BuildSubmission submission, /// private void LoadSolutionIntoConfiguration(BuildRequestConfiguration config, BuildRequest request) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + if (config.IsLoaded) { // We've already processed it, nothing to do. @@ -1578,11 +1526,34 @@ private void ProcessPacket(int node, INodePacket packet) } } + /// + /// To avoid deadlock possibility, this method MUST NOT be called inside of 'lock (_syncLock)' + /// + private void CompleteSubmissionWithException(BuildSubmission submission, BuildRequestConfiguration configuration, Exception exception) + { + Debug.Assert(!Monitor.IsEntered(_syncLock)); + + lock (_syncLock) + { + if (submission.BuildRequest is null) + { + AddBuildRequestToSubmission(submission, configuration.ConfigurationId); + } + } + + HandleSubmissionException(submission, exception); + } + /// - /// Deals with exceptions that may be thrown as a result of ExecuteSubmission. + /// Deals with exceptions that may be thrown when handling a submission. /// - private void HandleExecuteSubmissionException(BuildSubmission submission, Exception ex) + /// + /// To avoid deadlock possibility, this method MUST NOT be called inside of 'lock (_syncLock)' + /// + private void HandleSubmissionException(BuildSubmission submission, Exception ex) { + Debug.Assert(!Monitor.IsEntered(_syncLock)); + if (ex is AggregateException ae && ae.InnerExceptions.Count == 1) { ex = ae.InnerExceptions.First(); @@ -1597,22 +1568,54 @@ private void HandleExecuteSubmissionException(BuildSubmission submission, Except } } - // BuildRequest may be null if the submission fails early on. - if (submission.BuildRequest != null) + bool submissionNeedsCompletion; + lock (_syncLock) { - var result = new BuildResult(submission.BuildRequest, ex); - submission.CompleteResults(result); - submission.CompleteLogging(true); + // BuildRequest may be null if the submission fails early on. + submissionNeedsCompletion = submission.BuildRequest != null; + if (submissionNeedsCompletion) + { + var result = new BuildResult(submission.BuildRequest, ex); + submission.CompleteResults(result); + } } - _overallBuildSuccess = false; - CheckSubmissionCompletenessAndRemove(submission); + if (submissionNeedsCompletion) + { + WaitForAllLoggingServiceEventsToBeProcessed(); + } + + lock (_syncLock) + { + if (submissionNeedsCompletion) + { + submission.CompleteLogging(); + } + + _overallBuildSuccess = false; + CheckSubmissionCompletenessAndRemove(submission); + } + } + + /// + /// Waits to drain all events of logging service. + /// This method shall be used carefully because during draining, LoggingService will block all incoming events. + /// + /// + /// To avoid deadlock possibility, this method MUST NOT be called inside of 'lock (_syncLock)' + /// + private void WaitForAllLoggingServiceEventsToBeProcessed() + { + // this has to be called out of the lock (_syncLock) + // because processing events can callback to 'this' instance and cause deadlock + Debug.Assert(!Monitor.IsEntered(_syncLock)); + ((LoggingService) ((IBuildComponentHost) this).LoggingService).WaitForThreadToProcessEvents(); } /// /// Deals with exceptions that may be thrown as a result of ExecuteSubmission. /// - private void HandleExecuteSubmissionException(GraphBuildSubmission submission, Exception ex) + private void HandleSubmissionException(GraphBuildSubmission submission, Exception ex) { if (ex is InvalidProjectFileException projectException) { @@ -1628,76 +1631,135 @@ private void HandleExecuteSubmissionException(GraphBuildSubmission submission, E ? ae.InnerExceptions.First() : ex; - if (submission.IsStarted) + lock (_syncLock) { - submission.CompleteResults(new GraphBuildResult(submission.SubmissionId, ex)); + if (submission.IsStarted) + { + submission.CompleteResults(new GraphBuildResult(submission.SubmissionId, ex)); + } + + _overallBuildSuccess = false; + CheckSubmissionCompletenessAndRemove(submission); } + } - _overallBuildSuccess = false; - CheckSubmissionCompletenessAndRemove(submission); + private static void AddBuildRequestToSubmission(BuildSubmission submission, int configurationId) + { + submission.BuildRequest = new BuildRequest( + submission.SubmissionId, + BackEnd.BuildRequest.InvalidNodeRequestId, + configurationId, + submission.BuildRequestData.TargetNames, + submission.BuildRequestData.HostServices, + BuildEventContext.Invalid, + null, + submission.BuildRequestData.Flags, + submission.BuildRequestData.RequestedProjectState); + } + + private static void AddProxyBuildRequestToSubmission(BuildSubmission submission, int configurationId, ProxyTargets proxyTargets) + { + submission.BuildRequest = new BuildRequest( + submission.SubmissionId, + BackEnd.BuildRequest.InvalidNodeRequestId, + configurationId, + proxyTargets, + submission.BuildRequestData.HostServices, + submission.BuildRequestData.Flags, + submission.BuildRequestData.RequestedProjectState); } /// /// The submission is a top level build request entering the BuildManager. /// Sends the request to the scheduler with optional legacy threading semantics behavior. /// - private void IssueBuildSubmissionToScheduler(BuildSubmission submission, bool allowMainThreadBuild) + private void IssueBuildRequestForBuildSubmission(BuildSubmission submission, BuildRequestConfiguration configuration, bool allowMainThreadBuild = false) { - bool resetMainThreadOnFailure = false; - try - { - lock (_syncLock) + _workQueue.Post( + () => { - if (_shuttingDown) + try + { + IssueBuildSubmissionToSchedulerImpl(submission, allowMainThreadBuild); + } + catch (BuildAbortedException bae) + { + CompleteSubmissionWithException(submission, configuration, bae); + } + catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex)) { - throw new BuildAbortedException(); + HandleSubmissionException(submission, ex); } + }); - if (allowMainThreadBuild && _buildParameters.LegacyThreadingSemantics) + void IssueBuildSubmissionToSchedulerImpl(BuildSubmission submission, bool allowMainThreadBuild) + { + var resetMainThreadOnFailure = false; + try + { + lock (_syncLock) { - if (_legacyThreadingData.MainThreadSubmissionId == -1) + if (_shuttingDown) { - resetMainThreadOnFailure = true; - _legacyThreadingData.MainThreadSubmissionId = submission.SubmissionId; + throw new BuildAbortedException(); } - } - BuildRequestBlocker blocker = new BuildRequestBlocker(-1, Array.Empty(), new[] {submission.BuildRequest}); + if (allowMainThreadBuild && _buildParameters.LegacyThreadingSemantics) + { + if (_legacyThreadingData.MainThreadSubmissionId == -1) + { + resetMainThreadOnFailure = true; + _legacyThreadingData.MainThreadSubmissionId = submission.SubmissionId; + } + } + + BuildRequestBlocker blocker = new BuildRequestBlocker(-1, Array.Empty(), new[] {submission.BuildRequest}); - HandleNewRequest(Scheduler.VirtualNode, blocker); + HandleNewRequest(Scheduler.VirtualNode, blocker); + } } - } - catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex)) - { - InvalidProjectFileException projectException = ex as InvalidProjectFileException; - if (projectException != null) + catch (Exception ex) when (!ExceptionHandling.IsCriticalException(ex)) { - if (!projectException.HasBeenLogged) + var projectException = ex as InvalidProjectFileException; + if (projectException != null) { - BuildEventContext projectBuildEventContext = new BuildEventContext(submission.SubmissionId, 1, BuildEventContext.InvalidProjectInstanceId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidTaskId); - ((IBuildComponentHost)this).LoggingService.LogInvalidProjectFileError(projectBuildEventContext, projectException); - projectException.HasBeenLogged = true; + if (!projectException.HasBeenLogged) + { + BuildEventContext projectBuildEventContext = new BuildEventContext(submission.SubmissionId, 1, BuildEventContext.InvalidProjectInstanceId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidTaskId); + ((IBuildComponentHost)this).LoggingService.LogInvalidProjectFileError(projectBuildEventContext, projectException); + projectException.HasBeenLogged = true; + } + } + else if ((ex is BuildAbortedException) || ExceptionHandling.NotExpectedException(ex)) + { + throw; } - } - else if ((ex is BuildAbortedException) || ExceptionHandling.NotExpectedException(ex)) - { - throw; - } - if (resetMainThreadOnFailure) - { - _legacyThreadingData.MainThreadSubmissionId = -1; - } + lock (_syncLock) + { - if (projectException == null) - { - BuildEventContext buildEventContext = new BuildEventContext(submission.SubmissionId, 1, BuildEventContext.InvalidProjectInstanceId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidTaskId); - ((IBuildComponentHost)this).LoggingService.LogFatalBuildError(buildEventContext, ex, new BuildEventFileInfo(submission.BuildRequestData.ProjectFullPath)); - } + if (resetMainThreadOnFailure) + { + _legacyThreadingData.MainThreadSubmissionId = -1; + } - submission.CompleteLogging(true); - ReportResultsToSubmission(new BuildResult(submission.BuildRequest, ex)); - _overallBuildSuccess = false; + if (projectException == null) + { + var buildEventContext = new BuildEventContext(submission.SubmissionId, 1, BuildEventContext.InvalidProjectInstanceId, BuildEventContext.InvalidProjectContextId, BuildEventContext.InvalidTargetId, BuildEventContext.InvalidTaskId); + ((IBuildComponentHost)this).LoggingService.LogFatalBuildError(buildEventContext, ex, new BuildEventFileInfo(submission.BuildRequestData.ProjectFullPath)); + } + } + + WaitForAllLoggingServiceEventsToBeProcessed(); + + lock (_syncLock) + { + submission.CompleteLogging(); + ReportResultsToSubmission(new BuildResult(submission.BuildRequest, ex)); + _overallBuildSuccess = false; + } + + } } } @@ -1761,11 +1823,17 @@ private void ExecuteGraphBuildScheduler(GraphBuildSubmission submission) if (submission.BuildRequestData.GraphBuildOptions.Build) { var cacheServiceTask = Task.Run(() => SearchAndInitializeProjectCachePluginFromGraph(projectGraph)); - var targetListTask = Task.Run(() => projectGraph.GetTargetLists(submission.BuildRequestData.TargetNames)); + var targetListTask = projectGraph.GetTargetLists(submission.BuildRequestData.TargetNames); + + DumpGraph(projectGraph, targetListTask); using DisposablePluginService cacheService = cacheServiceTask.Result; - resultsPerNode = BuildGraph(projectGraph, targetListTask.Result, submission.BuildRequestData); + resultsPerNode = BuildGraph(projectGraph, targetListTask, submission.BuildRequestData); + } + else + { + DumpGraph(projectGraph); } ErrorUtilities.VerifyThrow( @@ -1824,7 +1892,22 @@ private void ExecuteGraphBuildScheduler(GraphBuildSubmission submission) ReportResultsToSubmission(result); - _overallBuildSuccess = false; + lock (_syncLock) + { + _overallBuildSuccess = false; + } + } + + static void DumpGraph(ProjectGraph graph, IReadOnlyDictionary> targetList = null) + { + if (Traits.Instance.DebugEngine is false) + { + return; + } + + var logPath = DebugUtils.FindNextAvailableDebugFilePath($"{DebugUtils.ProcessInfoString}_ProjectGraph.dot"); + + File.WriteAllText(logPath, graph.ToDot(targetList)); } } @@ -2016,17 +2099,20 @@ public void Dispose() /// private void ShutdownConnectedNodes(bool abort) { - _shuttingDown = true; + lock (_syncLock) + { + _shuttingDown = true; - // If we are aborting, we will NOT reuse the nodes because their state may be compromised by attempts to shut down while the build is in-progress. - _nodeManager.ShutdownConnectedNodes(!abort && _buildParameters.EnableNodeReuse); + // If we are aborting, we will NOT reuse the nodes because their state may be compromised by attempts to shut down while the build is in-progress. + _nodeManager.ShutdownConnectedNodes(!abort && _buildParameters.EnableNodeReuse); - // if we are aborting, the task host will hear about it in time through the task building infrastructure; - // so only shut down the task host nodes if we're shutting down tidily (in which case, it is assumed that all - // tasks are finished building and thus that there's no risk of a race between the two shutdown pathways). - if (!abort) - { - _taskHostNodeManager.ShutdownConnectedNodes(_buildParameters.EnableNodeReuse); + // if we are aborting, the task host will hear about it in time through the task building infrastructure; + // so only shut down the task host nodes if we're shutting down tidily (in which case, it is assumed that all + // tasks are finished building and thus that there's no risk of a race between the two shutdown pathways). + if (!abort) + { + _taskHostNodeManager.ShutdownConnectedNodes(_buildParameters.EnableNodeReuse); + } } } @@ -2092,7 +2178,6 @@ private void Reset() _workQueue = null; _graphSchedulingCancellationSource = null; _projectCacheService = null; - _projectCacheServiceInstantiatedByVSWorkaround = false; _acquiredProjectRootElementCacheFromProjectInstance = false; _unnamedProjectInstanceToNames.Clear(); @@ -2141,6 +2226,8 @@ private int GetNewConfigurationId() /// private BuildRequestConfiguration ResolveConfiguration(BuildRequestConfiguration unresolvedConfiguration, BuildRequestConfiguration matchingConfigurationFromCache, bool replaceProjectInstance) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + BuildRequestConfiguration resolvedConfiguration = matchingConfigurationFromCache ?? _configCache.GetMatchingConfiguration(unresolvedConfiguration); if (resolvedConfiguration == null) { @@ -2172,12 +2259,16 @@ private BuildRequestConfiguration ResolveConfiguration(BuildRequestConfiguration private void ReplaceExistingProjectInstance(BuildRequestConfiguration newConfiguration, BuildRequestConfiguration existingConfiguration) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + existingConfiguration.Project = newConfiguration.Project; _resultsCache.ClearResultsForConfiguration(existingConfiguration.ConfigurationId); } private BuildRequestConfiguration AddNewConfiguration(BuildRequestConfiguration unresolvedConfiguration) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + var newConfigurationId = _scheduler.GetConfigurationIdFromPlan(unresolvedConfiguration.ProjectFullPath); if (_configCache.HasConfiguration(newConfigurationId) || (newConfigurationId == BuildRequestConfiguration.InvalidConfigurationId)) @@ -2193,6 +2284,67 @@ private BuildRequestConfiguration AddNewConfiguration(BuildRequestConfiguration return newConfiguration; } + internal void PostCacheResult(CacheRequest cacheRequest, CacheResult cacheResult) + { + _workQueue.Post(() => + { + if (cacheResult.Exception is not null) + { + CompleteSubmissionWithException(cacheRequest.Submission, cacheRequest.Configuration, cacheResult.Exception); + return; + } + + HandleCacheResult(); + }); + + void HandleCacheResult() + { + lock (_syncLock) + { + try + { + var submission = cacheRequest.Submission; + var configuration = cacheRequest.Configuration; + + if (cacheResult.ResultType != CacheResultType.CacheHit) + { + // Issue the real build request. + AddBuildRequestToSubmission(submission, configuration.ConfigurationId); + IssueBuildRequestForBuildSubmission(submission, configuration, allowMainThreadBuild: false); + } + else if (cacheResult.ResultType == CacheResultType.CacheHit && cacheResult.ProxyTargets != null) + { + // Setup submission.BuildRequest with proxy targets. The proxy request is built on the inproc node (to avoid + // ProjectInstance serialization). The proxy target results are used as results for the real targets. + AddProxyBuildRequestToSubmission(submission, configuration.ConfigurationId, cacheResult.ProxyTargets); + IssueBuildRequestForBuildSubmission(submission, configuration, allowMainThreadBuild: false); + } + else if (cacheResult.ResultType == CacheResultType.CacheHit && cacheResult.BuildResult != null) + { + // Mark the build submission as complete with the provided results and return. + + // There must be a build request for the results, so fake one. + AddBuildRequestToSubmission(submission, configuration.ConfigurationId); + var result = new BuildResult(submission.BuildRequest); + + foreach (var cacheResult in cacheResult.BuildResult.ResultsByTarget) + { + result.AddResultsForTarget(cacheResult.Key, cacheResult.Value); + } + + _resultsCache.AddResult(result); + submission.CompleteLogging(); + ReportResultsToSubmission(result); + } + } + catch (Exception e) + { + CompleteSubmissionWithException(cacheRequest.Submission, cacheRequest.Configuration, e); + } + } + } + } + /// /// Handles a new request coming from a node. /// @@ -2232,6 +2384,8 @@ private void HandleNewRequest(int node, BuildRequestBlocker blocker) /// private void HandleResourceRequest(int node, ResourceRequest request) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + if (request.IsResourceAcquire) { // Resource request requires a response and may be blocking. Our continuation is effectively a callback @@ -2256,6 +2410,8 @@ private void HandleResourceRequest(int node, ResourceRequest request) /// private void HandleConfigurationRequest(int node, BuildRequestConfiguration unresolvedConfiguration) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + BuildRequestConfiguration resolvedConfiguration = ResolveConfiguration(unresolvedConfiguration, null, false); var response = new BuildRequestConfigurationResponse(unresolvedConfiguration.ConfigurationId, resolvedConfiguration.ConfigurationId, resolvedConfiguration.ResultsNodeId); @@ -2304,6 +2460,8 @@ private void HandleResult(int node, BuildResult result) /// private void HandleNodeShutdown(int node, NodeShutdown shutdownPacket) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + _shuttingDown = true; ErrorUtilities.VerifyThrow(_activeNodes.Contains(node), "Unexpected shutdown from node {0} which shouldn't exist.", node); _activeNodes.Remove(node); @@ -2366,6 +2524,8 @@ private void HandleNodeShutdown(int node, NodeShutdown shutdownPacket) /// private void CheckForActiveNodesAndCleanUpSubmissions() { + Debug.Assert(Monitor.IsEntered(_syncLock)); + if (_activeNodes.Count == 0) { var submissions = new List(_buildSubmissions.Values); @@ -2387,7 +2547,7 @@ private void CheckForActiveNodesAndCleanUpSubmissions() // If we never received a project started event, consider logging complete anyhow, since the nodes have // shut down. - submission.CompleteLogging(waitForLoggingThread: false); + submission.CompleteLogging(); _overallBuildSuccess = _overallBuildSuccess && (submission.BuildResult.OverallResult == BuildResultCode.Success); CheckSubmissionCompletenessAndRemove(submission); @@ -2416,6 +2576,8 @@ private void CheckForActiveNodesAndCleanUpSubmissions() /// private void PerformSchedulingActions(IEnumerable responses) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + foreach (ScheduleResponse response in responses) { switch (response.Action) @@ -2514,7 +2676,7 @@ private void ReportResultsToSubmission(BuildResult result) */ if (!submission.LoggingCompleted && result.Exception != null) { - submission.CompleteLogging(waitForLoggingThread: false); + submission.CompleteLogging(); } submission.CompleteResults(result); @@ -2587,6 +2749,8 @@ private void CheckSubmissionCompletenessAndRemove(GraphBuildSubmission submissio private void CheckAllSubmissionsComplete(BuildRequestDataFlags? flags) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + if (_buildSubmissions.Count == 0 && _graphBuildSubmissions.Count == 0) { if (flags.HasValue && flags.Value.HasFlag(BuildRequestDataFlags.ClearCachesAfterBuild)) @@ -2611,6 +2775,8 @@ private void CheckAllSubmissionsComplete(BuildRequestDataFlags? flags) /// private NodeConfiguration GetNodeConfiguration() { + Debug.Assert(Monitor.IsEntered(_syncLock)); + if (_nodeConfiguration == null) { // Get the remote loggers @@ -2637,7 +2803,7 @@ private NodeConfiguration GetNodeConfiguration() } /// - /// Handler for thread exceptions (logging thread, communications thread). This handler will only get called if the exception did not previously + /// Handler for thread exceptions. This handler will only get called if the exception did not previously /// get handled by a node exception handlers (for instance because the build is complete for the node.) In this case we /// get the exception and will put it into the OverallBuildResult so that the host can see what happened. /// @@ -2667,7 +2833,7 @@ private void OnThreadException(Exception e) { submission.BuildResult.Exception = e; } - submission.CompleteLogging(waitForLoggingThread: false); + submission.CompleteLogging(); submission.CompleteResults(new BuildResult(submission.BuildRequest, e)); CheckSubmissionCompletenessAndRemove(submission); @@ -2694,22 +2860,49 @@ private void OnThreadException(Exception e) } } + /// + /// Handler for LoggingService thread exceptions. + /// + private void OnLoggingThreadException(Exception e) + { + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0)) + { + _workQueue.Post(() => OnThreadException(e)); + } + else + { + OnThreadException(e); + } + } + /// /// Raised when a project finished logging message has been processed. /// private void OnProjectFinished(object sender, ProjectFinishedEventArgs e) { - lock (_syncLock) + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0)) { - if (_projectStartedEvents.TryGetValue(e.BuildEventContext.SubmissionId, out var originalArgs)) + _workQueue.Post(() => OnProjectFinishedBody(e)); + } + else + { + OnProjectFinishedBody(e); + } + + void OnProjectFinishedBody(ProjectFinishedEventArgs e) + { + lock (_syncLock) { - if (originalArgs.BuildEventContext.Equals(e.BuildEventContext)) + if (_projectStartedEvents.TryGetValue(e.BuildEventContext.SubmissionId, out var originalArgs)) { - _projectStartedEvents.Remove(e.BuildEventContext.SubmissionId); - if (_buildSubmissions.TryGetValue(e.BuildEventContext.SubmissionId, out var submission)) + if (originalArgs.BuildEventContext.Equals(e.BuildEventContext)) { - submission.CompleteLogging(false); - CheckSubmissionCompletenessAndRemove(submission); + _projectStartedEvents.Remove(e.BuildEventContext.SubmissionId); + if (_buildSubmissions.TryGetValue(e.BuildEventContext.SubmissionId, out var submission)) + { + submission.CompleteLogging(); + CheckSubmissionCompletenessAndRemove(submission); + } } } } @@ -2721,9 +2914,24 @@ private void OnProjectFinished(object sender, ProjectFinishedEventArgs e) /// private void OnProjectStarted(object sender, ProjectStartedEventArgs e) { - if (!_projectStartedEvents.ContainsKey(e.BuildEventContext.SubmissionId)) + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0)) + { + _workQueue.Post(() => OnProjectStartedBody(e)); + } + else { - _projectStartedEvents[e.BuildEventContext.SubmissionId] = e; + OnProjectStartedBody(e); + } + + void OnProjectStartedBody(ProjectStartedEventArgs e) + { + lock (_syncLock) + { + if (!_projectStartedEvents.ContainsKey(e.BuildEventContext.SubmissionId)) + { + _projectStartedEvents[e.BuildEventContext.SubmissionId] = e; + } + } } } @@ -2732,6 +2940,8 @@ private void OnProjectStarted(object sender, ProjectStartedEventArgs e) /// private ILoggingService CreateLoggingService(IEnumerable loggers, IEnumerable forwardingLoggers, ISet warningsAsErrors, ISet warningsAsMessages) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + int cpuCount = _buildParameters.MaxNodeCount; LoggerMode loggerMode = cpuCount == 1 && _buildParameters.UseSynchronousLogging @@ -2899,6 +3109,8 @@ private void Dispose(bool disposing) private bool ReuseOldCaches(string[] inputCacheFiles) { + Debug.Assert(Monitor.IsEntered(_syncLock)); + ErrorUtilities.VerifyThrowInternalNull(inputCacheFiles, nameof(inputCacheFiles)); ErrorUtilities.VerifyThrow(_configCache == null, "caches must not be set at this point"); ErrorUtilities.VerifyThrow(_resultsCache == null, "caches must not be set at this point"); @@ -2978,6 +3190,8 @@ private void LogErrorAndShutdown(string message) private void CancelAndMarkAsFailure() { + Debug.Assert(Monitor.IsEntered(_syncLock)); + CancelAllSubmissions(); // CancelAllSubmissions also ends up setting _shuttingDown and _overallBuildSuccess but it does so in a separate thread to avoid deadlocks. @@ -2989,7 +3203,7 @@ private void CancelAndMarkAsFailure() /// /// The logger registered to the logging service when no other one is. /// - private class NullLogger : ILogger + internal class NullLogger : ILogger { #region ILogger Members diff --git a/src/Build/BackEnd/BuildManager/BuildParameters.cs b/src/Build/BackEnd/BuildManager/BuildParameters.cs index 1259648e255..93d21956172 100644 --- a/src/Build/BackEnd/BuildManager/BuildParameters.cs +++ b/src/Build/BackEnd/BuildManager/BuildParameters.cs @@ -253,7 +253,7 @@ private BuildParameters(ITranslator translator) /// /// Copy constructor /// - private BuildParameters(BuildParameters other) + internal BuildParameters(BuildParameters other, bool resetEnvironment = false) { ErrorUtilities.VerifyThrowInternalNull(other, nameof(other)); @@ -261,7 +261,11 @@ private BuildParameters(BuildParameters other) _culture = other._culture; _defaultToolsVersion = other._defaultToolsVersion; _enableNodeReuse = other._enableNodeReuse; - _buildProcessEnvironment = other._buildProcessEnvironment != null ? new Dictionary(other._buildProcessEnvironment) : null; + _buildProcessEnvironment = resetEnvironment + ? CommunicationsUtilities.GetEnvironmentVariables() + : other._buildProcessEnvironment != null + ? new Dictionary(other._buildProcessEnvironment) + : null; _environmentProperties = other._environmentProperties != null ? new PropertyDictionary(other._environmentProperties) : null; _forwardingLoggers = other._forwardingLoggers != null ? new List(other._forwardingLoggers) : null; _globalProperties = other._globalProperties != null ? new PropertyDictionary(other._globalProperties) : null; diff --git a/src/Build/BackEnd/BuildManager/BuildSubmission.cs b/src/Build/BackEnd/BuildManager/BuildSubmission.cs index 91356f814c6..5b574b82b20 100644 --- a/src/Build/BackEnd/BuildManager/BuildSubmission.cs +++ b/src/Build/BackEnd/BuildManager/BuildSubmission.cs @@ -165,13 +165,8 @@ internal void CompleteResults(BuildResult result) /// /// Indicates that all logging events for this submission are complete. /// - internal void CompleteLogging(bool waitForLoggingThread) + internal void CompleteLogging() { - if (waitForLoggingThread) - { - ((BackEnd.Logging.LoggingService)((IBuildComponentHost)BuildManager).LoggingService).WaitForThreadToProcessEvents(); - } - LoggingCompleted = true; CheckForCompletion(); } diff --git a/src/Build/BackEnd/BuildManager/CacheSerialization.cs b/src/Build/BackEnd/BuildManager/CacheSerialization.cs index 4ac2ae28805..8bce00b3a26 100644 --- a/src/Build/BackEnd/BuildManager/CacheSerialization.cs +++ b/src/Build/BackEnd/BuildManager/CacheSerialization.cs @@ -79,7 +79,7 @@ public static (IConfigCache ConfigCache, IResultsCache ResultsCache, Exception e using (var fileStream = File.OpenRead(inputCacheFile)) { - var translator = BinaryTranslator.GetReadTranslator(fileStream, null); + using var translator = BinaryTranslator.GetReadTranslator(fileStream, null); translator.Translate(ref configCache); translator.Translate(ref resultsCache); diff --git a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs index 1038643d11f..2dbcd31ea0e 100644 --- a/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs +++ b/src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs @@ -12,6 +12,8 @@ using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Execution; using Microsoft.Build.Shared; +using Microsoft.Build.Shared.Debugging; +using Microsoft.Build.Utilities; using BuildAbortedException = Microsoft.Build.Exceptions.BuildAbortedException; namespace Microsoft.Build.BackEnd @@ -115,8 +117,10 @@ internal class BuildRequestEngine : IBuildRequestEngine, IBuildComponent /// internal BuildRequestEngine() { - _debugDumpState = Environment.GetEnvironmentVariable("MSBUILDDEBUGSCHEDULER") == "1"; - _debugDumpPath = Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); + _debugDumpState = Traits.Instance.DebugScheduler; + _debugDumpPath = ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0) + ? DebugUtils.DebugPath + : Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); _debugForceCaching = Environment.GetEnvironmentVariable("MSBUILDDEBUGFORCECACHING") == "1"; if (String.IsNullOrEmpty(_debugDumpPath)) diff --git a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs index 9dc051c711e..ef21df23454 100644 --- a/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs +++ b/src/Build/BackEnd/Components/Communications/NodeProviderOutOfProcBase.cs @@ -27,7 +27,6 @@ using BackendNativeMethods = Microsoft.Build.BackEnd.NativeMethods; using Task = System.Threading.Tasks.Task; -using DotNetFrameworkArchitecture = Microsoft.Build.Shared.DotNetFrameworkArchitecture; using Microsoft.Build.Framework; using Microsoft.Build.BackEnd.Logging; @@ -394,7 +393,7 @@ private Stream TryConnectToProcess(int nodeProcessId, int timeout, Handshake han CommunicationsUtilities.Trace("Reading handshake from pipe {0}", pipeName); -#if NETCOREAPP2_1 || MONO +#if NETCOREAPP2_1_OR_GREATER || MONO nodeStream.ReadEndOfHandshakeSignal(true, timeout); #else nodeStream.ReadEndOfHandshakeSignal(true); @@ -434,9 +433,9 @@ private Process LaunchNode(string msbuildLocation, string commandLineArgs) // Repeat the executable name as the first token of the command line because the command line // parser logic expects it and will otherwise skip the first argument - commandLineArgs = msbuildLocation + " " + commandLineArgs; + commandLineArgs = $"\"{msbuildLocation}\" {commandLineArgs}"; - BackendNativeMethods.STARTUP_INFO startInfo = new BackendNativeMethods.STARTUP_INFO(); + BackendNativeMethods.STARTUP_INFO startInfo = new(); startInfo.cb = Marshal.SizeOf(); // Null out the process handles so that the parent process does not wait for the child process @@ -466,11 +465,6 @@ private Process LaunchNode(string msbuildLocation, string commandLineArgs) creationFlags |= BackendNativeMethods.CREATE_NEW_CONSOLE; } - BackendNativeMethods.SECURITY_ATTRIBUTES processSecurityAttributes = new BackendNativeMethods.SECURITY_ATTRIBUTES(); - BackendNativeMethods.SECURITY_ATTRIBUTES threadSecurityAttributes = new BackendNativeMethods.SECURITY_ATTRIBUTES(); - processSecurityAttributes.nLength = Marshal.SizeOf(); - threadSecurityAttributes.nLength = Marshal.SizeOf(); - CommunicationsUtilities.Trace("Launching node from {0}", msbuildLocation); string exeName = msbuildLocation; @@ -481,7 +475,6 @@ private Process LaunchNode(string msbuildLocation, string commandLineArgs) { // Run the child process with the same host as the currently-running process. exeName = GetCurrentHost(); - commandLineArgs = "\"" + msbuildLocation + "\" " + commandLineArgs; } #endif @@ -526,14 +519,15 @@ private Process LaunchNode(string msbuildLocation, string commandLineArgs) else { #if RUNTIME_TYPE_NETCORE - if (NativeMethodsShared.IsWindows) - { - // Repeat the executable name in the args to suit CreateProcess - commandLineArgs = "\"" + exeName + "\" " + commandLineArgs; - } + // Repeat the executable name in the args to suit CreateProcess + commandLineArgs = $"\"{exeName}\" {commandLineArgs}"; #endif - BackendNativeMethods.PROCESS_INFORMATION processInfo = new BackendNativeMethods.PROCESS_INFORMATION(); + BackendNativeMethods.PROCESS_INFORMATION processInfo = new(); + BackendNativeMethods.SECURITY_ATTRIBUTES processSecurityAttributes = new(); + BackendNativeMethods.SECURITY_ATTRIBUTES threadSecurityAttributes = new(); + processSecurityAttributes.nLength = Marshal.SizeOf(); + threadSecurityAttributes.nLength = Marshal.SizeOf(); bool result = BackendNativeMethods.CreateProcess ( @@ -596,9 +590,18 @@ private static string GetCurrentHost() #if RUNTIME_TYPE_NETCORE || MONO if (CurrentHost == null) { - using (Process currentProcess = Process.GetCurrentProcess()) + string dotnetExe = Path.Combine(FileUtilities.GetFolderAbove(BuildEnvironmentHelper.Instance.CurrentMSBuildExePath, 2), + NativeMethodsShared.IsWindows ? "dotnet.exe" : "dotnet"); + if (File.Exists(dotnetExe)) + { + CurrentHost = dotnetExe; + } + else { - CurrentHost = currentProcess.MainModule.FileName; + using (Process currentProcess = Process.GetCurrentProcess()) + { + CurrentHost = currentProcess.MainModule.FileName; + } } } diff --git a/src/Build/BackEnd/Components/Communications/TranslatorExtensions.cs b/src/Build/BackEnd/Components/Communications/TranslatorExtensions.cs index 4116039458a..f10fbf8dc50 100644 --- a/src/Build/BackEnd/Components/Communications/TranslatorExtensions.cs +++ b/src/Build/BackEnd/Components/Communications/TranslatorExtensions.cs @@ -4,10 +4,11 @@ using System; using System.Collections.Concurrent; using System.Linq; +using System.Reflection; using Microsoft.Build.Collections; using Microsoft.Build.Execution; +using Microsoft.Build.Framework; using Microsoft.Build.Shared; -using System.Reflection; namespace Microsoft.Build.BackEnd { @@ -103,5 +104,17 @@ public static T FactoryForDeserializingTypeWithName(this ITranslator translat return (T) targetInstanceChild; } + + public static void TranslateOptionalBuildEventContext(this ITranslator translator, ref BuildEventContext buildEventContext) + { + if (translator.Mode == TranslationDirection.ReadFromStream) + { + buildEventContext = translator.Reader.ReadOptionalBuildEventContext(); + } + else + { + translator.Writer.WriteOptionalBuildEventContext(buildEventContext); + } + } } } diff --git a/src/Build/BackEnd/Components/Logging/ILoggingService.cs b/src/Build/BackEnd/Components/Logging/ILoggingService.cs index 7d489ca8b21..096fb99283f 100644 --- a/src/Build/BackEnd/Components/Logging/ILoggingService.cs +++ b/src/Build/BackEnd/Components/Logging/ILoggingService.cs @@ -207,6 +207,15 @@ bool IncludeTaskInputs set; } + /// + /// Returns the minimum logging importance that must be logged because there is a possibility that + /// at least one registered logger consumes it. + /// + MessageImportance MinimumRequiredMessageImportance + { + get; + } + #endregion /// @@ -525,8 +534,10 @@ void LogProjectEvaluationFinished( /// The name of the task /// The project file which is being built /// The file in which the task is defined - typically a .targets file + /// The line number in the file where the task invocation is located. + /// The column number in the file where the task invocation is located. /// The task build event context - BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode); + BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, int line, int column); /// /// Log that a task has just completed diff --git a/src/Build/BackEnd/Components/Logging/LoggingContext.cs b/src/Build/BackEnd/Components/Logging/LoggingContext.cs index 6194726d5f4..5fd43fbbfb7 100644 --- a/src/Build/BackEnd/Components/Logging/LoggingContext.cs +++ b/src/Build/BackEnd/Components/Logging/LoggingContext.cs @@ -213,6 +213,12 @@ internal void LogFatalError(Exception exception, BuildEventFileInfo file, string _hasLoggedErrors = true; } + internal void LogWarning(string messageResourceName, params object[] messageArgs) + { + ErrorUtilities.VerifyThrow(_isValid, "must be valid"); + _loggingService.LogWarning(_eventContext, null, BuildEventFileInfo.Empty, messageResourceName, messageArgs); + } + /// /// Log a warning /// diff --git a/src/Build/BackEnd/Components/Logging/LoggingService.cs b/src/Build/BackEnd/Components/Logging/LoggingService.cs index 7f8aa44f50f..19ed313b922 100644 --- a/src/Build/BackEnd/Components/Logging/LoggingService.cs +++ b/src/Build/BackEnd/Components/Logging/LoggingService.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; @@ -222,6 +223,12 @@ internal partial class LoggingService : ILoggingService, INodePacketHandler, IBu /// private IDictionary> _warningsAsMessagesByProject; + /// + /// The minimum message importance that must be logged because there is a possibility that a logger consumes it. + /// Null means that the optimization is disabled or no relevant logger has been registered. + /// + private MessageImportance? _minimumRequiredMessageImportance; + #region LoggingThread Data /// @@ -514,7 +521,18 @@ public bool IncludeTaskInputs /// public bool IncludeEvaluationPropertiesAndItems { - get => _includeEvaluationPropertiesAndItems ??= _eventSinkDictionary.Values.OfType().Any(sink => sink.IncludeEvaluationPropertiesAndItems); + get + { + if (_includeEvaluationPropertiesAndItems == null) + { + var sinks = _eventSinkDictionary.Values.OfType(); + // .All() on an empty list defaults to true, we want to default to false + _includeEvaluationPropertiesAndItems = sinks.Any() && sinks.All(sink => sink.IncludeEvaluationPropertiesAndItems); + } + + return _includeEvaluationPropertiesAndItems ?? false; + } + set => _includeEvaluationPropertiesAndItems = value; } @@ -691,6 +709,19 @@ public ICollection RegisteredSinkNames } } + /// + /// Returns the minimum logging importance that must be logged because there is a possibility that + /// at least one registered logger consumes it. + /// + public MessageImportance MinimumRequiredMessageImportance + { + get + { + // If we haven't set the field return the default of "all messages must be logged". + return _minimumRequiredMessageImportance ?? MessageImportance.Low; + } + } + #endregion #region Members @@ -1109,7 +1140,7 @@ public void LogBuildEvent(BuildEventArgs buildEvent) #endregion /// - /// This method will becalled from multiple threads in asynchronous mode. + /// This method will be called from multiple threads in asynchronous mode. /// /// Determine where to send the buildevent either to the filters or to a specific sink. /// When in Asynchronous mode the event should to into the logging queue (as long as we are initialized). @@ -1152,8 +1183,29 @@ internal void WaitForThreadToProcessEvents() // shutdown and nulled out the events we were going to wait on. if (_logMode == LoggerMode.Asynchronous && _loggingQueue != null) { - TerminateLoggingEventQueue(); - CreateLoggingEventQueue(); + BufferBlock loggingQueue = null; + ActionBlock loggingQueueProcessor = null; + + lock (_lockObject) + { + loggingQueue = _loggingQueue; + loggingQueueProcessor = _loggingQueueProcessor; + + // Replaces _loggingQueue and _loggingQueueProcessor with new one, this will assure that + // no further messages could possibly be trying to be added into queue we are about to drain + CreateLoggingEventQueue(); + } + + // Drain queue. + // This shall not be locked to avoid possible deadlock caused by + // event handlers to reenter 'this' instance while trying to log something. + if (loggingQueue != null) + { + Debug.Assert(!Monitor.IsEntered(_lockObject)); + + loggingQueue.Complete(); + loggingQueueProcessor.Completion.Wait(); + } } } @@ -1214,21 +1266,27 @@ private void CreateLoggingEventQueue() BoundedCapacity = Convert.ToInt32(_queueCapacity) }; - _loggingQueue = new BufferBlock(dataBlockOptions); + var loggingQueue = new BufferBlock(dataBlockOptions); var executionDataBlockOptions = new ExecutionDataflowBlockOptions { BoundedCapacity = 1 }; - _loggingQueueProcessor = new ActionBlock(loggingEvent => LoggingEventProcessor(loggingEvent), executionDataBlockOptions); + var loggingQueueProcessor = new ActionBlock(loggingEvent => LoggingEventProcessor(loggingEvent), executionDataBlockOptions); var dataLinkOptions = new DataflowLinkOptions { PropagateCompletion = true }; - _loggingQueue.LinkTo(_loggingQueueProcessor, dataLinkOptions); + loggingQueue.LinkTo(loggingQueueProcessor, dataLinkOptions); + + lock (_lockObject) + { + _loggingQueue = loggingQueue; + _loggingQueueProcessor = loggingQueueProcessor; + } } /// @@ -1542,10 +1600,57 @@ private void InitializeLogger(ILogger logger, IEventSource sourceForLogger) InternalLoggerException.Throw(e, null, "FatalErrorWhileInitializingLogger", true, logger.GetType().Name); } + // Update the minimum guaranteed message importance based on the newly added logger. + UpdateMinimumMessageImportance(logger); + // Keep track of the loggers so they can be unregistered later on _loggers.Add(logger); } + /// + /// Updates based on the given . + /// + /// The newly registered logger. + /// + /// This method contains knowledge about several logger classes used by MSBuild. The goal is to optimize common scenarios, + /// such as building on the command line with normal or minimum verbosity. If the user registers an external custom logger, + /// we will fall back to "minimum importance" == Low because we don't know how the logger processes messages, therefore we + /// must feed it everything. + /// + private void UpdateMinimumMessageImportance(ILogger logger) + { + var innerLogger = (logger is Evaluation.ProjectCollection.ReusableLogger reusableLogger) ? reusableLogger.OriginalLogger : logger; + + MessageImportance? minimumImportance = innerLogger switch + { + Build.Logging.ConsoleLogger consoleLogger => consoleLogger.GetMinimumMessageImportance(), + Build.Logging.ConfigurableForwardingLogger forwardingLogger => forwardingLogger.GetMinimumMessageImportance(), + + // Central forwarding loggers are used in worker nodes if logging verbosity could not be optimized, i.e. in cases + // where we must log everything. They can be ignored in inproc nodes. + CentralForwardingLogger => (_nodeId > 1 ? MessageImportance.Low : null), + + // The null logger has no effect on minimum verbosity. + Execution.BuildManager.NullLogger => null, + + // If the logger is not on our whitelist, there are no importance guarantees. Fall back to "any importance". + _ => MessageImportance.Low + }; + + if (minimumImportance != null) + { + if (_minimumRequiredMessageImportance == null) + { + _minimumRequiredMessageImportance = minimumImportance; + } + else + { + int newMinImportance = Math.Max((int)_minimumRequiredMessageImportance, (int)minimumImportance); + _minimumRequiredMessageImportance = (MessageImportance)newMinImportance; + } + } + } + /// /// When an exception is raised in the logging thread, we do not want the application to terminate right away. /// Whidbey and orcas msbuild have the logger exceptions occurring on the engine thread so that the host can @@ -1598,8 +1703,7 @@ private void TryRaiseProjectFinishedEvent(BuildEventArgs args) /// private string GetAndVerifyProjectFileFromContext(BuildEventContext context) { - string projectFile; - _projectFileMap.TryGetValue(context.ProjectContextId, out projectFile); + _projectFileMap.TryGetValue(context.ProjectContextId, out string projectFile); // PERF: Not using VerifyThrow to avoid boxing an int in the non-error case. if (projectFile == null) diff --git a/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs b/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs index 77de428ef63..c5c3555f201 100644 --- a/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs +++ b/src/Build/BackEnd/Components/Logging/LoggingServiceLogMethods.cs @@ -450,13 +450,10 @@ public void LogBuildStarted() // Raise the event with the filters ProcessLoggingEvent(buildEvent); - - // Make sure we process this event before going any further - if (_logMode == LoggerMode.Asynchronous) - { - WaitForThreadToProcessEvents(); - } } + + // Make sure we process this event before going any further + WaitForThreadToProcessEvents(); } /// @@ -478,12 +475,10 @@ public void LogBuildFinished(bool success) BuildFinishedEventArgs buildEvent = new BuildFinishedEventArgs(message, null /* no help keyword */, success); ProcessLoggingEvent(buildEvent); - - if (_logMode == LoggerMode.Asynchronous) - { - WaitForThreadToProcessEvents(); - } } + + // Make sure we process this event before going any further + WaitForThreadToProcessEvents(); } /// @@ -754,9 +749,11 @@ public void LogTaskStarted(BuildEventContext taskBuildEventContext, string taskN /// Task Name /// Project file being built /// Project file which contains the task + /// The line number in the file where the task invocation is located. + /// The column number in the file where the task invocation is located. /// The build event context for the task. /// BuildEventContext is null - public BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode) + public BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventContext, string taskName, string projectFile, string projectFileOfTaskNode, int line, int column) { lock (_lockObject) { @@ -782,6 +779,8 @@ public BuildEventContext LogTaskStarted2(BuildEventContext targetBuildEventConte taskName ); buildEvent.BuildEventContext = taskBuildEventContext; + buildEvent.LineNumber = line; + buildEvent.ColumnNumber = column; ProcessLoggingEvent(buildEvent); } diff --git a/src/Build/BackEnd/Components/Logging/NodeLoggingContext.cs b/src/Build/BackEnd/Components/Logging/NodeLoggingContext.cs index 0b55b80359a..6852343e9be 100644 --- a/src/Build/BackEnd/Components/Logging/NodeLoggingContext.cs +++ b/src/Build/BackEnd/Components/Logging/NodeLoggingContext.cs @@ -86,18 +86,26 @@ internal void LogRequestHandledFromCache(BuildRequest request, BuildRequestConfi { ProjectLoggingContext projectLoggingContext = LogProjectStarted(request, configuration); - // When pulling a request from the cache, we want to make sure we log a task skipped message for any targets which - // were used to build the request including default and inital targets. + // When pulling a request from the cache, we want to make sure we log a target skipped event for any targets which + // were used to build the request including default and initial targets. foreach (string target in configuration.GetTargetsUsedToBuildRequest(request)) { - projectLoggingContext.LogComment - ( - MessageImportance.Low, - result[target].ResultCode == TargetResultCode.Failure ? "TargetAlreadyCompleteFailure" : "TargetAlreadyCompleteSuccess", - target - ); - - if (result[target].ResultCode == TargetResultCode.Failure) + var targetResult = result[target]; + bool isFailure = targetResult.ResultCode == TargetResultCode.Failure; + + var skippedTargetEventArgs = new TargetSkippedEventArgs(message: null) + { + BuildEventContext = projectLoggingContext.BuildEventContext, + TargetName = target, + BuildReason = TargetBuiltReason.None, + SkipReason = isFailure ? TargetSkipReason.PreviouslyBuiltUnsuccessfully : TargetSkipReason.PreviouslyBuiltSuccessfully, + OriginallySucceeded = !isFailure, + OriginalBuildEventContext = (targetResult as TargetResult)?.OriginalBuildEventContext + }; + + projectLoggingContext.LogBuildEvent(skippedTargetEventArgs); + + if (targetResult.ResultCode == TargetResultCode.Failure) { break; } diff --git a/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs b/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs index 846c0b3a643..4c5e5934c83 100644 --- a/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs +++ b/src/Build/BackEnd/Components/Logging/ProjectLoggingContext.cs @@ -135,7 +135,12 @@ private ProjectLoggingContext(NodeLoggingContext nodeLoggingContext, int submiss properties, items, evaluationId); - LoggingService.LogComment(this.BuildEventContext, MessageImportance.Low, "ToolsVersionInEffectForBuild", toolsVersion); + + // No need to log a redundant message in the common case + if (toolsVersion != "Current") + { + LoggingService.LogComment(this.BuildEventContext, MessageImportance.Low, "ToolsVersionInEffectForBuild", toolsVersion); + } this.IsValid = true; } diff --git a/src/Build/BackEnd/Components/Logging/TaskLoggingContext.cs b/src/Build/BackEnd/Components/Logging/TaskLoggingContext.cs index 6dc62c389ce..51c266e6485 100644 --- a/src/Build/BackEnd/Components/Logging/TaskLoggingContext.cs +++ b/src/Build/BackEnd/Components/Logging/TaskLoggingContext.cs @@ -69,7 +69,9 @@ internal TaskLoggingContext(TargetLoggingContext targetLoggingContext, string pr targetLoggingContext.BuildEventContext, _taskName, projectFullPath, - task.Location.File + task.Location.File, + task.Location.Line, + task.Location.Column ); this.IsValid = true; } diff --git a/src/Build/BackEnd/Components/ProjectCache/CacheResult.cs b/src/Build/BackEnd/Components/ProjectCache/CacheResult.cs index 7f8a69ecbbd..50ac573159f 100644 --- a/src/Build/BackEnd/Components/ProjectCache/CacheResult.cs +++ b/src/Build/BackEnd/Components/ProjectCache/CacheResult.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. #nullable enable +using System; using System.Collections.Generic; using System.Linq; using Microsoft.Build.BackEnd; @@ -46,6 +47,11 @@ public enum CacheResultType /// public class CacheResult { + public CacheResultType ResultType { get; } + public BuildResult? BuildResult { get; } + public ProxyTargets? ProxyTargets { get; } + internal Exception? Exception { get; } + private CacheResult( CacheResultType resultType, BuildResult? buildResult = null, @@ -63,9 +69,11 @@ private CacheResult( ProxyTargets = proxyTargets; } - public CacheResultType ResultType { get; } - public BuildResult? BuildResult { get; } - public ProxyTargets? ProxyTargets { get; } + private CacheResult(Exception exception) + { + ResultType = CacheResultType.None; + Exception = exception; + } public static CacheResult IndicateCacheHit(BuildResult buildResult) { @@ -90,6 +98,11 @@ public static CacheResult IndicateNonCacheHit(CacheResultType resultType) return new CacheResult(resultType); } + internal static CacheResult IndicateException(Exception e) + { + return new CacheResult(e); + } + private static BuildResult ConstructBuildResult(IReadOnlyCollection targetResults) { var buildResult = new BuildResult(); diff --git a/src/Build/BackEnd/Components/ProjectCache/ProjectCacheDescriptor.cs b/src/Build/BackEnd/Components/ProjectCache/ProjectCacheDescriptor.cs index 22d98780b2b..10c97621ef5 100644 --- a/src/Build/BackEnd/Components/ProjectCache/ProjectCacheDescriptor.cs +++ b/src/Build/BackEnd/Components/ProjectCache/ProjectCacheDescriptor.cs @@ -30,6 +30,8 @@ public class ProjectCacheDescriptor public ProjectCachePluginBase? PluginInstance { get; } + internal bool VsWorkaround { get; } + private ProjectCacheDescriptor( IReadOnlyCollection? entryPoints, ProjectGraph? projectGraph, @@ -62,6 +64,19 @@ private ProjectCacheDescriptor( PluginInstance = pluginInstance; } + private ProjectCacheDescriptor(ProjectCacheItem projectCacheItem) + { + VsWorkaround = true; + PluginAssemblyPath = projectCacheItem.PluginPath; + PluginSettings = projectCacheItem.PluginSettings; + } + + // TODO: remove after we change VS to set the cache descriptor via build parameters. + internal static ProjectCacheDescriptor FromVisualStudioWorkaround(ProjectCacheItem projectCacheItem) + { + return new ProjectCacheDescriptor(projectCacheItem); + } + public static ProjectCacheDescriptor FromAssemblyPath( string pluginAssemblyPath, IReadOnlyCollection? entryPoints, @@ -87,18 +102,22 @@ public string GetDetailedDescription() : $"Assembly path based: {PluginAssemblyPath}"; var entryPointStyle = EntryPoints != null - ? "Graph entrypoint based" - : "Static graph based"; + ? "Explicit entry-point based" + : ProjectGraph != null + ? "Static graph based" + : "Visual Studio Workaround based"; var entryPoints = EntryPoints != null ? string.Join( "\n", EntryPoints.Select(e => $"{e.ProjectFile} {{{FormatGlobalProperties(e.GlobalProperties)}}}")) - : string.Join( - "\n", - ProjectGraph!.EntryPointNodes.Select( - n => - $"{n.ProjectInstance.FullPath} {{{FormatGlobalProperties(n.ProjectInstance.GlobalProperties)}}}")); + : ProjectGraph != null + ? string.Join( + "\n", + ProjectGraph!.EntryPointNodes.Select( + n => + $"{n.ProjectInstance.FullPath} {{{FormatGlobalProperties(n.ProjectInstance.GlobalProperties)}}}")) + : "Solution file"; return $"{loadStyle}\nEntry-point style: {entryPointStyle}\nEntry-points:\n{entryPoints}"; diff --git a/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs b/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs index ad7259723ce..42e912b95ae 100644 --- a/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs +++ b/src/Build/BackEnd/Components/ProjectCache/ProjectCacheService.cs @@ -4,19 +4,42 @@ #nullable enable using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using System.Xml; +using Microsoft.Build.BackEnd; using Microsoft.Build.BackEnd.Logging; +using Microsoft.Build.Construction; using Microsoft.Build.Execution; using Microsoft.Build.FileSystem; using Microsoft.Build.Framework; +using Microsoft.Build.Graph; +using Microsoft.Build.Internal; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; namespace Microsoft.Build.Experimental.ProjectCache { + internal record CacheRequest(BuildSubmission Submission, BuildRequestConfiguration Configuration); + + internal record NullableBool(bool Value) + { + public static implicit operator bool(NullableBool? d) => d is not null && d.Value; + } + + internal enum ProjectCacheServiceState + { + NotInitialized, + BeginBuildStarted, + BeginBuildFinished, + ShutdownStarted, + ShutdownFinished + } + internal class ProjectCacheService { private readonly BuildManager _buildManager; @@ -24,6 +47,20 @@ internal class ProjectCacheService private readonly ProjectCacheDescriptor _projectCacheDescriptor; private readonly CancellationToken _cancellationToken; private readonly ProjectCachePluginBase _projectCachePlugin; + private ProjectCacheServiceState _serviceState = ProjectCacheServiceState.NotInitialized; + + /// + /// An instanatiable version of MSBuildFileSystemBase not overriding any methods, + /// i.e. falling back to FileSystem.Default. + /// + private sealed class DefaultMSBuildFileSystem : MSBuildFileSystemBase { } + + // Use NullableBool to make it work with Interlock.CompareExchange (doesn't accept bool?). + // Assume that if one request is a design time build, all of them are. + // Volatile because it is read by the BuildManager thread and written by one project cache service thread pool thread. + // TODO: remove after we change VS to set the cache descriptor via build parameters. + public volatile NullableBool? DesignTimeBuildsDetected; + private TaskCompletionSource? LateInitializationForVSWorkaroundCompleted; private ProjectCacheService( ProjectCachePluginBase projectCachePlugin, @@ -49,22 +86,55 @@ public static async Task FromDescriptorAsync( var plugin = await Task.Run(() => GetPluginInstance(pluginDescriptor), cancellationToken) .ConfigureAwait(false); - // TODO: Detect and use the highest verbosity from all the user defined loggers. That's tricky because right now we can't discern between user set loggers and msbuild's internally added loggers. + // TODO: Detect and use the highest verbosity from all the user defined loggers. That's tricky because right now we can't query loggers about + // their verbosity levels. var loggerFactory = new Func(() => new LoggingServiceToPluginLoggerAdapter(LoggerVerbosity.Normal, loggingService)); - var logger = loggerFactory(); + var service = new ProjectCacheService(plugin, buildManager, loggerFactory, pluginDescriptor, cancellationToken); + + // TODO: remove the if after we change VS to set the cache descriptor via build parameters and always call BeginBuildAsync in FromDescriptorAsync. + // When running under VS we can't initialize the plugin until we evaluate a project (any project) and extract + // further information (set by VS) from it required by the plugin. + if (!pluginDescriptor.VsWorkaround) + { + await service.BeginBuildAsync(); + } + + return service; + } + + // TODO: remove vsWorkaroundOverrideDescriptor after we change VS to set the cache descriptor via build parameters. + private async Task BeginBuildAsync(ProjectCacheDescriptor? vsWorkaroundOverrideDescriptor = null) + { + var logger = _loggerFactory(); try { - await plugin.BeginBuildAsync( + SetState(ProjectCacheServiceState.BeginBuildStarted); + + logger.LogMessage("Initializing project cache plugin", MessageImportance.Low); + var timer = Stopwatch.StartNew(); + + if (_projectCacheDescriptor.VsWorkaround) + { + logger.LogMessage("Running project cache with Visual Studio workaround"); + } + + var projectDescriptor = vsWorkaroundOverrideDescriptor ?? _projectCacheDescriptor; + await _projectCachePlugin.BeginBuildAsync( new CacheContext( - pluginDescriptor.PluginSettings, - new IFileSystemAdapter(FileSystems.Default), - pluginDescriptor.ProjectGraph, - pluginDescriptor.EntryPoints), + projectDescriptor.PluginSettings, + new DefaultMSBuildFileSystem(), + projectDescriptor.ProjectGraph, + projectDescriptor.EntryPoints), // TODO: Detect verbosity from logging service. logger, - cancellationToken); + _cancellationToken); + + timer.Stop(); + logger.LogMessage($"Finished initializing project cache plugin in {timer.Elapsed.TotalMilliseconds} ms", MessageImportance.Low); + + SetState(ProjectCacheServiceState.BeginBuildFinished); } catch (Exception e) { @@ -75,8 +145,6 @@ await plugin.BeginBuildAsync( { ProjectCacheException.ThrowForErrorLoggedInsideTheProjectCache("ProjectCacheInitializationFailed"); } - - return new ProjectCacheService(plugin, buildManager, loggerFactory, pluginDescriptor, cancellationToken); } private static ProjectCachePluginBase GetPluginInstance(ProjectCacheDescriptor pluginDescriptor) @@ -127,7 +195,7 @@ Assembly LoadAssembly(string resolverPath) #if !FEATURE_ASSEMBLYLOADCONTEXT return Assembly.LoadFrom(resolverPath); #else - return _loader.LoadFromPath(resolverPath); + return s_loader.LoadFromPath(resolverPath); #endif } @@ -145,12 +213,259 @@ IEnumerable GetTypes(Assembly assembly) } #if FEATURE_ASSEMBLYLOADCONTEXT - private static readonly CoreClrAssemblyLoader _loader = new CoreClrAssemblyLoader(); + private static readonly CoreClrAssemblyLoader s_loader = new CoreClrAssemblyLoader(); #endif - public async Task GetCacheResultAsync(BuildRequestData buildRequest) + public void PostCacheRequest(CacheRequest cacheRequest) + { + Task.Run(async () => + { + try + { + var cacheResult = await ProcessCacheRequest(cacheRequest); + _buildManager.PostCacheResult(cacheRequest, cacheResult); + } + catch (Exception e) + { + _buildManager.PostCacheResult(cacheRequest, CacheResult.IndicateException(e)); + } + }, _cancellationToken); + + async Task ProcessCacheRequest(CacheRequest request) + { + // Prevent needless evaluation if design time builds detected. + if (_projectCacheDescriptor.VsWorkaround && DesignTimeBuildsDetected) + { + // The BuildManager should disable the cache when it finds its servicing design time builds. + return CacheResult.IndicateNonCacheHit(CacheResultType.CacheMiss); + } + + EvaluateProjectIfNecessary(request); + + // Detect design time builds. + if (_projectCacheDescriptor.VsWorkaround) + { + var isDesignTimeBuild = IsDesignTimeBuild(request.Configuration.Project); + + var previousValue = Interlocked.CompareExchange( + ref DesignTimeBuildsDetected, + new NullableBool(isDesignTimeBuild), + null); + + ErrorUtilities.VerifyThrowInternalError( + previousValue is null || previousValue == false || isDesignTimeBuild, + "Either all builds in a build session or design time builds, or none"); + + // No point progressing with expensive plugin initialization or cache query if design time build detected. + if (DesignTimeBuildsDetected) + { + // The BuildManager should disable the cache when it finds its servicing design time builds. + return CacheResult.IndicateNonCacheHit(CacheResultType.CacheMiss); + } + } + + // TODO: remove after we change VS to set the cache descriptor via build parameters. + // VS workaround needs to wait until the first project is evaluated to extract enough information to initialize the plugin. + // No cache request can progress until late initialization is complete. + if (_projectCacheDescriptor.VsWorkaround) + { + if (Interlocked.CompareExchange( + ref LateInitializationForVSWorkaroundCompleted, + new TaskCompletionSource(), + null) is null) + { + await LateInitializePluginForVsWorkaround(request); + LateInitializationForVSWorkaroundCompleted.SetResult(true); + } + else + { + // Can't be null. If the thread got here it means another thread initialized the completion source. + await LateInitializationForVSWorkaroundCompleted!.Task; + } + } + + ErrorUtilities.VerifyThrowInternalError( + LateInitializationForVSWorkaroundCompleted is null || + _projectCacheDescriptor.VsWorkaround && LateInitializationForVSWorkaroundCompleted.Task.IsCompleted, + "Completion source should be null when this is not the VS workaround"); + + return await GetCacheResultAsync( + new BuildRequestData( + request.Configuration.Project, + request.Submission.BuildRequestData.TargetNames.ToArray())); + } + + static bool IsDesignTimeBuild(ProjectInstance project) + { + var designTimeBuild = project.GetPropertyValue(DesignTimeProperties.DesignTimeBuild); + var buildingProject = project.GlobalPropertiesDictionary[DesignTimeProperties.BuildingProject]?.EvaluatedValue; + + return MSBuildStringIsTrue(designTimeBuild) || + buildingProject != null && !MSBuildStringIsTrue(buildingProject); + } + + void EvaluateProjectIfNecessary(CacheRequest request) + { + // TODO: only do this if the project cache requests evaluation. QB needs evaluations, but the Anybuild implementation + // TODO: might not need them, so no point evaluating if it's not necessary. As a caveat, evaluations would still be optimal + // TODO: when proxy builds are issued by the plugin ( scheduled on the inproc node, no point re-evaluating on out-of-proc nodes). + lock (request.Configuration) + { + if (!request.Configuration.IsLoaded) + { + request.Configuration.LoadProjectIntoConfiguration( + _buildManager, + request.Submission.BuildRequestData.Flags, + request.Submission.SubmissionId, + Scheduler.InProcNodeId + ); + + // If we're taking the time to evaluate, avoid having other nodes to repeat the same evaluation. + // Based on the assumption that ProjectInstance serialization is faster than evaluating from scratch. + request.Configuration.Project.TranslateEntireState = true; + } + } + } + + async Task LateInitializePluginForVsWorkaround(CacheRequest request) + { + var (_, configuration) = request; + var solutionPath = configuration.Project.GetPropertyValue(SolutionProjectGenerator.SolutionPathPropertyName); + var solutionConfigurationXml = configuration.Project.GetPropertyValue(SolutionProjectGenerator.CurrentSolutionConfigurationContents); + + ErrorUtilities.VerifyThrow( + solutionPath != null && !string.IsNullOrWhiteSpace(solutionPath) && solutionPath != "*Undefined*", + $"Expected VS to set a valid SolutionPath property but got: {solutionPath}"); + + ErrorUtilities.VerifyThrow( + FileSystems.Default.FileExists(solutionPath), + $"Solution file does not exist: {solutionPath}"); + + ErrorUtilities.VerifyThrow( + string.IsNullOrWhiteSpace(solutionConfigurationXml) is false, + "Expected VS to set a xml with all the solution projects' configurations for the currently building solution configuration."); + + // A solution supports multiple solution configurations (different values for Configuration and Platform). + // Each solution configuration generates a different static graph. + // Therefore, plugin implementations that rely on creating static graphs in their BeginBuild methods need access to the + // currently solution configuration used by VS. + // + // In this VS workaround, however, we do not have access to VS' solution configuration. The only information we have is a global property + // named "CurrentSolutionConfigurationContents" that VS sets on each built project. It does not contain the solution configuration itself, but + // instead it contains information on how the solution configuration maps to each project's configuration. + // + // So instead of using the solution file as the entry point, we parse this VS property and extract graph entry points from it, for every project + // mentioned in the "CurrentSolutionConfigurationContents" global property. + // + // Ideally, when the VS workaround is removed from MSBuild and moved into VS, VS should create ProjectGraphDescriptors with the solution path as + // the graph entrypoint file, and the VS solution configuration as the entry point's global properties. + var graphEntryPointsFromSolutionConfig = GenerateGraphEntryPointsFromSolutionConfigurationXml( + solutionConfigurationXml, + configuration.Project); + + await BeginBuildAsync( + ProjectCacheDescriptor.FromAssemblyPath( + _projectCacheDescriptor.PluginAssemblyPath!, + graphEntryPointsFromSolutionConfig, + projectGraph: null, + _projectCacheDescriptor.PluginSettings)); + } + + static IReadOnlyCollection GenerateGraphEntryPointsFromSolutionConfigurationXml( + string solutionConfigurationXml, + ProjectInstance project + ) + { + // TODO: fix code clone for parsing CurrentSolutionConfiguration xml: https://github.com/dotnet/msbuild/issues/6751 + var doc = new XmlDocument(); + doc.LoadXml(solutionConfigurationXml); + + var root = doc.DocumentElement!; + var projectConfigurationNodes = root.GetElementsByTagName("ProjectConfiguration"); + + ErrorUtilities.VerifyThrow(projectConfigurationNodes.Count > 0, "Expected at least one project in solution"); + + var definingProjectPath = project.FullPath; + var graphEntryPoints = new List(projectConfigurationNodes.Count); + + var templateGlobalProperties = new Dictionary(project.GlobalProperties, StringComparer.OrdinalIgnoreCase); + RemoveProjectSpecificGlobalProperties(templateGlobalProperties, project); + + foreach (XmlNode node in projectConfigurationNodes) + { + ErrorUtilities.VerifyThrowInternalNull(node.Attributes, nameof(node.Attributes)); + + var buildProjectInSolution = node.Attributes!["BuildProjectInSolution"]; + if (buildProjectInSolution is not null && + string.IsNullOrWhiteSpace(buildProjectInSolution.Value) is false && + bool.TryParse(buildProjectInSolution.Value, out var buildProject) && + buildProject is false) + { + continue; + } + + ErrorUtilities.VerifyThrow( + node.ChildNodes.OfType().FirstOrDefault(e => e.Name == "ProjectDependency") is null, + "Project cache service does not support solution only dependencies when running under Visual Studio."); + + var projectPathAttribute = node.Attributes!["AbsolutePath"]; + ErrorUtilities.VerifyThrow(projectPathAttribute is not null, "Expected VS to set the project path on each ProjectConfiguration element."); + + var projectPath = projectPathAttribute!.Value; + + var (configuration, platform) = SolutionFile.ParseConfigurationName(node.InnerText, definingProjectPath, 0, solutionConfigurationXml); + + // Take the defining project global properties and override the configuration and platform. + // It's sufficient to only set Configuration and Platform. + // But we send everything to maximize the plugins' potential to quickly workaround potential MSBuild issues while the MSBuild fixes flow into VS. + var globalProperties = new Dictionary(templateGlobalProperties, StringComparer.OrdinalIgnoreCase) + { + ["Configuration"] = configuration, + ["Platform"] = platform + }; + + graphEntryPoints.Add(new ProjectGraphEntryPoint(projectPath, globalProperties)); + } + + return graphEntryPoints; + + // If any project specific property is set, it will propagate down the project graph and force all nodes to that property's specific side effects, which is incorrect. + void RemoveProjectSpecificGlobalProperties(Dictionary globalProperties, ProjectInstance project) + { + // InnerBuildPropertyName is TargetFramework for the managed sdk. + var innerBuildPropertyName = ProjectInterpretation.GetInnerBuildPropertyName(project); + + IEnumerable projectSpecificPropertyNames = new []{innerBuildPropertyName, "Configuration", "Platform", "TargetPlatform", "OutputType"}; + + foreach (var propertyName in projectSpecificPropertyNames) + { + if (!string.IsNullOrWhiteSpace(propertyName) && globalProperties.ContainsKey(propertyName)) + { + globalProperties.Remove(propertyName); + } + } + } + } + + static bool MSBuildStringIsTrue(string msbuildString) => + ConversionUtilities.ConvertStringToBool(msbuildString, nullOrWhitespaceIsFalse: true); + } + + private async Task GetCacheResultAsync(BuildRequestData buildRequest) { - // TODO: Parent these logs under the project build event so they appear nested under the project in the binlog viewer. + lock (this) + { + CheckNotInState(ProjectCacheServiceState.NotInitialized); + CheckNotInState(ProjectCacheServiceState.BeginBuildStarted); + + if (_serviceState is ProjectCacheServiceState.ShutdownStarted or ProjectCacheServiceState.ShutdownFinished) + { + return CacheResult.IndicateNonCacheHit(CacheResultType.CacheNotApplicable); + } + } + + ErrorUtilities.VerifyThrowInternalNull(buildRequest.ProjectInstance, nameof(buildRequest.ProjectInstance)); + var queryDescription = $"{buildRequest.ProjectFullPath}" + $"\n\tTargets:[{string.Join(", ", buildRequest.TargetNames)}]" + $"\n\tGlobal Properties: {{{string.Join(",", buildRequest.GlobalProperties.Select(kvp => $"{kvp.Name}={kvp.EvaluatedValue}"))}}}"; @@ -176,7 +491,7 @@ public async Task GetCacheResultAsync(BuildRequestData buildRequest ProjectCacheException.ThrowForErrorLoggedInsideTheProjectCache("ProjectCacheQueryFailed", queryDescription); } - var message = $"Plugin result: {cacheResult.ResultType}."; + var message = $"------ Plugin result: {cacheResult.ResultType}."; switch (cacheResult.ResultType) { @@ -206,16 +521,28 @@ public async Task ShutDown() try { + SetState(ProjectCacheServiceState.ShutdownStarted); + + logger.LogMessage("Shutting down project cache plugin", MessageImportance.Low); + var timer = Stopwatch.StartNew(); + await _projectCachePlugin.EndBuildAsync(logger, _cancellationToken); + + timer.Stop(); + logger.LogMessage($"Finished shutting down project cache plugin in {timer.Elapsed.TotalMilliseconds} ms", MessageImportance.Low); + + if (logger.HasLoggedErrors) + { + ProjectCacheException.ThrowForErrorLoggedInsideTheProjectCache("ProjectCacheShutdownFailed"); + } } - catch (Exception e) + catch (Exception e) when (e is not ProjectCacheException) { HandlePluginException(e, nameof(ProjectCachePluginBase.EndBuildAsync)); } - - if (logger.HasLoggedErrors) + finally { - ProjectCacheException.ThrowForErrorLoggedInsideTheProjectCache("ProjectCacheShutdownFailed"); + SetState(ProjectCacheServiceState.ShutdownFinished); } } @@ -232,6 +559,52 @@ private static void HandlePluginException(Exception e, string apiExceptionWasThr apiExceptionWasThrownFrom); } + private void SetState(ProjectCacheServiceState newState) + { + lock (this) + { + switch (newState) + { + case ProjectCacheServiceState.NotInitialized: + ErrorUtilities.ThrowInternalError($"Cannot transition to {ProjectCacheServiceState.NotInitialized}"); + break; + case ProjectCacheServiceState.BeginBuildStarted: + CheckInState(ProjectCacheServiceState.NotInitialized); + break; + case ProjectCacheServiceState.BeginBuildFinished: + CheckInState(ProjectCacheServiceState.BeginBuildStarted); + break; + case ProjectCacheServiceState.ShutdownStarted: + CheckNotInState(ProjectCacheServiceState.ShutdownStarted); + CheckNotInState(ProjectCacheServiceState.ShutdownFinished); + break; + case ProjectCacheServiceState.ShutdownFinished: + CheckInState(ProjectCacheServiceState.ShutdownStarted); + break; + default: + throw new ArgumentOutOfRangeException(nameof(newState), newState, null); + } + + _serviceState = newState; + } + } + + private void CheckInState(ProjectCacheServiceState expectedState) + { + lock (this) + { + ErrorUtilities.VerifyThrowInternalError(_serviceState == expectedState, $"Expected state {expectedState}, actual state {_serviceState}"); + } + } + + private void CheckNotInState(ProjectCacheServiceState unexpectedState) + { + lock (this) + { + ErrorUtilities.VerifyThrowInternalError(_serviceState != unexpectedState, $"Unexpected state {_serviceState}"); + } + } + private class LoggingServiceToPluginLoggerAdapter : PluginLoggerBase { private readonly ILoggingService _loggingService; diff --git a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs index 66195775b22..109cb49b9bf 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupIntrinsicTask.cs @@ -218,7 +218,8 @@ private void ExecuteAdd(ProjectItemGroupTaskItemInstance child, ItemBucket bucke TaskParameterMessageKind.AddItem, child.ItemType, itemsToAdd, - logItemMetadata: true); + logItemMetadata: true, + child.Location); } // Now add the items we created to the lookup. @@ -261,7 +262,8 @@ private void ExecuteRemove(ProjectItemGroupTaskItemInstance child, ItemBucket bu TaskParameterMessageKind.RemoveItem, child.ItemType, itemsToRemove, - logItemMetadata: true); + logItemMetadata: true, + child.Location); } bucket.Lookup.RemoveItems(itemsToRemove); diff --git a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupLoggingHelper.cs b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupLoggingHelper.cs index 4f7440b61e3..b672732ee39 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupLoggingHelper.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/IntrinsicTasks/ItemGroupLoggingHelper.cs @@ -252,7 +252,8 @@ internal static void LogTaskParameter( TaskParameterMessageKind messageKind, string itemType, IList items, - bool logItemMetadata) + bool logItemMetadata, + IElementLocation location = null) { var args = CreateTaskParameterEventArgs( loggingContext.BuildEventContext, @@ -260,7 +261,10 @@ internal static void LogTaskParameter( itemType, items, logItemMetadata, - DateTime.UtcNow); + DateTime.UtcNow, + location?.Line ?? 0, + location?.Column ?? 0); + loggingContext.LogBuildEvent(args); } @@ -270,7 +274,9 @@ internal static TaskParameterEventArgs CreateTaskParameterEventArgs( string itemType, IList items, bool logItemMetadata, - DateTime timestamp) + DateTime timestamp, + int line = 0, + int column = 0) { // Only create a snapshot of items if we use AppDomains #if FEATURE_APPDOMAIN @@ -284,6 +290,8 @@ internal static TaskParameterEventArgs CreateTaskParameterEventArgs( logItemMetadata, timestamp); args.BuildEventContext = buildEventContext; + args.LineNumber = line; + args.ColumnNumber = column; return args; } diff --git a/src/Build/BackEnd/Components/RequestBuilder/TargetBuilder.cs b/src/Build/BackEnd/Components/RequestBuilder/TargetBuilder.cs index 21bdf35cb01..6b4b24dfa81 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TargetBuilder.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TargetBuilder.cs @@ -555,6 +555,7 @@ private bool CheckSkipTarget(ref bool stopProcessingStack, TargetEntry currentTa { // If we've already dealt with this target and it didn't skip, let's log appropriately // Otherwise we don't want anything more to do with it. + bool success = targetResult.ResultCode == TargetResultCode.Success; var skippedTargetEventArgs = new TargetSkippedEventArgs(message: null) { BuildEventContext = _projectLoggingContext.BuildEventContext, @@ -562,7 +563,9 @@ private bool CheckSkipTarget(ref bool stopProcessingStack, TargetEntry currentTa TargetFile = currentTargetEntry.Target.Location.File, ParentTarget = currentTargetEntry.ParentEntry?.Target.Name, BuildReason = currentTargetEntry.BuildReason, - OriginallySucceeded = targetResult.ResultCode == TargetResultCode.Success + OriginallySucceeded = success, + SkipReason = success ? TargetSkipReason.PreviouslyBuiltSuccessfully : TargetSkipReason.PreviouslyBuiltUnsuccessfully, + OriginalBuildEventContext = targetResult.OriginalBuildEventContext }; _projectLoggingContext.LogBuildEvent(skippedTargetEventArgs); diff --git a/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs b/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs index f7543d5d91d..d3a925b34a8 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs @@ -8,6 +8,7 @@ using System.Threading.Tasks; using Microsoft.Build.Collections; using Microsoft.Build.Evaluation; +using Microsoft.Build.Eventing; using Microsoft.Build.Execution; using Microsoft.Build.Framework; using Microsoft.Build.Shared; @@ -354,7 +355,10 @@ internal List GetDependencies(ProjectLoggingContext project if (!condition) { - _targetResult = new TargetResult(Array.Empty(), new WorkUnitResult(WorkUnitResultCode.Skipped, WorkUnitActionCode.Continue, null)); + _targetResult = new TargetResult( + Array.Empty(), + new WorkUnitResult(WorkUnitResultCode.Skipped, WorkUnitActionCode.Continue, null), + projectLoggingContext.BuildEventContext); _state = TargetEntryState.Completed; if (!projectLoggingContext.LoggingService.OnlyLogCriticalEvents) @@ -375,6 +379,7 @@ internal List GetDependencies(ProjectLoggingContext project TargetFile = _target.Location.File, ParentTarget = ParentEntry?.Target?.Name, BuildReason = BuildReason, + SkipReason = TargetSkipReason.ConditionWasFalse, Condition = _target.Condition, EvaluatedCondition = expanded }; @@ -460,8 +465,10 @@ internal async Task ExecuteTarget(ITaskBuilder taskBuilder, BuildRequestEntry re Lookup lookupForExecution; // UNDONE: (Refactor) Refactor TargetUpToDateChecker to take a logging context, not a logging service. + MSBuildEventSource.Log.TargetUpToDateStart(); TargetUpToDateChecker dependencyAnalyzer = new TargetUpToDateChecker(requestEntry.RequestConfiguration.Project, _target, targetLoggingContext.LoggingService, targetLoggingContext.BuildEventContext); DependencyAnalysisResult dependencyResult = dependencyAnalyzer.PerformDependencyAnalysis(bucket, out changedTargetInputs, out upToDateTargetInputs); + MSBuildEventSource.Log.TargetUpToDateStop((int)dependencyResult); switch (dependencyResult) { @@ -640,14 +647,11 @@ internal async Task ExecuteTarget(ITaskBuilder taskBuilder, BuildRequestEntry re } finally { - - - // log the last target finished since we now have the target outputs. - targetLoggingContext?.LogTargetBatchFinished(projectFullPath, targetSuccess, targetOutputItems?.Count > 0 ? targetOutputItems : null); - + // log the last target finished since we now have the target outputs. + targetLoggingContext?.LogTargetBatchFinished(projectFullPath, targetSuccess, targetOutputItems?.Count > 0 ? targetOutputItems : null); } - _targetResult = new TargetResult(targetOutputItems.ToArray(), aggregateResult); + _targetResult = new TargetResult(targetOutputItems.ToArray(), aggregateResult, targetLoggingContext?.BuildEventContext); if (aggregateResult.ResultCode == WorkUnitResultCode.Failed && aggregateResult.ActionCode == WorkUnitActionCode.Stop) { diff --git a/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs b/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs index 335dcf77097..9013bc11272 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TargetUpToDateChecker.cs @@ -233,9 +233,17 @@ out ItemDictionary upToDateTargetInputs if (result == DependencyAnalysisResult.SkipUpToDate) { - _loggingService.LogComment(_buildEventContext, MessageImportance.Normal, - "SkipTargetBecauseOutputsUpToDate", - TargetToAnalyze.Name); + var skippedTargetEventArgs = new TargetSkippedEventArgs(message: null) + { + BuildEventContext = _buildEventContext, + TargetName = TargetToAnalyze.Name, + BuildReason = TargetBuiltReason.None, + SkipReason = TargetSkipReason.OutputsUpToDate, + OriginallySucceeded = true, + Importance = MessageImportance.Normal + }; + + _loggingService.LogBuildEvent(skippedTargetEventArgs); // Log the target inputs & outputs if (!_loggingService.OnlyLogCriticalEvents) @@ -337,7 +345,7 @@ private static string GetIncrementalBuildReason(DependencyAnalysisLogDetail logD /// /// Extract only the unique inputs and outputs from all the inputs and outputs gathered - /// during depedency analysis + /// during dependency analysis /// private void LogUniqueInputsAndOutputs() { diff --git a/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs b/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs index 5390d169443..5fdd1a3e145 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TaskBuilder.cs @@ -847,7 +847,9 @@ private async Task ExecuteInstantiatedTask(ITaskExecutionHost ta } else if (type == typeof(ThreadAbortException)) { +#if !NET6_0_OR_GREATER && !NET6_0 // This is redundant but works around https://github.com/dotnet/sdk/issues/20700 Thread.ResetAbort(); +#endif _continueOnError = ContinueOnError.ErrorAndStop; // Cannot rethrow wrapped as ThreadAbortException is sealed and has no appropriate constructor diff --git a/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs b/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs index 9222e6e9baf..154ace42f96 100644 --- a/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs +++ b/src/Build/BackEnd/Components/RequestBuilder/TaskHost.cs @@ -22,6 +22,7 @@ using Microsoft.Build.BackEnd.Components.Caching; using System.Reflection; using Microsoft.Build.Eventing; +using Microsoft.Build.Utilities; namespace Microsoft.Build.BackEnd { @@ -33,12 +34,12 @@ internal class TaskHost : #if FEATURE_APPDOMAIN MarshalByRefObject, #endif - IBuildEngine9 + IBuildEngine10 { /// /// True if the "secret" environment variable MSBUILDNOINPROCNODE is set. /// - private static bool s_onlyUseOutOfProcNodes = Environment.GetEnvironmentVariable("MSBUILDNOINPROCNODE") == "1"; + private static bool s_disableInprocNodeByEnvironmentVariable = Environment.GetEnvironmentVariable("MSBUILDNOINPROCNODE") == "1"; /// /// Help diagnose tasks that log after they return. @@ -104,6 +105,8 @@ internal class TaskHost : /// private int _yieldThreadId = -1; + private bool _disableInprocNode; + /// /// Constructor /// @@ -123,7 +126,11 @@ public TaskHost(IBuildComponentHost host, BuildRequestEntry requestEntry, Elemen _targetBuilderCallback = targetBuilderCallback; _continueOnError = false; _activeProxy = true; - _callbackMonitor = new Object(); + _callbackMonitor = new object(); + _disableInprocNode = ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0) + ? s_disableInprocNodeByEnvironmentVariable || host.BuildParameters.DisableInProcNode + : s_disableInprocNodeByEnvironmentVariable; + EngineServices = new EngineServicesImpl(this); } /// @@ -137,7 +144,7 @@ public bool IsRunningMultipleNodes get { VerifyActiveProxy(); - return _host.BuildParameters.MaxNodeCount > 1 || s_onlyUseOutOfProcNodes; + return _host.BuildParameters.MaxNodeCount > 1 || _disableInprocNode; } } @@ -868,6 +875,42 @@ internal void ReleaseAllCores() #endregion + #region IBuildEngine10 Members + + [Serializable] + private sealed class EngineServicesImpl : EngineServices + { + private readonly TaskHost _taskHost; + + internal EngineServicesImpl(TaskHost taskHost) + { + _taskHost = taskHost; + } + + /// + public override bool LogsMessagesOfImportance(MessageImportance importance) + { +#if FEATURE_APPDOMAIN + if (RemotingServices.IsTransparentProxy(_taskHost)) + { + // If the check would be a cross-domain call, chances are that it wouldn't be worth it. + // Simply disable the optimization in such a case. + return true; + } +#endif + MessageImportance minimumImportance = _taskHost._taskLoggingContext?.LoggingService.MinimumRequiredMessageImportance ?? MessageImportance.Low; + return importance <= minimumImportance; + + } + + /// + public override bool IsTaskInputLoggingEnabled => _taskHost._host.BuildParameters.LogTaskInputs; + } + + public EngineServices EngineServices { get; } + + #endregion + /// /// Called by the internal MSBuild task. /// Does not take the lock because it is called by another request builder thread. diff --git a/src/Build/BackEnd/Components/Scheduler/SchedulableRequest.cs b/src/Build/BackEnd/Components/Scheduler/SchedulableRequest.cs index 9305abe7c66..994f3b155e9 100644 --- a/src/Build/BackEnd/Components/Scheduler/SchedulableRequest.cs +++ b/src/Build/BackEnd/Components/Scheduler/SchedulableRequest.cs @@ -463,6 +463,8 @@ public void VerifyOneOfStates(SchedulableRequestState[] requiredStates) ErrorUtilities.ThrowInternalError("State {0} is not one of the expected states.", _state); } + public bool IsProxyBuildRequest() => BuildRequest.IsProxyBuildRequest(); + /// /// Change to the specified state. Update internal counters. /// diff --git a/src/Build/BackEnd/Components/Scheduler/Scheduler.cs b/src/Build/BackEnd/Components/Scheduler/Scheduler.cs index 1e335cedb85..65af9d8c8a7 100644 --- a/src/Build/BackEnd/Components/Scheduler/Scheduler.cs +++ b/src/Build/BackEnd/Components/Scheduler/Scheduler.cs @@ -11,9 +11,11 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Build.Execution; +using Microsoft.Build.Experimental.ProjectCache; using Microsoft.Build.Framework; using Microsoft.Build.Shared; - +using Microsoft.Build.Shared.Debugging; +using Microsoft.Build.Utilities; using BuildAbortedException = Microsoft.Build.Exceptions.BuildAbortedException; using ILoggingService = Microsoft.Build.BackEnd.Logging.ILoggingService; using NodeLoggingContext = Microsoft.Build.BackEnd.Logging.NodeLoggingContext; @@ -140,7 +142,7 @@ internal class Scheduler : IScheduler /// /// Flag used for debugging by forcing all scheduling to go out-of-proc. /// - private bool _forceAffinityOutOfProc; + internal bool ForceAffinityOutOfProc { get; private set; } /// /// The path into which debug files will be written. @@ -166,14 +168,20 @@ internal class Scheduler : IScheduler /// private AssignUnscheduledRequestsDelegate _customRequestSchedulingAlgorithm; + private NodeLoggingContext _inprocNodeContext; + + private int _loggedWarningsForProxyBuildsOnOutOfProcNodes = 0; + /// /// Constructor. /// public Scheduler() { - _debugDumpState = Environment.GetEnvironmentVariable("MSBUILDDEBUGSCHEDULER") == "1"; - _forceAffinityOutOfProc = Environment.GetEnvironmentVariable("MSBUILDNOINPROCNODE") == "1"; - _debugDumpPath = Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); + // Be careful moving these to Traits, changing the timing of reading environment variables has a breaking potential. + _debugDumpState = Traits.Instance.DebugScheduler; + _debugDumpPath = ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0) + ? DebugUtils.DebugPath + : Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); _schedulingUnlimitedVariable = Environment.GetEnvironmentVariable("MSBUILDSCHEDULINGUNLIMITED"); _nodeLimitOffset = 0; @@ -610,6 +618,11 @@ public void InitializeComponent(IBuildComponentHost host) _componentHost = host; _resultsCache = (IResultsCache)_componentHost.GetComponent(BuildComponentType.ResultsCache); _configCache = (IConfigCache)_componentHost.GetComponent(BuildComponentType.ConfigCache); + _inprocNodeContext = new NodeLoggingContext(_componentHost.LoggingService, InProcNodeId, true); + var inprocNodeDisabledViaEnvironmentVariable = Environment.GetEnvironmentVariable("MSBUILDNOINPROCNODE") == "1"; + ForceAffinityOutOfProc = ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0) + ? inprocNodeDisabledViaEnvironmentVariable || _componentHost.BuildParameters.DisableInProcNode + : inprocNodeDisabledViaEnvironmentVariable; } /// @@ -791,6 +804,9 @@ private void AssignUnscheduledRequestsToNodes(List responses, { // We want to find more work first, and we assign traversals to the in-proc node first, if possible. AssignUnscheduledRequestsByTraversalsFirst(responses, idleNodes); + + AssignUnscheduledProxyBuildRequestsToInProcNode(responses, idleNodes); + if (idleNodes.Count == 0) { return; @@ -972,6 +988,27 @@ private void AssignUnscheduledRequestsByTraversalsFirst(List r } } + /// + /// Proxy build requests should be really cheap (only return properties and items) and it's not worth + /// paying the IPC cost and re-evaluating them on out of proc nodes (they are guaranteed to be evaluated in the Scheduler process). + /// + private void AssignUnscheduledProxyBuildRequestsToInProcNode(List responses, HashSet idleNodes) + { + if (idleNodes.Contains(InProcNodeId)) + { + List unscheduledRequests = new List(_schedulingData.UnscheduledRequestsWhichCanBeScheduled); + foreach (SchedulableRequest request in unscheduledRequests) + { + if (CanScheduleRequestToNode(request, InProcNodeId) && request.IsProxyBuildRequest()) + { + AssignUnscheduledRequestToNode(request, InProcNodeId, responses); + idleNodes.Remove(InProcNodeId); + break; + } + } + } + } + /// /// Returns true if the request is for a traversal project. Traversals are used to find more work. /// @@ -1325,12 +1362,6 @@ private void AssignUnscheduledRequestToNode(SchedulableRequest request, int node ErrorUtilities.VerifyThrowArgumentNull(responses, nameof(responses)); ErrorUtilities.VerifyThrow(nodeId != InvalidNodeId, "Invalid node id specified."); - // Currently we cannot move certain kinds of traversals (notably solution metaprojects) to other nodes because - // they only have a ProjectInstance representation, and besides these kinds of projects build very quickly - // and produce more references (more work to do.) This just verifies we do not attempt to send a traversal to - // an out-of-proc node because doing so is inefficient and presently will cause the engine to fail on the remote - // node because these projects cannot be found. - ErrorUtilities.VerifyThrow(nodeId == InProcNodeId || _forceAffinityOutOfProc || !IsTraversalRequest(request.BuildRequest), "Can't assign traversal request to out-of-proc node!"); request.VerifyState(SchedulableRequestState.Unscheduled); // Determine if this node has seen our configuration before. If not, we must send it along with this request. @@ -1348,7 +1379,27 @@ private void AssignUnscheduledRequestToNode(SchedulableRequest request, int node responses.Add(ScheduleResponse.CreateScheduleResponse(nodeId, request.BuildRequest, mustSendConfigurationToNode)); TraceScheduler("Executing request {0} on node {1} with parent {2}", request.BuildRequest.GlobalRequestId, nodeId, (request.Parent == null) ? -1 : request.Parent.BuildRequest.GlobalRequestId); + + WarnWhenProxyBuildsGetScheduledOnOutOfProcNode(); + request.ResumeExecution(nodeId); + + void WarnWhenProxyBuildsGetScheduledOnOutOfProcNode() + { + if (request.IsProxyBuildRequest() && nodeId != InProcNodeId && _schedulingData.CanScheduleRequestToNode(request, InProcNodeId)) + { + ErrorUtilities.VerifyThrow( + _componentHost.BuildParameters.DisableInProcNode || ForceAffinityOutOfProc, + "Proxy requests should only get scheduled to out of proc nodes when the inproc node is disabled"); + + var loggedWarnings = Interlocked.CompareExchange(ref _loggedWarningsForProxyBuildsOnOutOfProcNodes, 1, 0); + + if (loggedWarnings == 0) + { + _inprocNodeContext.LogWarning("ProxyRequestNotScheduledOnInprocNode"); + } + } + } } /// @@ -1713,7 +1764,25 @@ private void HandleRequestBlockedByNewRequests(SchedulableRequest parentRequest, if (affinityMismatch) { - BuildResult result = new BuildResult(request, new InvalidOperationException(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("AffinityConflict", requestAffinity, existingRequestAffinity))); + ErrorUtilities.VerifyThrowInternalError( + _configCache.HasConfiguration(request.ConfigurationId), + "A request should have a configuration if it makes it this far in the build process."); + + var config = _configCache[request.ConfigurationId]; + var globalProperties = string.Join( + ";", + config.GlobalProperties.ToDictionary().Select(kvp => $"{kvp.Key}={kvp.Value}")); + + var result = new BuildResult( + request, + new InvalidOperationException( + ResourceUtilities.FormatResourceStringStripCodeAndKeyword( + "AffinityConflict", + requestAffinity, + existingRequestAffinity, + config.ProjectFullPath, + globalProperties + ))); response = GetResponseForResult(nodeForResults, request, result); responses.Add(response); continue; @@ -2047,7 +2116,7 @@ private int ComputeClosureOfWaitingRequests(SchedulableRequest request) /// private NodeAffinity GetNodeAffinityForRequest(BuildRequest request) { - if (_forceAffinityOutOfProc) + if (ForceAffinityOutOfProc) { return NodeAffinity.OutOfProc; } @@ -2057,6 +2126,15 @@ private NodeAffinity GetNodeAffinityForRequest(BuildRequest request) return NodeAffinity.InProc; } + ErrorUtilities.VerifyThrow(request.ConfigurationId != BuildRequestConfiguration.InvalidConfigurationId, "Requests should have a valid configuration id at this point"); + // If this configuration has been previously built on an out of proc node, scheduling it on the inproc node can cause either an affinity mismatch error when + // there are other pending requests for the same configuration or "unscheduled requests remain in the presence of free out of proc nodes" errors if there's no pending requests. + // So only assign proxy builds to the inproc node if their config hasn't been previously assigned to an out of proc node. + if (_schedulingData.CanScheduleConfigurationToNode(request.ConfigurationId, InProcNodeId) && request.IsProxyBuildRequest()) + { + return NodeAffinity.InProc; + } + BuildRequestConfiguration configuration = _configCache[request.ConfigurationId]; // The affinity may have been specified by the host services. diff --git a/src/Build/BackEnd/Components/Scheduler/SchedulingData.cs b/src/Build/BackEnd/Components/Scheduler/SchedulingData.cs index 0edc83f296e..9aeb9009c80 100644 --- a/src/Build/BackEnd/Components/Scheduler/SchedulingData.cs +++ b/src/Build/BackEnd/Components/Scheduler/SchedulingData.cs @@ -647,7 +647,12 @@ public int GetAssignedNodeForRequestConfiguration(int configurationId) /// public bool CanScheduleRequestToNode(SchedulableRequest request, int nodeId) { - int requiredNodeId = GetAssignedNodeForRequestConfiguration(request.BuildRequest.ConfigurationId); + return CanScheduleConfigurationToNode(request.BuildRequest.ConfigurationId, nodeId); + } + + public bool CanScheduleConfigurationToNode(int configurationId, int nodeId) + { + int requiredNodeId = GetAssignedNodeForRequestConfiguration(configurationId); return requiredNodeId == Scheduler.InvalidNodeId || requiredNodeId == nodeId; } diff --git a/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs b/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs index c719a51d2df..b8546bbe1b6 100644 --- a/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs +++ b/src/Build/BackEnd/Components/Scheduler/SchedulingPlan.cs @@ -10,6 +10,7 @@ using Microsoft.Build.Shared; using Microsoft.Build.BackEnd.Logging; using Microsoft.Build.Shared.FileSystem; +using Microsoft.Build.Utilities; namespace Microsoft.Build.BackEnd { @@ -316,7 +317,7 @@ private int GetConfigWithComparison(IEnumerable realConfigsToSchedule, Comp private void AnalyzeData() { DoRecursiveAnalysis(); - if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDDEBUGSCHEDULER"))) + if (Traits.Instance.DebugScheduler) { DetermineExpensiveConfigs(); DetermineConfigsByNumberOfOccurrences(); diff --git a/src/Build/BackEnd/Components/SdkResolution/SdkResolverException.cs b/src/Build/BackEnd/Components/SdkResolution/SdkResolverException.cs new file mode 100644 index 00000000000..d4f5b35a3d7 --- /dev/null +++ b/src/Build/BackEnd/Components/SdkResolution/SdkResolverException.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using System; +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; + +namespace Microsoft.Build.BackEnd.SdkResolution +{ + /// + /// Represents an exception that occurs when an SdkResolver throws an unhandled exception. + /// + public class SdkResolverException : Exception + { + public SdkResolver Resolver { get; private set; } + + public SdkReference Sdk { get; private set; } + + public SdkResolverException(string resourceName, SdkResolver resolver, SdkReference sdk, Exception innerException, params string[] args) + : base(string.Format(ResourceUtilities.GetResourceString(resourceName), args), innerException) + { + Resolver = resolver; + Sdk = sdk; + } + } +} diff --git a/src/Build/BackEnd/Components/SdkResolution/SdkResolverLoader.cs b/src/Build/BackEnd/Components/SdkResolution/SdkResolverLoader.cs index 97b4e83a02c..9c2fca19031 100644 --- a/src/Build/BackEnd/Components/SdkResolution/SdkResolverLoader.cs +++ b/src/Build/BackEnd/Components/SdkResolution/SdkResolverLoader.cs @@ -17,7 +17,7 @@ namespace Microsoft.Build.BackEnd.SdkResolution internal class SdkResolverLoader { #if FEATURE_ASSEMBLYLOADCONTEXT - private readonly CoreClrAssemblyLoader _loader = new CoreClrAssemblyLoader(); + private static readonly CoreClrAssemblyLoader s_loader = new CoreClrAssemblyLoader(); #endif private readonly string IncludeDefaultResolver = Environment.GetEnvironmentVariable("MSBUILDINCLUDEDEFAULTSDKRESOLVER"); @@ -35,7 +35,7 @@ internal class SdkResolverLoader internal virtual IList LoadResolvers(LoggingContext loggingContext, ElementLocation location) { - var resolvers = !String.Equals(IncludeDefaultResolver, "false", StringComparison.OrdinalIgnoreCase) ? + var resolvers = !String.Equals(IncludeDefaultResolver, "false", StringComparison.OrdinalIgnoreCase) ? new List {new DefaultSdkResolver()} : new List(); @@ -192,7 +192,7 @@ protected virtual Assembly LoadResolverAssembly(string resolverPath, LoggingCont #if !FEATURE_ASSEMBLYLOADCONTEXT return Assembly.LoadFrom(resolverPath); #else - return _loader.LoadFromPath(resolverPath); + return s_loader.LoadFromPath(resolverPath); #endif } diff --git a/src/Build/BackEnd/Components/SdkResolution/SdkResolverService.cs b/src/Build/BackEnd/Components/SdkResolution/SdkResolverService.cs index 237ea72d01e..6f90dacaeb4 100644 --- a/src/Build/BackEnd/Components/SdkResolution/SdkResolverService.cs +++ b/src/Build/BackEnd/Components/SdkResolution/SdkResolverService.cs @@ -117,21 +117,19 @@ public virtual SdkResult ResolveSdk(int submissionId, SdkReference sdk, LoggingC { result = (SdkResult)sdkResolver.Resolve(sdk, context, resultFactory); } - catch (Exception e) when (e is FileNotFoundException || (e is FileLoadException && sdkResolver.GetType().GetTypeInfo().Name.Equals("NuGetSdkResolver", StringComparison.Ordinal))) + catch (Exception e) when ((e is FileNotFoundException || e is FileLoadException) && sdkResolver.GetType().GetTypeInfo().Name.Equals("NuGetSdkResolver", StringComparison.Ordinal)) { // Since we explicitly add the NuGetSdkResolver, we special case this. The NuGetSdkResolver has special logic // to load NuGet assemblies at runtime which could fail if the user is not running installed MSBuild. Rather // than give them a generic error, we want to give a more specific message. This exception cannot be caught by // the resolver itself because it is usually thrown before the class is loaded - // MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - loggingContext.LogWarning(null, new BuildEventFileInfo(sdkReferenceLocation), "CouldNotRunNuGetSdkResolver", MSBuildConstants.NuGetAssemblyPathEnvironmentVariableName, e.Message); - continue; + // The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + throw new SdkResolverException("CouldNotRunNuGetSdkResolver", sdkResolver, sdk, e, MSBuildConstants.NuGetAssemblyPathEnvironmentVariableName, e.ToString()); } catch (Exception e) { - // MSB4242: The SDK resolver "{0}" failed to run. {1} - loggingContext.LogWarning(null, new BuildEventFileInfo(sdkReferenceLocation), "CouldNotRunSdkResolver", sdkResolver.Name, e.Message); - continue; + // The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}": {2} + throw new SdkResolverException("SDKResolverFailed", sdkResolver, sdk, e, sdkResolver.Name, sdk.ToString(), e.ToString()); } SetResolverState(submissionId, sdkResolver, context.State); diff --git a/src/Build/BackEnd/Node/OutOfProcNode.cs b/src/Build/BackEnd/Node/OutOfProcNode.cs index 80e36648f23..9e500181510 100644 --- a/src/Build/BackEnd/Node/OutOfProcNode.cs +++ b/src/Build/BackEnd/Node/OutOfProcNode.cs @@ -118,11 +118,6 @@ public class OutOfProcNode : INode, IBuildComponentHost, INodePacketFactory, INo /// private Exception _shutdownException; - /// - /// Flag indicating if we should debug communications or not. - /// - private readonly bool _debugCommunications; - /// /// Data for the use of LegacyThreading semantics. /// @@ -140,8 +135,6 @@ public OutOfProcNode() { s_isOutOfProcNode = true; - _debugCommunications = (Environment.GetEnvironmentVariable("MSBUILDDEBUGCOMM") == "1"); - _receivedPackets = new ConcurrentQueue(); _packetReceivedEvent = new AutoResetEvent(false); _shutdownEvent = new ManualResetEvent(false); diff --git a/src/Build/BackEnd/Shared/BuildRequest.cs b/src/Build/BackEnd/Shared/BuildRequest.cs index 4a0a4efb7f1..2bde7843447 100644 --- a/src/Build/BackEnd/Shared/BuildRequest.cs +++ b/src/Build/BackEnd/Shared/BuildRequest.cs @@ -419,5 +419,10 @@ internal static INodePacket FactoryForDeserialization(ITranslator translator) } #endregion + + public bool IsProxyBuildRequest() + { + return ProxyTargets != null; + } } } diff --git a/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs b/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs index a72435698c9..2cc6ab1f7b4 100644 --- a/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs +++ b/src/Build/BackEnd/Shared/BuildRequestConfiguration.cs @@ -131,8 +131,6 @@ internal class BuildRequestConfiguration : IEquatable /// private string _savedCurrentDirectory; - private bool _translateEntireProjectInstanceState; - #endregion /// @@ -178,7 +176,6 @@ internal BuildRequestConfiguration(int configId, BuildRequestData data, string d _project = data.ProjectInstance; _projectInitialTargets = data.ProjectInstance.InitialTargets; _projectDefaultTargets = data.ProjectInstance.DefaultTargets; - _translateEntireProjectInstanceState = data.ProjectInstance.TranslateEntireState; if (data.PropertiesToTransfer != null) { @@ -216,7 +213,6 @@ internal BuildRequestConfiguration(int configId, ProjectInstance instance) _project = instance; _projectInitialTargets = instance.InitialTargets; _projectDefaultTargets = instance.DefaultTargets; - _translateEntireProjectInstanceState = instance.TranslateEntireState; IsCacheable = false; } @@ -230,7 +226,6 @@ private BuildRequestConfiguration(int configId, BuildRequestConfiguration other) ErrorUtilities.VerifyThrow(other._transferredState == null, "Unexpected transferred state still set on other configuration."); _project = other._project; - _translateEntireProjectInstanceState = other._translateEntireProjectInstanceState; _transferredProperties = other._transferredProperties; _projectDefaultTargets = other._projectDefaultTargets; _projectInitialTargets = other._projectInitialTargets; @@ -410,7 +405,6 @@ private void SetProjectBasedState(ProjectInstance project) ProjectDefaultTargets = _project.DefaultTargets; ProjectInitialTargets = _project.InitialTargets; - _translateEntireProjectInstanceState = _project.TranslateEntireState; if (IsCached) { @@ -498,7 +492,7 @@ private void InitializeProject(BuildParameters buildParameters, Func private CacheInfo _cacheInfo; + /// + /// The (possibly null) from the original target build + /// + private BuildEventContext _originalBuildEventContext; + /// /// Initializes the results with specified items and result. /// /// The items produced by the target. /// The overall result for the target. - internal TargetResult(TaskItem[] items, WorkUnitResult result) + /// The original build event context from when the target was first built, if available. + /// Non-null when creating a after building the target initially (or skipping due to false condition). + /// Null when the is being created in other scenarios: + /// * Target that never ran because a dependency had an error + /// * in when Cancellation was requested + /// * in ProjectCache.CacheResult.ConstructBuildResult + /// + internal TargetResult(TaskItem[] items, WorkUnitResult result, BuildEventContext originalBuildEventContext = null) { ErrorUtilities.VerifyThrowArgumentNull(items, nameof(items)); ErrorUtilities.VerifyThrowArgumentNull(result, nameof(result)); _items = items; _result = result; + _originalBuildEventContext = originalBuildEventContext; } /// @@ -130,6 +143,11 @@ internal WorkUnitResult WorkUnitResult get => _result; } + /// + /// The (possibly null) from the original target build + /// + internal BuildEventContext OriginalBuildEventContext => _originalBuildEventContext; + /// /// Sets or gets a flag indicating whether or not a failure results should cause the build to fail. /// @@ -225,22 +243,15 @@ internal void CacheItems(int configId, string targetName) return; } - ITranslator translator = GetResultsCacheTranslator(configId, targetName, TranslationDirection.WriteToStream); + using ITranslator translator = GetResultsCacheTranslator(configId, targetName, TranslationDirection.WriteToStream); // If the translator is null, it means these results were cached once before. Since target results are immutable once they // have been created, there is no point in writing them again. if (translator != null) { - try - { - TranslateItems(translator); - _items = null; - _cacheInfo = new CacheInfo(configId, targetName); - } - finally - { - translator.Writer.BaseStream.Dispose(); - } + TranslateItems(translator); + _items = null; + _cacheInfo = new CacheInfo(configId, targetName); } } } @@ -253,6 +264,7 @@ private void InternalTranslate(ITranslator translator) translator.Translate(ref _result, WorkUnitResult.FactoryForDeserialization); translator.Translate(ref _targetFailureDoesntCauseBuildFailure); translator.Translate(ref _afterTargetsHaveFailed); + translator.TranslateOptionalBuildEventContext(ref _originalBuildEventContext); TranslateItems(translator); } @@ -265,17 +277,10 @@ private void RetrieveItemsFromCache() { if (_items == null) { - ITranslator translator = GetResultsCacheTranslator(_cacheInfo.ConfigId, _cacheInfo.TargetName, TranslationDirection.ReadFromStream); - - try - { - TranslateItems(translator); - _cacheInfo = new CacheInfo(); - } - finally - { - translator.Reader.BaseStream.Dispose(); - } + using ITranslator translator = GetResultsCacheTranslator(_cacheInfo.ConfigId, _cacheInfo.TargetName, TranslationDirection.ReadFromStream); + + TranslateItems(translator); + _cacheInfo = new CacheInfo(); } } } @@ -320,7 +325,7 @@ private void TranslateItems(ITranslator translator) ErrorUtilities.VerifyThrow(buffer != null, "Unexpected null items buffer during translation."); using MemoryStream itemsStream = new MemoryStream(buffer, 0, buffer.Length, writable: false, publiclyVisible: true); - var itemTranslator = BinaryTranslator.GetReadTranslator(itemsStream, null); + using var itemTranslator = BinaryTranslator.GetReadTranslator(itemsStream, null); _items = new TaskItem[itemsCount]; for (int i = 0; i < _items.Length; i++) { diff --git a/src/Build/Construction/ProjectElement.cs b/src/Build/Construction/ProjectElement.cs index 129e9ae3f35..8327feda09e 100644 --- a/src/Build/Construction/ProjectElement.cs +++ b/src/Build/Construction/ProjectElement.cs @@ -415,7 +415,7 @@ public virtual void CopyFrom(ProjectElement element) } /// - /// Hook for subclasses to specify whether the given should be cloned or not + /// Hook for subclasses to specify whether the given should be cloned or not /// protected virtual bool ShouldCloneXmlAttribute(XmlAttribute attribute) => true; diff --git a/src/Build/Construction/ProjectElementContainer.cs b/src/Build/Construction/ProjectElementContainer.cs index b55ba655ff4..3c4c0d6f269 100644 --- a/src/Build/Construction/ProjectElementContainer.cs +++ b/src/Build/Construction/ProjectElementContainer.cs @@ -316,7 +316,7 @@ public void RemoveChild(ProjectElement child) /// /// /// It is safe to modify the children in this way - /// during enumeration. See RemoveChild. + /// during enumeration. See . /// public void RemoveAllChildren() { diff --git a/src/Build/Construction/ProjectRootElement.cs b/src/Build/Construction/ProjectRootElement.cs index 9d86a4731dd..389f1f3cf6e 100644 --- a/src/Build/Construction/ProjectRootElement.cs +++ b/src/Build/Construction/ProjectRootElement.cs @@ -207,8 +207,12 @@ private ProjectRootElement(ProjectRootElementCacheBase projectRootElementCache, /// Assumes path is already normalized. /// May throw InvalidProjectFileException. /// - private ProjectRootElement(string path, ProjectRootElementCacheBase projectRootElementCache, - bool preserveFormatting) + private ProjectRootElement + ( + string path, + ProjectRootElementCacheBase projectRootElementCache, + bool preserveFormatting + ) { ErrorUtilities.VerifyThrowArgumentLength(path, nameof(path)); ErrorUtilities.VerifyThrowInternalRooted(path); @@ -222,8 +226,6 @@ private ProjectRootElement(string path, ProjectRootElementCacheBase projectRootE XmlDocumentWithLocation document = LoadDocument(path, preserveFormatting, projectRootElementCache.LoadProjectsReadOnly); ProjectParser.Parse(document, this); - - projectRootElementCache.AddEntry(this); } /// @@ -1677,19 +1679,33 @@ private void ReloadFrom(Func documentProducer, bo { ThrowIfUnsavedChanges(throwIfUnsavedChanges); - XmlDocumentWithLocation document = documentProducer(preserveFormatting ?? PreserveFormatting); - - // Reload should only mutate the state if there are no parse errors. - ThrowIfDocumentHasParsingErrors(document); - - // Do not clear the string cache. - // Based on the assumption that Projects are reloaded repeatedly from their file with small increments, - // and thus most strings would get reused - //this.XmlDocument.ClearAnyCachedStrings(); + var oldDocument = XmlDocument; + XmlDocumentWithLocation newDocument = documentProducer(preserveFormatting ?? PreserveFormatting); + try + { + // Reload should only mutate the state if there are no parse errors. + ThrowIfDocumentHasParsingErrors(newDocument); - RemoveAllChildren(); + RemoveAllChildren(); - ProjectParser.Parse(document, this); + ProjectParser.Parse(newDocument, this); + } + finally + { + // Whichever document didn't become this element's document must be removed from the string cache. + // We do it after the fact based on the assumption that Projects are reloaded repeatedly from their + // file with small increments, and thus most strings would get reused avoiding unnecessary churn in + // the string cache. + var currentDocument = XmlDocument; + if (!object.ReferenceEquals(currentDocument, oldDocument)) + { + oldDocument.ClearAnyCachedStrings(); + } + if (!object.ReferenceEquals(currentDocument, newDocument)) + { + newDocument.ClearAnyCachedStrings(); + } + } MarkDirty("Project reloaded", null); } diff --git a/src/Build/Construction/Solution/SolutionConfigurationInSolution.cs b/src/Build/Construction/Solution/SolutionConfigurationInSolution.cs index d5e869cb5eb..e35d36a9330 100644 --- a/src/Build/Construction/Solution/SolutionConfigurationInSolution.cs +++ b/src/Build/Construction/Solution/SolutionConfigurationInSolution.cs @@ -14,7 +14,7 @@ public sealed class SolutionConfigurationInSolution /// internal const char ConfigurationPlatformSeparator = '|'; - internal static readonly char[] ConfigurationPlatformSeparatorArray = new char[] { '|' }; + internal static readonly char[] ConfigurationPlatformSeparatorArray = { '|' }; /// /// Constructor diff --git a/src/Build/Construction/Solution/SolutionFile.cs b/src/Build/Construction/Solution/SolutionFile.cs index 364e449dcbe..f3e2c331c50 100644 --- a/src/Build/Construction/Solution/SolutionFile.cs +++ b/src/Build/Construction/Solution/SolutionFile.cs @@ -1384,7 +1384,6 @@ internal void ParseNestedProjects() internal void ParseSolutionConfigurations() { var nameValueSeparators = '='; - var configPlatformSeparators = new[] { SolutionConfigurationInSolution.ConfigurationPlatformSeparator }; do { @@ -1419,15 +1418,26 @@ internal void ParseSolutionConfigurations() ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(fullConfigurationName == configurationNames[1].Trim(), "SubCategoryForSolutionParsingErrors", new BuildEventFileInfo(FullPath, _currentLineNumber, 0), "SolutionParseInvalidSolutionConfigurationEntry", str); - string[] configurationPlatformParts = fullConfigurationName.Split(configPlatformSeparators); + var (configuration, platform) = ParseConfigurationName(fullConfigurationName, FullPath, _currentLineNumber, str); - ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(configurationPlatformParts.Length == 2, "SubCategoryForSolutionParsingErrors", - new BuildEventFileInfo(FullPath, _currentLineNumber, 0), "SolutionParseInvalidSolutionConfigurationEntry", str); - - _solutionConfigurations.Add(new SolutionConfigurationInSolution(configurationPlatformParts[0], configurationPlatformParts[1])); + _solutionConfigurations.Add(new SolutionConfigurationInSolution(configuration, platform)); } while (true); } + internal static (string Configuration, string Platform) ParseConfigurationName(string fullConfigurationName, string projectPath, int lineNumber, string containingString) + { + string[] configurationPlatformParts = fullConfigurationName.Split(SolutionConfigurationInSolution.ConfigurationPlatformSeparatorArray); + + ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile( + configurationPlatformParts.Length == 2, + "SubCategoryForSolutionParsingErrors", + new BuildEventFileInfo(projectPath, lineNumber, 0), + "SolutionParseInvalidSolutionConfigurationEntry", + containingString); + + return (configurationPlatformParts[0], configurationPlatformParts[1]); + } + /// /// Read project configurations in solution configurations section. /// diff --git a/src/Build/Construction/Solution/SolutionProjectGenerator.cs b/src/Build/Construction/Solution/SolutionProjectGenerator.cs index 4b1edd904a4..625352ba374 100644 --- a/src/Build/Construction/Solution/SolutionProjectGenerator.cs +++ b/src/Build/Construction/Solution/SolutionProjectGenerator.cs @@ -49,6 +49,11 @@ internal class SolutionProjectGenerator private const string WebProjectOverrideFolder = "_PublishedWebsites"; #endif // FEATURE_ASPNET_COMPILER + /// + /// Property set by VS when building projects. It's an XML containing the project configurations for ALL projects in the solution for the currently selected solution configuration. + /// + internal const string CurrentSolutionConfigurationContents = nameof(CurrentSolutionConfigurationContents); + /// /// The set of properties all projects in the solution should be built with /// @@ -66,7 +71,11 @@ internal class SolutionProjectGenerator "Build", "Clean", "Rebuild", - "Publish" + "Publish", + "ValidateSolutionConfiguration", + "ValidateToolsVersions", + "ValidateProjects", + "GetSolutionConfigurationContents" ); #if FEATURE_ASPNET_COMPILER @@ -226,6 +235,7 @@ internal static void AddPropertyGroupForSolutionConfiguration(ProjectRootElement }; using (XmlWriter xw = XmlWriter.Create(solutionConfigurationContents, settings)) { + // TODO: fix code clone for parsing CurrentSolutionConfiguration xml: https://github.com/dotnet/msbuild/issues/6751 xw.WriteStartElement("SolutionConfiguration"); // add a project configuration entry for each project in the solution @@ -975,6 +985,10 @@ private ProjectInstance CreateTraversalInstance(string wrapperProjectToolsVersio _submissionId ); + // Traversal meta project entire state has to be serialized as it was generated and hence + // does not have disk representation to load project from. + traversalInstance.TranslateEntireState = true; + // Make way for the real ones foreach (string targetName in dummyTargetsForEvaluationTime) { @@ -1182,6 +1196,10 @@ private ProjectInstance CreateMetaproject(ProjectInstance traversalProject, Proj // Create a new project instance with global properties and tools version from the existing project ProjectInstance metaprojectInstance = new ProjectInstance(EscapingUtilities.UnescapeAll(GetMetaprojectName(project)), traversalProject, GetMetaprojectGlobalProperties(traversalProject)); + // Traversal meta project entire state has to be serialized as it was generated and hence + // does not have disk representation to load project from. + metaprojectInstance.TranslateEntireState = true; + // Add the project references which must build before this one. AddMetaprojectReferenceItems(traversalProject, metaprojectInstance, project); diff --git a/src/Build/Definition/Project.cs b/src/Build/Definition/Project.cs index f158ca5a2bb..d842373c86c 100644 --- a/src/Build/Definition/Project.cs +++ b/src/Build/Definition/Project.cs @@ -43,7 +43,7 @@ namespace Microsoft.Build.Evaluation /// Edits to this project always update the backing XML. /// // UNDONE: (Multiple configurations.) Protect against problems when attempting to edit, after edits were made to the same ProjectRootElement either directly or through other projects evaluated from that ProjectRootElement. - [DebuggerDisplay("{FullPath} EffectiveToolsVersion={ToolsVersion} #GlobalProperties={_data.GlobalPropertiesDictionary.Count} #Properties={_data.Properties.Count} #ItemTypes={_data.ItemTypes.Count} #ItemDefinitions={_data.ItemDefinitions.Count} #Items={_data.Items.Count} #Targets={_data.Targets.Count}")] + [DebuggerDisplay("{FullPath} EffectiveToolsVersion={ToolsVersion} #GlobalProperties={implementation._data.GlobalPropertiesDictionary.Count} #Properties={implementation._data.Properties.Count} #ItemTypes={implementation._data.ItemTypes.Count} #ItemDefinitions={implementation._data.ItemDefinitions.Count} #Items={implementation._data.Items.Count} #Targets={implementation._data.Targets.Count}")] public class Project : ILinkableObject { /// @@ -88,7 +88,7 @@ public class Project : ILinkableObject /// - /// - /// - /// When this property is set to true, the previous item operations throw an + /// When this property is set to true, the previous item operations throw an /// instead of expanding the item element. /// public bool ThrowInsteadOfSplittingItemElement @@ -461,7 +461,7 @@ private Project(string projectFile, IDictionary globalProperties { // If possible, clear out the XML we just loaded into the XML cache: // if we had loaded the XML from disk into the cache within this constructor, - // and then are are bailing out because there is a typo in the XML such that + // and then are are bailing out because there is a typo in the XML such that // evaluation failed, we don't want to leave the bad XML in the cache; // the user wouldn't be able to fix the XML file and try again. if (!ExceptionHandling.IsCriticalException(ex)) @@ -2406,7 +2406,7 @@ public override bool IsBuildEnabled /// their previously stored value to find out, and if so perhaps decide to update their own state. /// Note that the number may not increase monotonically. /// - /// This number corresponds to the and can be used to connect + /// This number corresponds to the and can be used to connect /// evaluation logging events back to the Project instance. /// public override int LastEvaluationId => _data.EvaluationId; @@ -2503,7 +2503,7 @@ private List GetAllGlobs(List projectItemElement // 5. // 6. // this remove applies to the includes at 1, 3, 5 // So A's applicable removes are composed of: - // + // // The applicable removes for the element at position 1 (xml element A) are composed of: // - all the removes seen by the next include statement of I's type (xml element B, position 3, which appears after A in file order). In this example that's Removes at positions 4 and 6. // - new removes between A and B. In this example that's Remove 2. @@ -2634,7 +2634,7 @@ public override List GetItemProvenance(string itemToMatch, str /// /// See . /// - /// /// + /// /// /// The ProjectItem object that indicates: the itemspec to match and the item type to constrain the search in. /// The search is also constrained on item elements appearing before the item element that produced this . /// The element that produced this is included in the results. @@ -3163,7 +3163,7 @@ public override void RemoveItems(IEnumerable items) ErrorUtilities.VerifyThrowArgumentNull(items, nameof(items)); // Copying to a list makes it possible to remove - // all items of a particular type with + // all items of a particular type with // RemoveItems(p.GetItems("mytype")) // without modifying the collection during enumeration. var itemsList = new List(items); @@ -3586,7 +3586,7 @@ private void ReevaluateIfNecessary( EvaluationContext evaluationContext = null) { // We will skip the evaluation if the flag is set. This will give us better performance on scenarios - // that we know we don't have to reevaluate. One example is project conversion bulk addfiles and set attributes. + // that we know we don't have to reevaluate. One example is project conversion bulk addfiles and set attributes. if (!SkipEvaluation && !ProjectCollection.SkipEvaluation && IsDirty) { try @@ -3684,9 +3684,9 @@ internal void Initialize(IDictionary globalProperties, string to { if (String.Equals(pair.Key, Constants.SubToolsetVersionPropertyName, StringComparison.OrdinalIgnoreCase) && subToolsetVersion != null) { - // if we have a sub-toolset version explicitly provided by the ProjectInstance constructor, AND a sub-toolset version provided as a global property, - // make sure that the one passed in with the constructor wins. If there isn't a matching global property, the sub-toolset version will be set at - // a later point. + // if we have a sub-toolset version explicitly provided by the ProjectInstance constructor, AND a sub-toolset version provided as a global property, + // make sure that the one passed in with the constructor wins. If there isn't a matching global property, the sub-toolset version will be set at + // a later point. globalPropertiesCollection.Set(ProjectPropertyInstance.Create(pair.Key, subToolsetVersion)); } else @@ -4185,7 +4185,7 @@ public void InitializeForEvaluation(IToolsetProvider toolsetProvider, IFileSyste _globalPropertiesToTreatAsLocal?.Clear(); - // Include the main project in the list of imports, as this list is + // Include the main project in the list of imports, as this list is // used to figure out if any of them have changed. RecordImport(null, Project.Xml, Project.Xml.Version, null); @@ -4225,7 +4225,7 @@ out var usingDifferentToolsVersionFromProjectFile SubToolsetVersion = Toolset.GenerateSubToolsetVersion(GlobalPropertiesDictionary); } - // Create a task registry which will fall back on the toolset task registry if necessary. + // Create a task registry which will fall back on the toolset task registry if necessary. TaskRegistry = new TaskRegistry(Toolset, Project.ProjectCollection.ProjectRootElementCache); } @@ -4235,7 +4235,7 @@ out var usingDifferentToolsVersionFromProjectFile /// public void FinishEvaluation() { - // We assume there will be no further changes to the targets collection + // We assume there will be no further changes to the targets collection // This also makes sure that we are thread safe Targets.MakeReadOnly(); @@ -4254,7 +4254,7 @@ public void FinishEvaluation() } else { - // Else we'll guess that this latest one is a potential match for the next, + // Else we'll guess that this latest one is a potential match for the next, // if it actually has any elements (eg., it's not a .user or .filters file) if (Targets.Count > 0) { @@ -4489,7 +4489,7 @@ internal bool RemoveItem(ProjectItem item) // This remove will not succeed if the item include was changed. // If many items are modified and then removed, this will leak them - // until the next reevaluation. + // until the next reevaluation. ItemsByEvaluatedIncludeCache.Remove(item.EvaluatedInclude, item); ItemsIgnoringCondition.Remove(item); diff --git a/src/Build/Definition/ProjectCollection.cs b/src/Build/Definition/ProjectCollection.cs index 07519d583ba..8629bd94317 100644 --- a/src/Build/Definition/ProjectCollection.cs +++ b/src/Build/Definition/ProjectCollection.cs @@ -1801,6 +1801,11 @@ internal class ReusableLogger : INodeLogger, IEventSource4 /// private readonly ILogger _originalLogger; + /// + /// Returns the logger we are wrapping. + /// + internal ILogger OriginalLogger => _originalLogger; + /// /// The design-time event source /// diff --git a/src/Build/Definition/ProjectImportPathMatch.cs b/src/Build/Definition/ProjectImportPathMatch.cs index f31d63fb957..1c670eb06a4 100644 --- a/src/Build/Definition/ProjectImportPathMatch.cs +++ b/src/Build/Definition/ProjectImportPathMatch.cs @@ -62,4 +62,4 @@ internal static ProjectImportPathMatch FactoryForDeserialization(ITranslator tra return new ProjectImportPathMatch(translator); } } -} \ No newline at end of file +} diff --git a/src/Build/Definition/Toolset.cs b/src/Build/Definition/Toolset.cs index a4e4d21565b..77d32963bf6 100644 --- a/src/Build/Definition/Toolset.cs +++ b/src/Build/Definition/Toolset.cs @@ -485,7 +485,7 @@ public string DefaultSubToolsetVersion return Constants.Dev10SubToolsetValue; } - // 2) Otherwise, just pick the highest available. + // 2) Otherwise, just pick the highest available. SortedDictionary subToolsetsWithVersion = new SortedDictionary(); List additionalSubToolsetNames = new List(); @@ -499,7 +499,7 @@ public string DefaultSubToolsetVersion } else { - // if it doesn't parse to an actual version number, shrug and just add it to the end. + // if it doesn't parse to an actual version number, shrug and just add it to the end. additionalSubToolsetNames.Add(subToolsetName); } } @@ -538,11 +538,11 @@ internal static bool Dev10IsInstalled { try { - // Figure out whether Dev10 is currently installed using the following heuristic: - // - Check whether the overall key (installed if any version of Dev10 is installed) is there. - // - If it's not, no version of Dev10 exists or has ever existed on this machine, so return 'false'. - // - If it is, we know that some version of Dev10 has been installed at some point, but we don't know - // for sure whether it's still there or not. Check the inndividual keys for {Pro, Premium, Ultimate, + // Figure out whether Dev10 is currently installed using the following heuristic: + // - Check whether the overall key (installed if any version of Dev10 is installed) is there. + // - If it's not, no version of Dev10 exists or has ever existed on this machine, so return 'false'. + // - If it is, we know that some version of Dev10 has been installed at some point, but we don't know + // for sure whether it's still there or not. Check the inndividual keys for {Pro, Premium, Ultimate, // C# Express, VB Express, C++ Express, VWD Express, LightSwitch} 2010 // - If even one of them exists, return 'true'. // - Otherwise, return 'false. @@ -804,7 +804,7 @@ internal string GenerateSubToolsetVersion(int visualStudioVersionFromSolution) } } - // Next, try the toolset environment properties + // Next, try the toolset environment properties if (_environmentProperties != null) { ProjectPropertyInstance visualStudioVersionProperty = _environmentProperties[Constants.SubToolsetVersionPropertyName]; @@ -823,8 +823,8 @@ internal string GenerateSubToolsetVersion(int visualStudioVersionFromSolution) subToolsetVersion = SubToolsets.Keys.FirstOrDefault(version => visualStudioVersionFromSolutionAsVersion.Equals(VersionUtilities.ConvertToVersion(version))); } - // Solution version also didn't work out, so fall back to default. - // If subToolsetVersion is null, there simply wasn't a matching solution version. + // Solution version also didn't work out, so fall back to default. + // If subToolsetVersion is null, there simply wasn't a matching solution version. return subToolsetVersion ?? (DefaultSubToolsetVersion); } @@ -920,12 +920,12 @@ private void InitializeProperties(ILoggingService loggingServices, BuildEventCon reservedProperties.Add(ProjectPropertyInstance.Create(ReservedPropertyNames.assemblyVersion, Constants.AssemblyVersion, mayBeReserved: true)); reservedProperties.Add(ProjectPropertyInstance.Create(ReservedPropertyNames.version, MSBuildAssemblyFileVersion.Instance.MajorMinorBuild, mayBeReserved: true)); - // Add one for the subtoolset version property -- it may or may not be set depending on whether it has already been set by the - // environment or global properties, but it's better to create a dictionary that's one too big than one that's one too small. + // Add one for the subtoolset version property -- it may or may not be set depending on whether it has already been set by the + // environment or global properties, but it's better to create a dictionary that's one too big than one that's one too small. int count = _environmentProperties.Count + reservedProperties.Count + Properties.Values.Count + _globalProperties.Count + 1; - // GenerateSubToolsetVersion checks the environment and global properties, so it's safe to go ahead and gather the - // subtoolset properties here without fearing that we'll have somehow come up with the wrong subtoolset version. + // GenerateSubToolsetVersion checks the environment and global properties, so it's safe to go ahead and gather the + // subtoolset properties here without fearing that we'll have somehow come up with the wrong subtoolset version. string subToolsetVersion = this.GenerateSubToolsetVersion(); SubToolset subToolset; ICollection subToolsetProperties = null; @@ -941,10 +941,10 @@ private void InitializeProperties(ILoggingService loggingServices, BuildEventCon _propertyBag = new PropertyDictionary(count); - // Should be imported in the same order as in the evaluator: + // Should be imported in the same order as in the evaluator: // - Environment // - Toolset - // - Subtoolset (if any) + // - Subtoolset (if any) // - Global _propertyBag.ImportProperties(_environmentProperties); diff --git a/src/Build/Definition/ToolsetConfigurationReader.cs b/src/Build/Definition/ToolsetConfigurationReader.cs index 5665b1aaf09..990a8b686cb 100644 --- a/src/Build/Definition/ToolsetConfigurationReader.cs +++ b/src/Build/Definition/ToolsetConfigurationReader.cs @@ -10,6 +10,7 @@ using Microsoft.Build.Execution; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; +using Microsoft.Build.Utilities; using ErrorUtilities = Microsoft.Build.Shared.ErrorUtilities; using InvalidToolsetDefinitionException = Microsoft.Build.Exceptions.InvalidToolsetDefinitionException; @@ -41,6 +42,13 @@ internal class ToolsetConfigurationReader : ToolsetReader /// private static readonly char[] s_separatorForExtensionsPathSearchPaths = MSBuildConstants.SemicolonChar; + /// + /// Caching MSBuild exe configuration. + /// Used only by ReadApplicationConfiguration factory function (default) as oppose to unit tests config factory functions + /// which must not cache configs. + /// + private static readonly Lazy s_configurationCache = new Lazy(ReadOpenMappedExeConfiguration); + /// /// Cached values of tools version -> project import search paths table /// @@ -250,6 +258,18 @@ private Dictionary ComputeDistinctListOfSearchPa /// Unit tests wish to avoid reading (nunit.exe) application configuration file. /// private static Configuration ReadApplicationConfiguration() + { + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0)) + { + return s_configurationCache.Value; + } + else + { + return ReadOpenMappedExeConfiguration(); + } + } + + private static Configuration ReadOpenMappedExeConfiguration() { // When running from the command-line or from VS, use the msbuild.exe.config file. if (BuildEnvironmentHelper.Instance.Mode != BuildEnvironmentMode.None && diff --git a/src/Build/Definition/ToolsetRegistryReader.cs b/src/Build/Definition/ToolsetRegistryReader.cs index e2081dc5ad7..d49b10b1cf2 100644 --- a/src/Build/Definition/ToolsetRegistryReader.cs +++ b/src/Build/Definition/ToolsetRegistryReader.cs @@ -323,7 +323,7 @@ private static string GetValue(RegistryKeyWrapper wrapper, string valueName) object result = wrapper.GetValue(valueName); // RegistryKey.GetValue returns null if the value is not present - // and String.Empty if the value is present and no data is defined. + // and String.Empty if the value is present and no data is defined. // We preserve this distinction, because a string property in the registry with // no value really has an empty string for a value (which is a valid property value) // rather than null for a value (which is an invalid property value) diff --git a/src/Build/Evaluation/Context/EvaluationContext.cs b/src/Build/Evaluation/Context/EvaluationContext.cs index 470b4f0cb1e..827d9465d75 100644 --- a/src/Build/Evaluation/Context/EvaluationContext.cs +++ b/src/Build/Evaluation/Context/EvaluationContext.cs @@ -91,7 +91,7 @@ public static EvaluationContext Create(SharingPolicy policy, MSBuildFileSystemBa { var context = new EvaluationContext( policy, - fileSystem == null ? null : new MSBuildFileSystemAdapter(fileSystem)); + fileSystem); TestOnlyHookOnCreate?.Invoke(context); diff --git a/src/Build/Evaluation/Evaluator.cs b/src/Build/Evaluation/Evaluator.cs index d47f970c517..712307191f4 100644 --- a/src/Build/Evaluation/Evaluator.cs +++ b/src/Build/Evaluation/Evaluator.cs @@ -627,7 +627,7 @@ private void Evaluate() } _data.InitialTargets = initialTargets; - MSBuildEventSource.Log.EvaluatePass1Stop(projectFile, _projectRootElement.Properties.Count, _projectRootElement.Imports.Count); + MSBuildEventSource.Log.EvaluatePass1Stop(projectFile); // Pass2: evaluate item definitions // Don't box via IEnumerator and foreach; cache count so not to evaluate via interface each iteration MSBuildEventSource.Log.EvaluatePass2Start(projectFile); @@ -641,7 +641,7 @@ private void Evaluate() } } } - MSBuildEventSource.Log.EvaluatePass2Stop(projectFile, _itemDefinitionGroupElements.Count); + MSBuildEventSource.Log.EvaluatePass2Stop(projectFile); LazyItemEvaluator lazyEvaluator = null; using (_evaluationProfiler.TrackPass(EvaluationPass.Items)) { @@ -684,7 +684,7 @@ private void Evaluate() lazyEvaluator = null; } - MSBuildEventSource.Log.EvaluatePass3Stop(projectFile, _itemGroupElements.Count); + MSBuildEventSource.Log.EvaluatePass3Stop(projectFile); // Pass4: evaluate using-tasks MSBuildEventSource.Log.EvaluatePass4Start(projectFile); @@ -696,7 +696,7 @@ private void Evaluate() } } - // If there was no DefaultTargets attribute found in the depth first pass, + // If there was no DefaultTargets attribute found in the depth first pass, // use the name of the first target. If there isn't any target, don't error until build time. if (_data.DefaultTargets == null) @@ -714,7 +714,7 @@ private void Evaluate() Dictionary> targetsWhichRunAfterByTarget = new Dictionary>(StringComparer.OrdinalIgnoreCase); LinkedList activeTargetsByEvaluationOrder = new LinkedList(); Dictionary> activeTargets = new Dictionary>(StringComparer.OrdinalIgnoreCase); - MSBuildEventSource.Log.EvaluatePass4Stop(projectFile, _usingTaskElements.Count); + MSBuildEventSource.Log.EvaluatePass4Stop(projectFile); using (_evaluationProfiler.TrackPass(EvaluationPass.Targets)) { @@ -748,7 +748,7 @@ private void Evaluate() if (Traits.Instance.EscapeHatches.DebugEvaluation) { - // This is so important for VS performance it's worth always tracing; accidentally having + // This is so important for VS performance it's worth always tracing; accidentally having // inconsistent sets of global properties will cause reevaluations, which are wasteful and incorrect if (_projectRootElement.Count > 0) // VB/C# will new up empty projects; they aren't worth recording { @@ -773,7 +773,7 @@ private void Evaluate() } _data.FinishEvaluation(); - MSBuildEventSource.Log.EvaluatePass5Stop(projectFile, targetElementsCount); + MSBuildEventSource.Log.EvaluatePass5Stop(projectFile); } } @@ -1002,7 +1002,7 @@ private void EvaluateUsingTaskElement(string directoryOfImportingFile, ProjectUs /// private void ReadTargetElement(ProjectTargetElement targetElement, LinkedList activeTargetsByEvaluationOrder, Dictionary> activeTargets) { - // If we already have read a target instance for this element, use that. + // If we already have read a target instance for this element, use that. ProjectTargetInstance targetInstance = targetElement.TargetInstance ?? ReadNewTargetElement(targetElement, _projectSupportsReturnsAttribute[(ProjectRootElement)targetElement.Parent], _evaluationProfiler); string targetName = targetElement.Name; @@ -1091,6 +1091,8 @@ private void ValidateChangeWaveState() } } + private static readonly string CachedFileVersion = ProjectCollection.Version.ToString(); + /// /// Set the built-in properties, most of which are read-only /// @@ -1106,6 +1108,8 @@ private void AddBuiltInProperties() SetBuiltInProperty(ReservedPropertyNames.programFiles32, FrameworkLocationHelper.programFiles32); SetBuiltInProperty(ReservedPropertyNames.assemblyVersion, Constants.AssemblyVersion); SetBuiltInProperty(ReservedPropertyNames.version, MSBuildAssemblyFileVersion.Instance.MajorMinorBuild); + SetBuiltInProperty(ReservedPropertyNames.fileVersion, CachedFileVersion); + SetBuiltInProperty(ReservedPropertyNames.semanticVersion, ProjectCollection.DisplayVersion); ValidateChangeWaveState(); @@ -1192,9 +1196,9 @@ private void AddToolsetProperties() } else { - // Make the subtoolset version itself available as a property -- but only if it's not already set. + // Make the subtoolset version itself available as a property -- but only if it's not already set. // Because some people may be depending on this value even if there isn't a matching sub-toolset, - // set the property even if there is no matching sub-toolset. + // set the property even if there is no matching sub-toolset. if (!_data.Properties.Contains(Constants.SubToolsetVersionPropertyName)) { _data.SetProperty(Constants.SubToolsetVersionPropertyName, _data.SubToolsetVersion, false /* NOT global property */, false /* may NOT be a reserved name */); @@ -1249,8 +1253,8 @@ private void EvaluatePropertyElement(ProjectPropertyElement propertyElement) using (_evaluationProfiler.TrackElement(propertyElement)) { // Global properties cannot be overridden. We silently ignore them if we try. Legacy behavior. - // That is, unless this global property has been explicitly labeled as one that we want to treat as overridable for the duration - // of this project (or import). + // That is, unless this global property has been explicitly labeled as one that we want to treat as overridable for the duration + // of this project (or import). if ( ((IDictionary)_data.GlobalPropertiesDictionary).ContainsKey(propertyElement.Name) && !_data.GlobalPropertiesToTreatAsLocal.Contains(propertyElement.Name) @@ -1781,7 +1785,16 @@ static string EvaluateProperty(string value, IElementLocation location, } // Combine SDK path with the "project" relative path - sdkResult = _sdkResolverService.ResolveSdk(_submissionId, sdkReference, _evaluationLoggingContext, importElement.Location, solutionPath, projectPath, _interactive, _isRunningInVisualStudio); + try + { + sdkResult = _sdkResolverService.ResolveSdk(_submissionId, sdkReference, _evaluationLoggingContext, importElement.Location, solutionPath, projectPath, _interactive, _isRunningInVisualStudio); + } + catch (SdkResolverException e) + { + // We throw using e.Message because e.Message already contains the stack trace + // https://github.com/dotnet/msbuild/pull/6763 + ProjectErrorUtilities.ThrowInvalidProject(importElement.SdkLocation, "SDKResolverCriticalFailure", e.Message); + } if (!sdkResult.Success) { @@ -1926,8 +1939,6 @@ ProjectRootElement InnerCreate(string _, ProjectRootElementCacheBase __) } } - _projectRootElementCache.AddEntry(project); - return project; } @@ -2278,8 +2289,8 @@ private LoadImportsResult ExpandAndLoadImportsFromUnescapedImportExpression(stri } } - // Because these expressions will never be expanded again, we - // can store the unescaped value. The only purpose of escaping is to + // Because these expressions will never be expanded again, we + // can store the unescaped value. The only purpose of escaping is to // avoid undesired splitting or expansion. _importsSeen.Add(importFileUnescaped, importElement); } diff --git a/src/Build/Evaluation/Expander.cs b/src/Build/Evaluation/Expander.cs index 80ddea0b0c2..fe397e4469a 100644 --- a/src/Build/Evaluation/Expander.cs +++ b/src/Build/Evaluation/Expander.cs @@ -610,7 +610,7 @@ private static bool IsValidPropertyName(string propertyName) /// private static bool IsTruncationEnabled(ExpanderOptions options) { - return (options & ExpanderOptions.Truncate) != 0 && !Traits.Instance.EscapeHatches.DoNotTruncateConditions && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_8); + return (options & ExpanderOptions.Truncate) != 0 && !Traits.Instance.EscapeHatches.DoNotTruncateConditions; } /// @@ -1759,7 +1759,7 @@ internal static ExpressionShredder.ItemExpressionCapture ExpandSingleItemVectorE } List matches; - if (s_invariantCompareInfo.IndexOf(expression, '@') == -1) + if (expression.IndexOf('@') == -1) { return null; } @@ -2539,7 +2539,7 @@ internal static IEnumerable> Metadata(Expander expander, I { // It may be that the itemspec has unescaped ';'s in it so we need to split here to handle // that case. - if (s_invariantCompareInfo.IndexOf(metadataValue, ';') >= 0) + if (metadataValue.IndexOf(';') >= 0) { var splits = ExpressionShredder.SplitSemiColonSeparatedList(metadataValue); @@ -3352,6 +3352,12 @@ internal object Execute(object objectInstance, IPropertyProvider properties, } else { + // Check that the function that we're going to call is valid to call + if (!IsInstanceMethodAvailable(_methodMethodName)) + { + ProjectErrorUtilities.ThrowInvalidProject(elementLocation, "InvalidFunctionMethodUnavailable", _methodMethodName, _receiverType.FullName); + } + _bindingFlags |= BindingFlags.Instance; // The object that we're about to call methods on may have escaped characters @@ -5017,6 +5023,19 @@ private static bool IsStaticMethodAvailable(Type receiverType, string methodName return AvailableStaticMethods.GetTypeInformationFromTypeCache(receiverType.FullName, methodName) != null; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsInstanceMethodAvailable(string methodName) + { + if (Traits.Instance.EnableAllPropertyFunctions) + { + // anything goes + return true; + } + + // This could be expanded to an allow / deny list. + return methodName != "GetType"; + } + /// /// Construct and instance of objectType based on the constructor or method arguments provided. /// Arguments must never be null. diff --git a/src/Build/Evaluation/ExpressionShredder.cs b/src/Build/Evaluation/ExpressionShredder.cs index aa5f8b40442..ffa04158d9d 100644 --- a/src/Build/Evaluation/ExpressionShredder.cs +++ b/src/Build/Evaluation/ExpressionShredder.cs @@ -65,7 +65,7 @@ internal static SemiColonTokenizer SplitSemiColonSeparatedList(string expression /// where metadata key is like "itemname.metadataname" or "metadataname". /// PERF: Tables are null if there are no entries, because this is quite a common case. /// - internal static ItemsAndMetadataPair GetReferencedItemNamesAndMetadata(List expressions) + internal static ItemsAndMetadataPair GetReferencedItemNamesAndMetadata(IEnumerable expressions) { ItemsAndMetadataPair pair = new ItemsAndMetadataPair(null, null); diff --git a/src/Build/Evaluation/ItemDataCollectionValue.cs b/src/Build/Evaluation/ItemDataCollectionValue.cs new file mode 100644 index 00000000000..15807cc996d --- /dev/null +++ b/src/Build/Evaluation/ItemDataCollectionValue.cs @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; + +namespace Microsoft.Build.Evaluation +{ + /// + /// An efficient multi-value wrapper holding one or more items. + /// + internal struct ItemDataCollectionValue + { + /// + /// A non-allocating enumerator for the multi-value. + /// + public struct Enumerator : IEnumerator + { + private object _value; + private int _index; + + public Enumerator(object value) + { + _value = value; + _index = -1; + } + + public I Current => (_value is IList list) ? list[_index] : (I)_value; + object System.Collections.IEnumerator.Current => Current; + + public void Dispose() + { } + + public bool MoveNext() + { + // If value is not a list, it is either null or a single item. + int count = (_value is IList list) ? list.Count : (_value is null ? 0 : 1); + if (_index + 1 < count) + { + _index++; + return true; + } + return false; + } + + public void Reset() + { + _index = -1; + } + } + + /// + /// Holds one value or a list of values. + /// + private object _value; + + public bool IsEmpty => _value == null || (_value is List list && list.Count == 0); + + public ItemDataCollectionValue(I item) + { + _value = item; + } + + public void Add(I item) + { + if (_value is null) + { + _value = item; + } + else + { + if (_value is not List list) + { + list = new List() + { + (I)_value + }; + _value = list; + } + list.Add(item); + } + } + + public void Delete(I item) + { + if (_value is List list) + { + list.Remove(item); + } + else if (object.Equals(_value, item)) + { + _value = null; + } + } + + public void Replace(I oldItem, I newItem) + { + if (_value is List list) + { + int index = list.IndexOf(oldItem); + if (index >= 0) + { + list[index] = newItem; + } + } + else if (object.Equals(_value, oldItem)) + { + _value = newItem; + } + } + + public Enumerator GetEnumerator() + { + return new Enumerator(_value); + } + } +} diff --git a/src/Build/Evaluation/ItemSpec.cs b/src/Build/Evaluation/ItemSpec.cs index 7a1cb4db89b..fbc0b6f6b1a 100644 --- a/src/Build/Evaluation/ItemSpec.cs +++ b/src/Build/Evaluation/ItemSpec.cs @@ -85,6 +85,11 @@ public override bool IsMatch(string itemToMatch) return ReferencedItems.Any(v => v.ItemAsValueFragment.IsMatch(itemToMatch)); } + public override IEnumerable GetReferencedItems() + { + return ReferencedItems.Select(v => EscapingUtilities.UnescapeAll(v.ItemAsValueFragment.TextFragment)); + } + public override IMSBuildGlob ToMSBuildGlob() { return MsBuildGlob; @@ -316,6 +321,48 @@ public IEnumerable FragmentsMatchingItem(string itemToMatch, o return result; } + /// + /// Returns a list of normalized paths that are common between this itemspec and keys of the given dictionary. + /// + /// The dictionary to match this itemspec against. + /// The keys of that are also referenced by this itemspec. + public IList IntersectsWith(IReadOnlyDictionary> itemsByNormalizedValue) + { + IList matches = null; + + foreach (var fragment in Fragments) + { + IEnumerable referencedItems = fragment.GetReferencedItems(); + if (referencedItems != null) + { + // The fragment can enumerate its referenced items, we can do dictionary lookups. + foreach (var spec in referencedItems) + { + string key = FileUtilities.NormalizePathForComparisonNoThrow(spec, fragment.ProjectDirectory); + if (itemsByNormalizedValue.TryGetValue(key, out var multiValue)) + { + matches ??= new List(); + matches.Add(key); + } + } + } + else + { + // The fragment cannot enumerate its referenced items. Iterate over the dictionary and test each item. + foreach (var kvp in itemsByNormalizedValue) + { + if (fragment.IsMatchNormalized(kvp.Key)) + { + matches ??= new List(); + matches.Add(kvp.Key); + } + } + } + } + + return matches ?? Array.Empty(); + } + /// /// Return an MSBuildGlob that represents this ItemSpec. /// @@ -415,6 +462,16 @@ public virtual bool IsMatch(string itemToMatch) return FileMatcher.IsMatch(itemToMatch); } + public virtual bool IsMatchNormalized(string normalizedItemToMatch) + { + return FileMatcher.IsMatchNormalized(normalizedItemToMatch); + } + + public virtual IEnumerable GetReferencedItems() + { + return Enumerable.Repeat(EscapingUtilities.UnescapeAll(TextFragment), 1); + } + public virtual IMSBuildGlob ToMSBuildGlob() { return MsBuildGlob; @@ -446,6 +503,12 @@ public GlobFragment(string textFragment, string projectDirectory) { } + public override IEnumerable GetReferencedItems() + { + // This fragment cannot efficiently enumerate its referenced items. + return null; + } + /// /// True if TextFragment starts with /**/ or a variation thereof with backslashes. /// @@ -462,7 +525,7 @@ public GlobFragment(string textFragment, string projectDirectory) /// on multiple metadata. If one item specifies NotTargetFramework to be net46 and TargetFramework to /// be netcoreapp3.1, we wouldn't want to match that to an item with TargetFramework 46 and /// NotTargetFramework netcoreapp3.1. - /// + /// /// Implementing this as a list of sets where each metadatum key has its own set also would not work /// because different items could match on different metadata, and we want to check to see if any /// single item matches on all the metadata. As an example, consider this scenario: @@ -474,10 +537,10 @@ public GlobFragment(string textFragment, string projectDirectory) /// should match none of them because Forgind doesn't match all three metadata of any of the items. /// With a list of sets, Forgind would match Baby on BadAt, Child on GoodAt, and Adolescent on OkAt, /// and Forgind would be erroneously removed. - /// + /// /// With a Trie as below, Items specify paths in the tree, so going to any child node eliminates all /// items that don't share that metadatum. This ensures the match is proper. - /// + /// /// Todo: Tries naturally can have different shapes depending on in what order the metadata are considered. /// Specifically, if all the items share a single metadata value for the one metadatum and have different /// values for a second metadatum, it will have only one node more than the number of items if the first diff --git a/src/Build/Evaluation/LazyItemEvaluator.IItemOperation.cs b/src/Build/Evaluation/LazyItemEvaluator.IItemOperation.cs index 66f927b4025..40ee16b1d88 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.IItemOperation.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.IItemOperation.cs @@ -9,7 +9,7 @@ internal partial class LazyItemEvaluator { internal interface IItemOperation { - void Apply(ImmutableList.Builder listBuilder, ImmutableHashSet globsToIgnore); + void Apply(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet globsToIgnore); } } -} \ No newline at end of file +} diff --git a/src/Build/Evaluation/LazyItemEvaluator.IncludeOperation.cs b/src/Build/Evaluation/LazyItemEvaluator.IncludeOperation.cs index 233156392bd..da5f61449b6 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.IncludeOperation.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.IncludeOperation.cs @@ -6,6 +6,7 @@ using Microsoft.Build.Internal; using Microsoft.Build.Shared; using Microsoft.Build.Utilities; +using Microsoft.CodeAnalysis.Collections; using System; using System.Collections.Generic; using System.Collections.Immutable; @@ -21,7 +22,7 @@ class IncludeOperation : LazyItemOperation readonly string _rootDirectory; - readonly ImmutableList _excludes; + readonly ImmutableSegmentedList _excludes; readonly ImmutableList _metadata; @@ -35,7 +36,7 @@ public IncludeOperation(IncludeOperationBuilder builder, LazyItemEvaluator SelectItems(ImmutableList.Builder listBuilder, ImmutableHashSet globsToIgnore) + protected override ImmutableList SelectItems(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet globsToIgnore) { var itemsToAdd = ImmutableList.CreateBuilder(); @@ -92,7 +93,7 @@ protected override ImmutableList SelectItems(ImmutableList.Builder { // If this item is behind a false condition and represents a full drive/filesystem scan, expanding it is // almost certainly undesired. It should be skipped to avoid evaluation taking an excessive amount of time. - bool skipGlob = !_conditionResult && globFragment.IsFullFileSystemScan && !Traits.Instance.EscapeHatches.AlwaysEvaluateDangerousGlobs && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_8); + bool skipGlob = !_conditionResult && globFragment.IsFullFileSystemScan && !Traits.Instance.EscapeHatches.AlwaysEvaluateDangerousGlobs; if (!skipGlob) { string glob = globFragment.TextFragment; @@ -153,7 +154,7 @@ protected override void MutateItems(ImmutableList items) DecorateItemsWithMetadata(items.Select(i => new ItemBatchingContext(i)), _metadata); } - protected override void SaveItems(ImmutableList items, ImmutableList.Builder listBuilder) + protected override void SaveItems(ImmutableList items, OrderedItemDataCollection.Builder listBuilder) { foreach (var item in items) { @@ -167,7 +168,7 @@ class IncludeOperationBuilder : OperationBuilderWithMetadata public int ElementOrder { get; set; } public string RootDirectory { get; set; } - public ImmutableList.Builder Excludes { get; } = ImmutableList.CreateBuilder(); + public ImmutableSegmentedList.Builder Excludes { get; } = ImmutableSegmentedList.CreateBuilder(); public IncludeOperationBuilder(ProjectItemElement itemElement, bool conditionResult) : base(itemElement, conditionResult) { diff --git a/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs b/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs index b072a36f854..288d11ce9b9 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.LazyItemOperation.cs @@ -51,7 +51,7 @@ protected LazyItemOperation(OperationBuilder builder, LazyItemEvaluator _lazyEvaluator.EngineFileUtilities; - public void Apply(ImmutableList.Builder listBuilder, ImmutableHashSet globsToIgnore) + public void Apply(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet globsToIgnore) { MSBuildEventSource.Log.ApplyLazyItemOperationsStart(_itemElement.ItemType); using (_lazyEvaluator._evaluationProfiler.TrackElement(_itemElement)) @@ -61,7 +61,7 @@ public void Apply(ImmutableList.Builder listBuilder, ImmutableHashSet< MSBuildEventSource.Log.ApplyLazyItemOperationsStop(_itemElement.ItemType); } - protected virtual void ApplyImpl(ImmutableList.Builder listBuilder, ImmutableHashSet globsToIgnore) + protected virtual void ApplyImpl(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet globsToIgnore) { var items = SelectItems(listBuilder, globsToIgnore); MutateItems(items); @@ -71,7 +71,7 @@ protected virtual void ApplyImpl(ImmutableList.Builder listBuilder, Im /// /// Produce the items to operate on. For example, create new ones or select existing ones /// - protected virtual ImmutableList SelectItems(ImmutableList.Builder listBuilder, ImmutableHashSet globsToIgnore) + protected virtual ImmutableList SelectItems(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet globsToIgnore) { return listBuilder.Select(itemData => itemData.Item) .ToImmutableList(); @@ -80,7 +80,7 @@ protected virtual ImmutableList SelectItems(ImmutableList.Builder l // todo Refactoring: MutateItems should clone each item before mutation. See https://github.com/Microsoft/msbuild/issues/2328 protected virtual void MutateItems(ImmutableList items) { } - protected virtual void SaveItems(ImmutableList items, ImmutableList.Builder listBuilder) { } + protected virtual void SaveItems(ImmutableList items, OrderedItemDataCollection.Builder listBuilder) { } private IList GetReferencedItems(string itemType, ImmutableHashSet globsToIgnore) { @@ -230,7 +230,6 @@ protected void DecorateItemsWithMetadata(IEnumerable itemBa // End of legal area for metadata expressions. _expander.Metadata = null; } - // End of pseudo batching //////////////////////////////////////////////////// // Start of old code @@ -283,17 +282,18 @@ protected void DecorateItemsWithMetadata(IEnumerable itemBa } } - protected bool NeedToExpandMetadataForEachItem(ImmutableList metadata, out ItemsAndMetadataPair itemsAndMetadataFound) + private static IEnumerable GetMetadataValuesAndConditions(ImmutableList metadata) { - List values = new List(metadata.Count * 2); - foreach (var metadataElement in metadata) { - values.Add(metadataElement.Value); - values.Add(metadataElement.Condition); + yield return metadataElement.Value; + yield return metadataElement.Condition; } + } - itemsAndMetadataFound = ExpressionShredder.GetReferencedItemNamesAndMetadata(values); + protected bool NeedToExpandMetadataForEachItem(ImmutableList metadata, out ItemsAndMetadataPair itemsAndMetadataFound) + { + itemsAndMetadataFound = ExpressionShredder.GetReferencedItemNamesAndMetadata(GetMetadataValuesAndConditions(metadata)); bool needToExpandMetadataForEachItem = false; diff --git a/src/Build/Evaluation/LazyItemEvaluator.OrderedItemDataCollection.cs b/src/Build/Evaluation/LazyItemEvaluator.OrderedItemDataCollection.cs new file mode 100644 index 00000000000..e56334b81a7 --- /dev/null +++ b/src/Build/Evaluation/LazyItemEvaluator.OrderedItemDataCollection.cs @@ -0,0 +1,218 @@ +// Copyright (c) Microsoft. 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.Collections.Immutable; + +namespace Microsoft.Build.Evaluation +{ + internal partial class LazyItemEvaluator + { + /// + /// A collection of ItemData that maintains insertion order and internally optimizes some access patterns, e.g. bulk removal + /// based on normalized item values. + /// + internal sealed class OrderedItemDataCollection + { + #region Inner types + + /// + /// A mutable and enumerable version of . + /// + internal sealed class Builder : IEnumerable + { + /// + /// The list of items in the collection. Defines the enumeration order. + /// + private ImmutableList.Builder _listBuilder; + + /// + /// A dictionary of items keyed by their normalized value. + /// + private Dictionary> _dictionaryBuilder; + + internal Builder(ImmutableList.Builder listBuilder) + { + _listBuilder = listBuilder; + } + + #region IEnumerable implementation + + ImmutableList.Enumerator GetEnumerator() => _listBuilder.GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => _listBuilder.GetEnumerator(); + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => _listBuilder.GetEnumerator(); + + #endregion + + public int Count => _listBuilder.Count; + + public ItemData this[int index] + { + get { return _listBuilder[index]; } + set + { + // Update the dictionary if it exists. + if (_dictionaryBuilder is not null) + { + ItemData oldItemData = _listBuilder[index]; + string oldNormalizedValue = oldItemData.NormalizedItemValue; + string newNormalizedValue = value.NormalizedItemValue; + if (!string.Equals(oldNormalizedValue, newNormalizedValue, StringComparison.OrdinalIgnoreCase)) + { + // Normalized values are different - delete from the old entry and add to the new entry. + ItemDataCollectionValue oldDictionaryEntry = _dictionaryBuilder[oldNormalizedValue]; + oldDictionaryEntry.Delete(oldItemData.Item); + if (oldDictionaryEntry.IsEmpty) + { + _dictionaryBuilder.Remove(oldNormalizedValue); + } + else + { + _dictionaryBuilder[oldNormalizedValue] = oldDictionaryEntry; + } + + ItemDataCollectionValue newDictionaryEntry = _dictionaryBuilder[newNormalizedValue]; + newDictionaryEntry.Add(value.Item); + _dictionaryBuilder[newNormalizedValue] = newDictionaryEntry; + + } + else + { + // Normalized values are the same - replace the item in the entry. + ItemDataCollectionValue dictionaryEntry = _dictionaryBuilder[newNormalizedValue]; + dictionaryEntry.Replace(oldItemData.Item, value.Item); + _dictionaryBuilder[newNormalizedValue] = dictionaryEntry; + } + } + _listBuilder[index] = value; + } + } + + /// + /// Gets or creates a dictionary keyed by normalized values. + /// + public Dictionary> Dictionary + { + get + { + if (_dictionaryBuilder == null) + { + _dictionaryBuilder = new Dictionary>(StringComparer.OrdinalIgnoreCase); + for (int i = 0; i < _listBuilder.Count; i++) + { + ItemData itemData = _listBuilder[i]; + AddToDictionary(ref itemData); + _listBuilder[i] = itemData; + } + } + return _dictionaryBuilder; + } + } + + public void Add(ItemData data) + { + if (_dictionaryBuilder is not null) + { + AddToDictionary(ref data); + } + _listBuilder.Add(data); + } + + public void Clear() + { + _listBuilder.Clear(); + _dictionaryBuilder?.Clear(); + } + + /// + /// Removes all items passed in a collection. + /// + public void RemoveAll(ICollection itemsToRemove) + { + _listBuilder.RemoveAll(item => itemsToRemove.Contains(item.Item)); + // This is a rare operation, don't bother updating the dictionary for now. It will be recreated as needed. + _dictionaryBuilder = null; + } + + /// + /// Removes all items whose normalized path is passed in a collection. + /// + public void RemoveAll(ICollection itemPathsToRemove) + { + var dictionary = Dictionary; + HashSet itemsToRemove = null; + foreach (string itemValue in itemPathsToRemove) + { + if (dictionary.TryGetValue(itemValue, out var multiItem)) + { + foreach (I item in multiItem) + { + itemsToRemove ??= new HashSet(); + itemsToRemove.Add(item); + } + _dictionaryBuilder.Remove(itemValue); + } + } + + if (itemsToRemove is not null) + { + _listBuilder.RemoveAll(item => itemsToRemove.Contains(item.Item)); + } + } + + /// + /// Creates an immutable view of this collection. + /// + public OrderedItemDataCollection ToImmutable() + { + return new OrderedItemDataCollection(_listBuilder.ToImmutable()); + } + + private void AddToDictionary(ref ItemData itemData) + { + string key = itemData.NormalizedItemValue; + + if (!_dictionaryBuilder.TryGetValue(key, out var dictionaryValue)) + { + dictionaryValue = new ItemDataCollectionValue(itemData.Item); + } + else + { + dictionaryValue.Add(itemData.Item); + } + _dictionaryBuilder[key] = dictionaryValue; + } + } + + #endregion + + /// + /// The list of items in the collection. Defines the enumeration order. + /// + private ImmutableList _list; + + private OrderedItemDataCollection(ImmutableList list) + { + _list = list; + } + + /// + /// Creates a new mutable collection. + /// + public static Builder CreateBuilder() + { + return new Builder(ImmutableList.CreateBuilder()); + } + + /// + /// Creates a mutable view of this collection. Changes made to the returned builder are not reflected in this collection. + /// + public Builder ToBuilder() + { + return new Builder(_list.ToBuilder()); + } + } + } +} diff --git a/src/Build/Evaluation/LazyItemEvaluator.RemoveOperation.cs b/src/Build/Evaluation/LazyItemEvaluator.RemoveOperation.cs index 5a2d19ad7b2..1db3ddfd3ce 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.RemoveOperation.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.RemoveOperation.cs @@ -3,6 +3,8 @@ using Microsoft.Build.Construction; using Microsoft.Build.Shared; +using Microsoft.Build.Utilities; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -35,33 +37,52 @@ public RemoveOperation(RemoveOperationBuilder builder, LazyItemEvaluator /// - /// This operation is mostly implemented in terms of the default . - /// This override exists to apply the removing-everything short-circuit. + /// This override exists to apply the removing-everything short-circuit and to avoid creating a redundant list of items to remove. /// - protected override void ApplyImpl(ImmutableList.Builder listBuilder, ImmutableHashSet globsToIgnore) + protected override void ApplyImpl(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet globsToIgnore) { - if (_matchOnMetadata.IsEmpty && ItemspecContainsASingleBareItemReference(_itemSpec, _itemElement.ItemType) && _conditionResult) + if (!_conditionResult) { - // Perf optimization: If the Remove operation references itself (e.g. ) - // then all items are removed and matching is not necessary - listBuilder.Clear(); return; } - base.ApplyImpl(listBuilder, globsToIgnore); - } + bool matchingOnMetadata = !_matchOnMetadata.IsEmpty; + if (!matchingOnMetadata) + { + if (ItemspecContainsASingleBareItemReference(_itemSpec, _itemElement.ItemType)) + { + // Perf optimization: If the Remove operation references itself (e.g. ) + // then all items are removed and matching is not necessary + listBuilder.Clear(); + return; + } + + if (listBuilder.Count >= Traits.Instance.DictionaryBasedItemRemoveThreshold) + { + // Perf optimization: If the number of items in the running list is large, construct a dictionary, + // enumerate all items referenced by the item spec, and perform dictionary look-ups to find items + // to remove. + IList matches = _itemSpec.IntersectsWith(listBuilder.Dictionary); + listBuilder.RemoveAll(matches); + return; + } + } - // todo Perf: do not match against the globs: https://github.com/Microsoft/msbuild/issues/2329 - protected override ImmutableList SelectItems(ImmutableList.Builder listBuilder, ImmutableHashSet globsToIgnore) - { - var items = ImmutableHashSet.CreateBuilder(); + // todo Perf: do not match against the globs: https://github.com/Microsoft/msbuild/issues/2329 + HashSet items = null; foreach (ItemData item in listBuilder) { - if (_matchOnMetadata.IsEmpty ? _itemSpec.MatchesItem(item.Item) : MatchesItemOnMetadata(item.Item)) + bool isMatch = matchingOnMetadata ? MatchesItemOnMetadata(item.Item) : _itemSpec.MatchesItem(item.Item); + if (isMatch) + { + items ??= new HashSet(); items.Add(item.Item); + } + } + if (items is not null) + { + listBuilder.RemoveAll(items); } - - return items.ToImmutableList(); } private bool MatchesItemOnMetadata(I item) @@ -69,16 +90,6 @@ private bool MatchesItemOnMetadata(I item) return _metadataSet.Contains(_matchOnMetadata.Select(m => item.GetMetadataValue(m))); } - protected override void SaveItems(ImmutableList items, ImmutableList.Builder listBuilder) - { - if (!_conditionResult) - { - return; - } - - listBuilder.RemoveAll(itemData => items.Contains(itemData.Item)); - } - public ImmutableHashSet.Builder GetRemovedGlobs() { var builder = ImmutableHashSet.CreateBuilder(); diff --git a/src/Build/Evaluation/LazyItemEvaluator.UpdateOperation.cs b/src/Build/Evaluation/LazyItemEvaluator.UpdateOperation.cs index 5423bcf0286..35b2ffa899f 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.UpdateOperation.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.UpdateOperation.cs @@ -39,7 +39,7 @@ public MatchResult(bool isMatch, Dictionary capturedItemsFromReferenc delegate MatchResult ItemSpecMatchesItem(ItemSpec itemSpec, I itemToMatch); - protected override void ApplyImpl(ImmutableList.Builder listBuilder, ImmutableHashSet globsToIgnore) + protected override void ApplyImpl(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet globsToIgnore) { if (!_conditionResult) { diff --git a/src/Build/Evaluation/LazyItemEvaluator.cs b/src/Build/Evaluation/LazyItemEvaluator.cs index e5f2a72f61a..9fd3eec87e7 100644 --- a/src/Build/Evaluation/LazyItemEvaluator.cs +++ b/src/Build/Evaluation/LazyItemEvaluator.cs @@ -16,6 +16,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Threading; namespace Microsoft.Build.Evaluation { @@ -124,12 +125,13 @@ private static string GetCurrentDirectoryForConditionEvaluation(ProjectElement e public struct ItemData { - public ItemData(I item, ProjectItemElement originatingItemElement, int elementOrder, bool conditionResult) + public ItemData(I item, ProjectItemElement originatingItemElement, int elementOrder, bool conditionResult, string normalizedItemValue = null) { Item = item; OriginatingItemElement = originatingItemElement; ElementOrder = elementOrder; ConditionResult = conditionResult; + _normalizedItemValue = normalizedItemValue; } public ItemData Clone(IItemFactory itemFactory, ProjectItemElement initialItemElementForFactory) @@ -140,19 +142,37 @@ public ItemData Clone(IItemFactory itemFactory, ProjectItemElement initial var clonedItem = itemFactory.CreateItem(Item, OriginatingItemElement.ContainingProject.FullPath); itemFactory.ItemElement = initialItemElementForFactory; - return new ItemData(clonedItem, OriginatingItemElement, ElementOrder, ConditionResult); + return new ItemData(clonedItem, OriginatingItemElement, ElementOrder, ConditionResult, _normalizedItemValue); } public I Item { get; } public ProjectItemElement OriginatingItemElement { get; } public int ElementOrder { get; } public bool ConditionResult { get; } + + /// + /// Lazily created normalized item value. + /// + private string _normalizedItemValue; + public string NormalizedItemValue + { + get + { + var normalizedItemValue = Volatile.Read(ref _normalizedItemValue); + if (normalizedItemValue == null) + { + normalizedItemValue = FileUtilities.NormalizePathForComparisonNoThrow(Item.EvaluatedInclude, Item.ProjectDirectory); + Volatile.Write(ref _normalizedItemValue, normalizedItemValue); + } + return normalizedItemValue; + } + } } private class MemoizedOperation : IItemOperation { public LazyItemOperation Operation { get; } - private Dictionary, ImmutableList> _cache; + private Dictionary, OrderedItemDataCollection> _cache; private bool _isReferenced; #if DEBUG @@ -164,7 +184,7 @@ public MemoizedOperation(LazyItemOperation operation) Operation = operation; } - public void Apply(ImmutableList.Builder listBuilder, ImmutableHashSet globsToIgnore) + public void Apply(OrderedItemDataCollection.Builder listBuilder, ImmutableHashSet globsToIgnore) { #if DEBUG CheckInvariant(); @@ -200,7 +220,7 @@ private void CheckInvariant() } #endif - public bool TryGetFromCache(ISet globsToIgnore, out ImmutableList items) + public bool TryGetFromCache(ISet globsToIgnore, out OrderedItemDataCollection items) { if (_cache != null) { @@ -219,11 +239,11 @@ public void MarkAsReferenced() _isReferenced = true; } - private void AddItemsToCache(ImmutableHashSet globsToIgnore, ImmutableList items) + private void AddItemsToCache(ImmutableHashSet globsToIgnore, OrderedItemDataCollection items) { if (_cache == null) { - _cache = new Dictionary, ImmutableList>(); + _cache = new Dictionary, OrderedItemDataCollection>(); } _cache[globsToIgnore] = items; @@ -253,7 +273,7 @@ public ImmutableList GetMatchedItems(ImmutableHashSet globsToIgnore) return items.ToImmutable(); } - public ImmutableList.Builder GetItemData(ImmutableHashSet globsToIgnore) + public OrderedItemDataCollection.Builder GetItemData(ImmutableHashSet globsToIgnore) { // Cache results only on the LazyItemOperations whose results are required by an external caller (via GetItems). This means: // - Callers of GetItems who have announced ahead of time that they would reference an operation (via MarkAsReferenced()) @@ -275,7 +295,7 @@ public ImmutableList.Builder GetItemData(ImmutableHashSet glob // does not mutate: one can add operations on top, but the base never changes, and (ii) the globsToIgnore passed to the tail is the concatenation between // the globsToIgnore received as an arg, and the globsToIgnore produced by the head (if the head is a Remove operation) - ImmutableList items; + OrderedItemDataCollection items; if (_memoizedOperation.TryGetFromCache(globsToIgnore, out items)) { return items.ToBuilder(); @@ -299,12 +319,12 @@ public ImmutableList.Builder GetItemData(ImmutableHashSet glob /// is to optimize the case in which as series of UpdateOperations, each of which affects a single ItemSpec, are applied to all /// items in the list, leading to a quadratic-time operation. /// - private static ImmutableList.Builder ComputeItems(LazyItemList lazyItemList, ImmutableHashSet globsToIgnore) + private static OrderedItemDataCollection.Builder ComputeItems(LazyItemList lazyItemList, ImmutableHashSet globsToIgnore) { // Stack of operations up to the first one that's cached (exclusive) Stack itemListStack = new Stack(); - ImmutableList.Builder items = null; + OrderedItemDataCollection.Builder items = null; // Keep a separate stack of lists of globs to ignore that only gets modified for Remove operations Stack> globsToIgnoreStack = null; @@ -313,7 +333,7 @@ private static ImmutableList.Builder ComputeItems(LazyItemList lazyIte { var globsToIgnoreFromFutureOperations = globsToIgnoreStack?.Peek() ?? globsToIgnore; - ImmutableList itemsFromCache; + OrderedItemDataCollection itemsFromCache; if (currentList._memoizedOperation.TryGetFromCache(globsToIgnoreFromFutureOperations, out itemsFromCache)) { // the base items on top of which to apply the uncached operations are the items of the first operation that is cached @@ -341,7 +361,7 @@ private static ImmutableList.Builder ComputeItems(LazyItemList lazyIte if (items == null) { - items = ImmutableList.CreateBuilder(); + items = OrderedItemDataCollection.CreateBuilder(); } ImmutableHashSet currentGlobsToIgnore = globsToIgnoreStack == null ? globsToIgnore : globsToIgnoreStack.Peek(); @@ -419,7 +439,7 @@ private static ImmutableList.Builder ComputeItems(LazyItemList lazyIte return items; } - private static void ProcessNonWildCardItemUpdates(Dictionary itemsWithNoWildcards, ImmutableList.Builder items) + private static void ProcessNonWildCardItemUpdates(Dictionary itemsWithNoWildcards, OrderedItemDataCollection.Builder items) { #if DEBUG ErrorUtilities.VerifyThrow(itemsWithNoWildcards.All(fragment => !MSBuildConstants.CharactersForExpansion.Any(fragment.Key.Contains)), $"{nameof(itemsWithNoWildcards)} should not contain any text fragments with wildcards."); @@ -612,38 +632,36 @@ private void ProcessItemSpec(string rootDirectory, string itemSpec, IElementLoca } } + private static IEnumerable GetExpandedMetadataValuesAndConditions(ICollection metadata, Expander expander) + { + // Since we're just attempting to expand properties in order to find referenced items and not expanding metadata, + // unexpected errors may occur when evaluating property functions on unexpanded metadata. Just ignore them if that happens. + // See: https://github.com/Microsoft/msbuild/issues/3460 + const ExpanderOptions expanderOptions = ExpanderOptions.ExpandProperties | ExpanderOptions.LeavePropertiesUnexpandedOnError; + + // Expand properties here, because a property may have a value which is an item reference (ie "@(Bar)"), and + // if so we need to add the right item reference. + foreach (var metadatumElement in metadata) + { + yield return expander.ExpandIntoStringLeaveEscaped( + metadatumElement.Value, + expanderOptions, + metadatumElement.Location); + + yield return expander.ExpandIntoStringLeaveEscaped( + metadatumElement.Condition, + expanderOptions, + metadatumElement.ConditionLocation); + } + } + private void ProcessMetadataElements(ProjectItemElement itemElement, OperationBuilderWithMetadata operationBuilder) { if (itemElement.HasMetadata) { operationBuilder.Metadata.AddRange(itemElement.Metadata); - var values = new List(itemElement.Metadata.Count * 2); - - // Expand properties here, because a property may have a value which is an item reference (ie "@(Bar)"), and - // if so we need to add the right item reference. - foreach (var metadatumElement in itemElement.Metadata) - { - // Since we're just attempting to expand properties in order to find referenced items and not expanding metadata, - // unexpected errors may occur when evaluating property functions on unexpanded metadata. Just ignore them if that happens. - // See: https://github.com/Microsoft/msbuild/issues/3460 - const ExpanderOptions expanderOptions = ExpanderOptions.ExpandProperties | ExpanderOptions.LeavePropertiesUnexpandedOnError; - - var valueWithPropertiesExpanded = _expander.ExpandIntoStringLeaveEscaped( - metadatumElement.Value, - expanderOptions, - metadatumElement.Location); - - var conditionWithPropertiesExpanded = _expander.ExpandIntoStringLeaveEscaped( - metadatumElement.Condition, - expanderOptions, - metadatumElement.ConditionLocation); - - values.Add(valueWithPropertiesExpanded); - values.Add(conditionWithPropertiesExpanded); - } - - var itemsAndMetadataFound = ExpressionShredder.GetReferencedItemNamesAndMetadata(values); + var itemsAndMetadataFound = ExpressionShredder.GetReferencedItemNamesAndMetadata(GetExpandedMetadataValuesAndConditions(itemElement.Metadata, _expander)); if (itemsAndMetadataFound.Items != null) { foreach (var itemType in itemsAndMetadataFound.Items) diff --git a/src/Build/Evaluation/ProjectRootElementCache.cs b/src/Build/Evaluation/ProjectRootElementCache.cs index b75de665c00..a526ce4540b 100644 --- a/src/Build/Evaluation/ProjectRootElementCache.cs +++ b/src/Build/Evaluation/ProjectRootElementCache.cs @@ -21,37 +21,37 @@ namespace Microsoft.Build.Evaluation /// Maintains a cache of all loaded ProjectRootElement's for design time purposes. /// Weak references are held to add added ProjectRootElement's. /// Strong references are held to a limited number of added ProjectRootElement's. - /// + /// /// 1. Loads of a ProjectRootElement will share any existing loaded ProjectRootElement, rather /// than loading and parsing a new one. This is the case whether the ProjectRootElement /// is loaded directly or imported. - /// + /// /// 2. For design time, only a weak reference needs to be held, because all users have a strong reference. - /// + /// /// 3. Because all loads of a ProjectRootElement consult this cache, they can be assured that any /// entries in this cache are up to date. For example, if a ProjectRootElement is modified and saved, /// the cached ProjectRootElement will be the loaded one that was saved, so it will be up to date. - /// + /// /// 4. If, after a project has been loaded, an external app changes the project file content on disk, it is /// important that a subsequent load of that project does not return stale ProjectRootElement. To avoid this, the /// timestamp of the file on disk is compared to the timestamp of the file at the time that the ProjectRootElement loaded it. - /// + /// /// 5. For build time, some strong references need to be held, as otherwise the ProjectRootElement's for reuseable /// imports will be collected, and time will be wasted reparsing them. However we do not want to hold strong references /// to all ProjectRootElement's, consuming memory without end. So a simple priority queue is used. All Adds and Gets boost their /// entry to the top. As the queue gets too big, low priority entries are dropped. - /// + /// /// No guesses are made at which files are more interesting to cache, beyond the most-recently-used list. For example, ".targets" files /// or imported files are not treated specially, as this is a potentially unreliable heuristic. Besides, caching a project file itself could /// be useful, if for example you want to build it twice with different sets of properties. - /// + /// /// Because of the strongly typed list, some ProjectRootElement's will be held onto indefinitely. This is an acceptable price to pay for /// being able to provide a commonly used ProjectRootElement immediately it's needed. It is mitigated by the list being finite and small, and /// because we allow ProjectCollection.UnloadAllProjects to hint to us to clear the list. - /// + /// /// Implicit references are those which were loaded as a result of a build, and not explicitly loaded through, for instance, the project /// collection. - /// + /// /// internal class ProjectRootElementCache : ProjectRootElementCacheBase { @@ -59,22 +59,31 @@ internal class ProjectRootElementCache : ProjectRootElementCacheBase /// The maximum number of entries to keep strong references to. /// This has to be strong enough to make sure that key .targets files aren't pushed /// off by transient loads of non-reusable files like .user files. - /// - /// Made this as large as 50 because VC has a large number of - /// regularly used property sheets and other imports. - /// If you change this, update the unit tests. /// /// + /// Made this as large as 200 because ASP.NET Core (6.0) projects have + /// something like 80-90 imports. This was observed to give a noticeable + /// performance improvement compared to a mid-17.0 MSBuild with the old + /// value of 50. + /// /// If this number is increased much higher, the datastructure may /// need to be changed from a linked list, since it's currently O(n). /// - private static readonly int s_maximumStrongCacheSize = 50; + private static readonly int s_maximumStrongCacheSize = 200; /// /// Whether the cache should log activity to the Debug.Out stream /// private static bool s_debugLogCacheActivity; + /// + /// Whether the cache should check file content for cache entry invalidation. + /// + /// + /// Value shall be true only in case of testing. Outside QA tests it shall be false. + /// + private static bool s_сheckFileContent; + /// /// The map of weakly-held ProjectRootElement's /// @@ -116,6 +125,7 @@ static ProjectRootElementCache() } s_debugLogCacheActivity = Environment.GetEnvironmentVariable("MSBUILDDEBUGXMLCACHE") == "1"; + s_сheckFileContent = !String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDCACHECHECKFILECONTENT")); } /// @@ -131,15 +141,66 @@ internal ProjectRootElementCache(bool autoReloadFromDisk, bool loadProjectsReadO LoadProjectsReadOnly = loadProjectsReadOnly; } + + /// + /// Returns true if given cache entry exists and is outdated. + /// + private bool IsInvalidEntry(string projectFile, ProjectRootElement projectRootElement) + { + if (projectRootElement != null && _autoReloadFromDisk) + { + FileInfo fileInfo = FileUtilities.GetFileInfoNoThrow(projectFile); + + // If the file doesn't exist on disk, go ahead and use the cached version. + // It's an in-memory project that hasn't been saved yet. + if (fileInfo != null) + { + if (fileInfo.LastWriteTime != projectRootElement.LastWriteTimeWhenRead) + { + // File was changed on disk by external means. Cached version is no longer valid. + // We could throw here or ignore the problem, but it is a common and reasonable pattern to change a file + // externally and load a new project over it to see the new content. So we dump it from the cache + // to force a load from disk. There might then exist more than one ProjectRootElement with the same path, + // but clients ought not get themselves into such a state - and unless they save them to disk, + // it may not be a problem. + return true; + } + else if (s_сheckFileContent) + { + // QA tests run too fast for the timestamp check to work. This environment variable is for their + // use: it checks the file content as well as the timestamp. That's better than completely disabling + // the cache as we get test coverage of the rest of the cache code. + XmlDocument document = new XmlDocument(); + document.PreserveWhitespace = projectRootElement.XmlDocument.PreserveWhitespace; + + using (var xtr = XmlReaderExtension.Create(projectRootElement.FullPath, projectRootElement.ProjectRootElementCache.LoadProjectsReadOnly)) + { + document.Load(xtr.Reader); + } + + string diskContent = document.OuterXml; + string cacheContent = projectRootElement.XmlDocument.OuterXml; + + if (diskContent != cacheContent) + { + return true; + } + } + } + } + + return false; + } + /// /// Returns an existing ProjectRootElement for the specified file path, if any. /// If none exists, calls the provided delegate to load one, and adds that to the cache. /// The reason that it calls back to do this is so that the cache is locked between determining /// that the entry does not exist and adding the entry. - /// + /// /// If was set to true, and the file on disk has changed since it was cached, /// it will be reloaded before being returned. - /// + /// /// Thread safe. /// /// @@ -148,7 +209,7 @@ internal ProjectRootElementCache(bool autoReloadFromDisk, bool loadProjectsReadO /// If item is found, boosts it to the top of the strong cache. /// /// The project file which contains the ProjectRootElement. Must be a full path. - /// The delegate to use to load if necessary. May be null. + /// The delegate to use to load if necessary. May be null. Must not update the cache. /// true if the project is explicitly loaded, otherwise false. /// true to the project was loaded with the formated preserved, otherwise false. /// The ProjectRootElement instance if one exists. Null otherwise. @@ -158,91 +219,82 @@ internal override ProjectRootElement Get(string projectFile, OpenProjectRootElem // Should already have been canonicalized ErrorUtilities.VerifyThrowInternalRooted(projectFile); + ProjectRootElement projectRootElement; lock (_locker) { - ProjectRootElement projectRootElement; _weakCache.TryGetValue(projectFile, out projectRootElement); - if (preserveFormatting != null && projectRootElement != null && projectRootElement.XmlDocument.PreserveWhitespace != preserveFormatting) + if (projectRootElement != null) { - // Cached project doesn't match preserveFormatting setting, so reload it - projectRootElement.Reload(true, preserveFormatting); - } - - if (projectRootElement != null && _autoReloadFromDisk) - { - FileInfo fileInfo = FileUtilities.GetFileInfoNoThrow(projectFile); + BoostEntryInStrongCache(projectRootElement); - // If the file doesn't exist on disk, go ahead and use the cached version. - // It's an in-memory project that hasn't been saved yet. - if (fileInfo != null) + // An implicit load will never reset the explicit flag. + if (isExplicitlyLoaded) { - bool forgetEntry = false; - - if (fileInfo.LastWriteTime != projectRootElement.LastWriteTimeWhenRead) - { - // File was changed on disk by external means. Cached version is no longer reliable. - // We could throw here or ignore the problem, but it is a common and reasonable pattern to change a file - // externally and load a new project over it to see the new content. So we dump it from the cache - // to force a load from disk. There might then exist more than one ProjectRootElement with the same path, - // but clients ought not get themselves into such a state - and unless they save them to disk, - // it may not be a problem. - forgetEntry = true; - } - else if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDCACHECHECKFILECONTENT"))) - { - // QA tests run too fast for the timestamp check to work. This environment variable is for their - // use: it checks the file content as well as the timestamp. That's better than completely disabling - // the cache as we get test coverage of the rest of the cache code. - XmlDocument document = new XmlDocument(); - document.PreserveWhitespace = projectRootElement.XmlDocument.PreserveWhitespace; - - using (var xtr = XmlReaderExtension.Create(projectRootElement.FullPath, projectRootElement.ProjectRootElementCache.LoadProjectsReadOnly)) - { - document.Load(xtr.Reader); - } - - string diskContent = document.OuterXml; - string cacheContent = projectRootElement.XmlDocument.OuterXml; - - if (diskContent != cacheContent) - { - forgetEntry = true; - } - } - - if (forgetEntry) - { - ForgetEntry(projectRootElement); - - DebugTraceCache("Out of date dropped from XML cache: ", projectFile); - projectRootElement = null; - } + projectRootElement.MarkAsExplicitlyLoaded(); } } + else + { + DebugTraceCache("Not found in cache: ", projectFile); + } - if (projectRootElement == null && openProjectRootElement != null) + if (preserveFormatting != null && projectRootElement != null && projectRootElement.XmlDocument.PreserveWhitespace != preserveFormatting) { - projectRootElement = openProjectRootElement(projectFile, this); + // Cached project doesn't match preserveFormatting setting, so reload it + projectRootElement.Reload(true, preserveFormatting); + } + } - ErrorUtilities.VerifyThrowInternalNull(projectRootElement, "projectRootElement"); - ErrorUtilities.VerifyThrow(projectRootElement.FullPath == projectFile, "Got project back with incorrect path"); - ErrorUtilities.VerifyThrow(_weakCache.Contains(projectFile), "Open should have renamed into cache and boosted"); + bool projectRootElementIsInvalid = IsInvalidEntry(projectFile, projectRootElement); + if (projectRootElementIsInvalid) + { + DebugTraceCache("Not satisfied from cache: ", projectFile); + ForgetEntryIfExists(projectRootElement); + } + + if (openProjectRootElement == null) + { + if (projectRootElement == null || projectRootElementIsInvalid) + { + return null; } - else if (projectRootElement != null) + else { DebugTraceCache("Satisfied from XML cache: ", projectFile); - BoostEntryInStrongCache(projectRootElement); + return projectRootElement; } + } + + // Use openProjectRootElement to reload the element if the cache element does not exist or need to be reloaded. + if (projectRootElement == null || projectRootElementIsInvalid) + { + // We do not lock loading with common _locker of the cache, to avoid lock contention. + // Decided also not to lock this section with the key specific locker to avoid the overhead and code overcomplication, as + // it is not likely that two threads would use Get function for the same project simulteniously and it is not a big deal if in some cases we load the same project twice. + + projectRootElement = openProjectRootElement(projectFile, this); + ErrorUtilities.VerifyThrowInternalNull(projectRootElement, "projectRootElement"); + ErrorUtilities.VerifyThrow(projectRootElement.FullPath == projectFile, "Got project back with incorrect path"); // An implicit load will never reset the explicit flag. - if (projectRootElement != null && isExplicitlyLoaded) + if (isExplicitlyLoaded) { projectRootElement.MarkAsExplicitlyLoaded(); } - return projectRootElement; + // Update cache element. + // It is unlikely, but it might be that while without the lock, the projectRootElement in cache was updated by another thread. + // And here its entry will be replaced with the loaded projectRootElement. This is fine: + // if loaded projectRootElement is out of date (so, it changed since the time we loaded it), it will be updated the next time some thread calls Get function. + AddEntry(projectRootElement); } + else + { + DebugTraceCache("Satisfied from XML cache: ", projectFile); + } + + return projectRootElement; } /// @@ -317,7 +369,7 @@ internal override void DiscardStrongReferences() RaiseProjectRootElementRemovedFromStrongCache(projectRootElement); } - // A scavenge of the weak cache is probably not worth it as + // A scavenge of the weak cache is probably not worth it as // the GC would have had to run immediately after the line above. } } @@ -348,7 +400,7 @@ internal override void DiscardImplicitReferences() { lock (_locker) { - // Make a new Weak cache only with items that have been explicitly loaded, this will be a small number, there will most likely + // Make a new Weak cache only with items that have been explicitly loaded, this will be a small number, there will most likely // be many items which were not explicitly loaded (ie p2p references). WeakValueDictionary oldWeakCache = _weakCache; _weakCache = new WeakValueDictionary(StringComparer.OrdinalIgnoreCase); @@ -429,7 +481,7 @@ private void RenameEntryInternal(string oldFullPathIfAny, ProjectRootElement pro // (and thus gone from the client's point of view) that merely remains // in the cache because we still have a reference to it from our strong cache. // Another possibility is that there are two, unrelated, un-saved, in-memory projects that were given the same path. - // Replacing the cache entry does not in itself cause a problem -- if there are any actual users of the old + // Replacing the cache entry does not in itself cause a problem -- if there are any actual users of the old // entry they will not be affected. There would then exist more than one ProjectRootElement with the same path, // but clients ought not get themselves into such a state - and unless they save them to disk, // it may not be a problem. Replacing also doesn't cause a problem for the strong cache, @@ -508,6 +560,22 @@ private void ForgetEntry(ProjectRootElement projectRootElement) _strongCache.Remove(strongCacheEntry); RaiseProjectRootElementRemovedFromStrongCache(strongCacheEntry.Value); } + + DebugTraceCache("Out of date dropped from XML cache: ", projectRootElement.FullPath); + } + + /// + /// Completely remove an entry from this cache if it exists. + /// + private void ForgetEntryIfExists(ProjectRootElement projectRootElement) + { + lock (_locker) + { + if (_weakCache.TryGetValue(projectRootElement.FullPath, out var cached) && cached == projectRootElement) + { + ForgetEntry(projectRootElement); + } + } } /// diff --git a/src/Build/Evaluation/ProjectStringCache.cs b/src/Build/Evaluation/ProjectStringCache.cs index 699964700c6..32277f91fec 100644 --- a/src/Build/Evaluation/ProjectStringCache.cs +++ b/src/Build/Evaluation/ProjectStringCache.cs @@ -61,6 +61,20 @@ internal int Count } } + /// + /// Obtain the number of documents contained in the cache. + /// + internal int DocumentCount + { + get + { + lock (_locker) + { + return _documents.Count; + } + } + } + /// /// Add the given string to the cache or return the existing string if it is already /// in the cache. diff --git a/src/Build/Evaluation/SimpleProjectRootElementCache.cs b/src/Build/Evaluation/SimpleProjectRootElementCache.cs index 6890d8bd75b..7e7700d1467 100644 --- a/src/Build/Evaluation/SimpleProjectRootElementCache.cs +++ b/src/Build/Evaluation/SimpleProjectRootElementCache.cs @@ -63,8 +63,11 @@ private ProjectRootElement GetFromOrAddToCache(string projectFile, OpenProjectRo ErrorUtilities.VerifyThrowInternalNull(rootElement, "projectRootElement"); ErrorUtilities.VerifyThrow(rootElement.FullPath.Equals(key, StringComparison.OrdinalIgnoreCase), "Got project back with incorrect path"); + + AddEntry(rootElement); + ErrorUtilities.VerifyThrow(_cache.TryGetValue(key, out _), - "Open should have renamed into cache and boosted"); + "Project should have been added into cache and boosted"); return rootElement; }); diff --git a/src/Build/FileSystem/IFileSystemAdapter.cs b/src/Build/FileSystem/IFileSystemAdapter.cs deleted file mode 100644 index dce1574702c..00000000000 --- a/src/Build/FileSystem/IFileSystemAdapter.cs +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) Microsoft. 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.IO; -using Microsoft.Build.Shared.FileSystem; - -namespace Microsoft.Build.FileSystem -{ - internal class IFileSystemAdapter : MSBuildFileSystemBase - { - private readonly IFileSystem _wrappedFileSystem; - - public IFileSystemAdapter(IFileSystem wrappedFileSystem) - { - _wrappedFileSystem = wrappedFileSystem; - } - - public override TextReader ReadFile(string path) - { - return _wrappedFileSystem.ReadFile(path); - } - - public override Stream GetFileStream( - string path, - FileMode mode, - FileAccess access, - FileShare share) - { - return _wrappedFileSystem.GetFileStream( - path, - mode, - access, - share); - } - - public override string ReadFileAllText(string path) - { - return _wrappedFileSystem.ReadFileAllText(path); - } - - public override byte[] ReadFileAllBytes(string path) - { - return _wrappedFileSystem.ReadFileAllBytes(path); - } - - public override IEnumerable EnumerateFiles( - string path, - string searchPattern = "*", - SearchOption searchOption = SearchOption.TopDirectoryOnly) - { - return _wrappedFileSystem.EnumerateFiles(path, searchPattern, searchOption); - } - - public override IEnumerable EnumerateDirectories( - string path, - string searchPattern = "*", - SearchOption searchOption = SearchOption.TopDirectoryOnly) - { - return _wrappedFileSystem.EnumerateDirectories(path, searchPattern, searchOption); - } - - public override IEnumerable EnumerateFileSystemEntries( - string path, - string searchPattern = "*", - SearchOption searchOption = SearchOption.TopDirectoryOnly) - { - return _wrappedFileSystem.EnumerateFileSystemEntries(path, searchPattern, searchOption); - } - - public override FileAttributes GetAttributes(string path) - { - return _wrappedFileSystem.GetAttributes(path); - } - - public override DateTime GetLastWriteTimeUtc(string path) - { - return _wrappedFileSystem.GetLastWriteTimeUtc(path); - } - - public override bool DirectoryExists(string path) - { - return _wrappedFileSystem.DirectoryExists(path); - } - - public override bool FileExists(string path) - { - return _wrappedFileSystem.FileExists(path); - } - - public override bool FileOrDirectoryExists(string path) - { - return _wrappedFileSystem.DirectoryEntryExists(path); - } - } -} diff --git a/src/Build/FileSystem/MSBuildFileSystemAdapter.cs b/src/Build/FileSystem/MSBuildFileSystemAdapter.cs deleted file mode 100644 index 4c69284d955..00000000000 --- a/src/Build/FileSystem/MSBuildFileSystemAdapter.cs +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright (c) Microsoft. 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.IO; -using Microsoft.Build.Shared.FileSystem; - -namespace Microsoft.Build.FileSystem -{ - internal class MSBuildFileSystemAdapter : IFileSystem - { - private readonly MSBuildFileSystemBase _msbuildFileSystem; - public MSBuildFileSystemAdapter(MSBuildFileSystemBase msbuildFileSystem) - { - _msbuildFileSystem = msbuildFileSystem; - } - public TextReader ReadFile(string path) => _msbuildFileSystem.ReadFile(path); - - public Stream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share) => _msbuildFileSystem.GetFileStream(path, mode, access, share); - - public string ReadFileAllText(string path) => _msbuildFileSystem.ReadFileAllText(path); - - public byte[] ReadFileAllBytes(string path) => _msbuildFileSystem.ReadFileAllBytes(path); - - public IEnumerable EnumerateFiles(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) - { - return _msbuildFileSystem.EnumerateFiles(path, searchPattern, searchOption); - } - - public IEnumerable EnumerateDirectories(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) - { - return _msbuildFileSystem.EnumerateDirectories(path, searchPattern, searchOption); - } - - public IEnumerable EnumerateFileSystemEntries( - string path, - string searchPattern = "*", - SearchOption searchOption = SearchOption.TopDirectoryOnly) - { - return _msbuildFileSystem.EnumerateFileSystemEntries(path, searchPattern, searchOption); - } - - public FileAttributes GetAttributes(string path) => _msbuildFileSystem.GetAttributes(path); - - public DateTime GetLastWriteTimeUtc(string path) => _msbuildFileSystem.GetLastWriteTimeUtc(path); - - public bool DirectoryExists(string path) => _msbuildFileSystem.DirectoryExists(path); - - public bool FileExists(string path) => _msbuildFileSystem.FileExists(path); - - public bool DirectoryEntryExists(string path) => _msbuildFileSystem.FileOrDirectoryExists(path); - } -} diff --git a/src/Build/FileSystem/MSBuildFileSystemBase.cs b/src/Build/FileSystem/MSBuildFileSystemBase.cs index 5383e717a9b..8855cd50da4 100644 --- a/src/Build/FileSystem/MSBuildFileSystemBase.cs +++ b/src/Build/FileSystem/MSBuildFileSystemBase.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Microsoft.Build.Shared.FileSystem; using System; using System.Collections.Generic; using System.IO; @@ -14,66 +15,73 @@ namespace Microsoft.Build.FileSystem /// - must be thread safe /// - may cache some or all the calls. /// - public abstract class MSBuildFileSystemBase + public abstract class MSBuildFileSystemBase : IFileSystem { + #region IFileSystem implementation + /// /// Use this for var sr = new StreamReader(path) /// - public abstract TextReader ReadFile(string path); + public virtual TextReader ReadFile(string path) => FileSystems.Default.ReadFile(path); /// /// Use this for new FileStream(path, mode, access, share) /// - public abstract Stream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share); + public virtual Stream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share) => FileSystems.Default.GetFileStream(path, mode, access, share); /// /// Use this for File.ReadAllText(path) /// - public abstract string ReadFileAllText(string path); + public virtual string ReadFileAllText(string path) => FileSystems.Default.ReadFileAllText(path); /// /// Use this for File.ReadAllBytes(path) /// - public abstract byte[] ReadFileAllBytes(string path); + public virtual byte[] ReadFileAllBytes(string path) => FileSystems.Default.ReadFileAllBytes(path); /// /// Use this for Directory.EnumerateFiles(path, pattern, option) /// - public abstract IEnumerable EnumerateFiles(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly); + public virtual IEnumerable EnumerateFiles(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) + => FileSystems.Default.EnumerateFiles(path, searchPattern, searchOption); /// /// Use this for Directory.EnumerateFolders(path, pattern, option) /// - public abstract IEnumerable EnumerateDirectories(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly); + public virtual IEnumerable EnumerateDirectories(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) + => FileSystems.Default.EnumerateDirectories(path, searchPattern, searchOption); /// /// Use this for Directory.EnumerateFileSystemEntries(path, pattern, option) /// - public abstract IEnumerable EnumerateFileSystemEntries(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly); + public virtual IEnumerable EnumerateFileSystemEntries(string path, string searchPattern = "*", SearchOption searchOption = SearchOption.TopDirectoryOnly) + => FileSystems.Default.EnumerateFileSystemEntries(path, searchPattern, searchOption); /// /// Use this for File.GetAttributes() /// - public abstract FileAttributes GetAttributes(string path); + public virtual FileAttributes GetAttributes(string path) => FileSystems.Default.GetAttributes(path); /// /// Use this for File.GetLastWriteTimeUtc(path) /// - public abstract DateTime GetLastWriteTimeUtc(string path); + public virtual DateTime GetLastWriteTimeUtc(string path) => FileSystems.Default.GetLastWriteTimeUtc(path); /// /// Use this for Directory.Exists(path) /// - public abstract bool DirectoryExists(string path); + public virtual bool DirectoryExists(string path) => FileSystems.Default.DirectoryExists(path); /// /// Use this for File.Exists(path) /// - public abstract bool FileExists(string path); + public virtual bool FileExists(string path) => FileSystems.Default.FileExists(path); /// /// Use this for File.Exists(path) || Directory.Exists(path) /// - public abstract bool FileOrDirectoryExists(string path); + public virtual bool FileOrDirectoryExists(string path) => FileSystems.Default.FileOrDirectoryExists(path); + + #endregion } } diff --git a/src/Build/Globbing/MSBuildGlob.cs b/src/Build/Globbing/MSBuildGlob.cs index 0420aa9edd3..9460958a40f 100644 --- a/src/Build/Globbing/MSBuildGlob.cs +++ b/src/Build/Globbing/MSBuildGlob.cs @@ -7,6 +7,7 @@ using System.Text.RegularExpressions; using Microsoft.Build.Collections; using Microsoft.Build.Shared; +using Microsoft.Build.Utilities; using Microsoft.NET.StringTools; namespace Microsoft.Build.Globbing @@ -126,10 +127,13 @@ public MatchInfoResult MatchInfo(string stringToMatch) normalizedInput, _state.Value.Regex, out bool isMatch, - out string fixedDirectoryPart, out string wildcardDirectoryPart, out string filenamePart); + // We don't capture the fixed directory part in the regex but we can infer it from the other two. + int fixedDirectoryPartLength = normalizedInput.Length - wildcardDirectoryPart.Length - filenamePart.Length; + string fixedDirectoryPart = normalizedInput.Substring(0, fixedDirectoryPartLength); + return new MatchInfoResult(isMatch, fixedDirectoryPart, wildcardDirectoryPart, filenamePart); } @@ -202,8 +206,20 @@ public static MSBuildGlob Parse(string globRoot, string fileSpec) if (regex == null) { + RegexOptions regexOptions = FileMatcher.DefaultRegexOptions; // compile the regex since it's expected to be used multiple times - Regex newRegex = new Regex(matchFileExpression, FileMatcher.DefaultRegexOptions | RegexOptions.Compiled); + // For the kind of regexes used here, compilation on .NET Framework tends to be expensive and not worth the small + // run-time boost so it's enabled only on .NET Core by default. +#if RUNTIME_TYPE_NETCORE + bool compileRegex = true; +#else + bool compileRegex = !ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0); +#endif + if (compileRegex) + { + regexOptions |= RegexOptions.Compiled; + } + Regex newRegex = new Regex(matchFileExpression, regexOptions); lock (s_regexCache) { if (!s_regexCache.TryGetValue(matchFileExpression, out regex)) diff --git a/src/Build/Graph/ProjectGraph.cs b/src/Build/Graph/ProjectGraph.cs index b445f72d83d..40cf6aee0ed 100644 --- a/src/Build/Graph/ProjectGraph.cs +++ b/src/Build/Graph/ProjectGraph.cs @@ -10,19 +10,20 @@ using System.Linq; using System.Text; using System.Threading; -using Microsoft.Build.BackEnd; using Microsoft.Build.Evaluation; using Microsoft.Build.Eventing; using Microsoft.Build.Exceptions; using Microsoft.Build.Execution; using Microsoft.Build.Shared; +using Microsoft.Build.Shared.Debugging; +using Microsoft.Build.Utilities; namespace Microsoft.Build.Graph { /// /// Represents a graph of evaluated projects. /// - [DebuggerDisplay(@"#roots={GraphRoots.Count}, #nodes={ProjectNodes.Count}, #entryPoints={EntryPointNodes.Count}")] + [DebuggerDisplay(@"{DebuggerDisplayString()}")] public sealed class ProjectGraph { /// @@ -475,13 +476,16 @@ GraphConstructionMetrics EndMeasurement() } } - internal string ToDot() + internal string ToDot(IReadOnlyDictionary> targetsPerNode = null) { var nodeCount = 0; - return ToDot(node => nodeCount++.ToString()); + return ToDot(node => nodeCount++.ToString(), targetsPerNode); } - internal string ToDot(Func nodeIdProvider) + internal string ToDot( + Func nodeIdProvider, + IReadOnlyDictionary> targetsPerNode = null + ) { ErrorUtilities.VerifyThrowArgumentNull(nodeIdProvider, nameof(nodeIdProvider)); @@ -489,31 +493,56 @@ internal string ToDot(Func nodeIdProvider) var sb = new StringBuilder(); - sb.Append("digraph g\n{\n\tnode [shape=box]\n"); + sb.AppendLine($"/* {DebuggerDisplayString()} */"); + + sb.AppendLine("digraph g") + .AppendLine("{") + .AppendLine("\tnode [shape=box]"); foreach (var node in ProjectNodes) { - var nodeId = nodeIds.GetOrAdd(node, (n, idProvider) => idProvider(n), nodeIdProvider); + var nodeId = GetNodeId(node); var nodeName = Path.GetFileNameWithoutExtension(node.ProjectInstance.FullPath); + var globalPropertiesString = string.Join( "
", node.ProjectInstance.GlobalProperties.OrderBy(kvp => kvp.Key) .Select(kvp => $"{kvp.Key}={kvp.Value}")); - sb.Append('\t').Append(nodeId).Append(" [label=<").Append(nodeName).Append("
").Append(globalPropertiesString).AppendLine(">]"); + var targetListString = GetTargetListString(node); + + sb.AppendLine($"\t{nodeId} [label=<{nodeName}
({targetListString})
{globalPropertiesString}>]"); foreach (var reference in node.ProjectReferences) { - var referenceId = nodeIds.GetOrAdd(reference, (n, idProvider) => idProvider(n), nodeIdProvider); + var referenceId = GetNodeId(reference); - sb.Append('\t').Append(nodeId).Append(" -> ").AppendLine(referenceId); + sb.AppendLine($"\t{nodeId} -> {referenceId}"); } } sb.Append("}"); return sb.ToString(); + + string GetNodeId(ProjectGraphNode node) + { + return nodeIds.GetOrAdd(node, (n, idProvider) => idProvider(n), nodeIdProvider); + } + + string GetTargetListString(ProjectGraphNode node) + { + var targetListString = targetsPerNode is null + ? string.Empty + : string.Join(", ", targetsPerNode[node]); + return targetListString; + } + } + + private string DebuggerDisplayString() + { + return $"#roots={GraphRoots.Count}, #nodes={ProjectNodes.Count}, #entryPoints={EntryPointNodes.Count}"; } private static IReadOnlyCollection TopologicalSort( diff --git a/src/Build/Graph/ProjectInterpretation.cs b/src/Build/Graph/ProjectInterpretation.cs index ae88e1064b0..99362abab30 100644 --- a/src/Build/Graph/ProjectInterpretation.cs +++ b/src/Build/Graph/ProjectInterpretation.cs @@ -100,17 +100,17 @@ public IEnumerable GetReferences(ProjectInstance requesterInstanc } } - private static string GetInnerBuildPropertyValue(ProjectInstance project) + internal static string GetInnerBuildPropertyValue(ProjectInstance project) { return project.GetPropertyValue(GetInnerBuildPropertyName(project)); } - private static string GetInnerBuildPropertyName(ProjectInstance project) + internal static string GetInnerBuildPropertyName(ProjectInstance project) { return project.GetPropertyValue(PropertyNames.InnerBuildProperty); } - private static string GetInnerBuildPropertyValues(ProjectInstance project) + internal static string GetInnerBuildPropertyValues(ProjectInstance project) { return project.GetPropertyValue(project.GetPropertyValue(PropertyNames.InnerBuildPropertyValues)); } diff --git a/src/Build/Instance/ProjectInstance.cs b/src/Build/Instance/ProjectInstance.cs index e29d3346621..b67aba3de21 100644 --- a/src/Build/Instance/ProjectInstance.cs +++ b/src/Build/Instance/ProjectInstance.cs @@ -564,8 +564,7 @@ private ProjectInstance(ProjectInstance that, bool isImmutable, RequestedProject _hostServices = that._hostServices; _isImmutable = isImmutable; _evaluationId = that.EvaluationId; - - TranslateEntireState = that.TranslateEntireState; + _translateEntireState = that._translateEntireState; if (filter == null) { @@ -849,23 +848,8 @@ public List EvaluatedItemElements ///
public bool TranslateEntireState { - get - { - return Traits.Instance.EscapeHatches.ProjectInstanceTranslation switch - { - EscapeHatches.ProjectInstanceTranslationMode.Full => true, - EscapeHatches.ProjectInstanceTranslationMode.Partial => false, - _ => _translateEntireState, - }; - } - - set - { - if (Traits.Instance.EscapeHatches.ProjectInstanceTranslation == null) - { - _translateEntireState = value; - } - } + get => _translateEntireState; + set => _translateEntireState = value; } /// @@ -899,8 +883,7 @@ public string Directory public string FullPath { [DebuggerStepThrough] - get - { return _projectFileLocation.File; } + get => _projectFileLocation?.File ?? string.Empty; } /// @@ -2019,9 +2002,15 @@ internal void LateInitialize(ProjectRootElementCacheBase projectRootElementCache /// void ITranslatable.Translate(ITranslator translator) { + if (translator.Mode == TranslationDirection.WriteToStream) + { + // When serializing into stream apply Traits.Instance.EscapeHatches.ProjectInstanceTranslation if defined. + MaybeForceTranslateEntireStateMode(); + } + translator.Translate(ref _translateEntireState); - if (TranslateEntireState) + if (_translateEntireState) { TranslateAllState(translator); } @@ -2031,6 +2020,27 @@ void ITranslatable.Translate(ITranslator translator) } } + private void MaybeForceTranslateEntireStateMode() + { + var forcedProjectInstanceTranslationMode = Traits.Instance.EscapeHatches.ProjectInstanceTranslation; + if (forcedProjectInstanceTranslationMode != null) + { + switch (forcedProjectInstanceTranslationMode) + { + case EscapeHatches.ProjectInstanceTranslationMode.Full: + _translateEntireState = true; + break; + case EscapeHatches.ProjectInstanceTranslationMode.Partial: + _translateEntireState = false; + break; + default: + // if EscapeHatches.ProjectInstanceTranslation has an unexpected value, do not force TranslateEntireStateMode. + // Just leave it as is. + break; + } + } + } + internal void TranslateMinimalState(ITranslator translator) { translator.TranslateDictionary(ref _globalProperties, ProjectPropertyInstance.FactoryForDeserialization); diff --git a/src/Build/Instance/TaskFactories/TaskHostTask.cs b/src/Build/Instance/TaskFactories/TaskHostTask.cs index c080e9338e3..99bc7663895 100644 --- a/src/Build/Instance/TaskFactories/TaskHostTask.cs +++ b/src/Build/Instance/TaskFactories/TaskHostTask.cs @@ -268,6 +268,7 @@ public bool Execute() BuildEngine.ContinueOnError, _taskType.Type.FullName, AssemblyUtilities.GetAssemblyLocation(_taskType.Type.GetTypeInfo().Assembly), + _buildComponentHost.BuildParameters.LogTaskInputs, _setParameters, new Dictionary(_buildComponentHost.BuildParameters.GlobalProperties), _taskLoggingContext.GetWarningsAsErrors(), diff --git a/src/Build/Logging/BaseConsoleLogger.cs b/src/Build/Logging/BaseConsoleLogger.cs index 5a3752d7b04..d23ea16eca0 100644 --- a/src/Build/Logging/BaseConsoleLogger.cs +++ b/src/Build/Logging/BaseConsoleLogger.cs @@ -2,19 +2,21 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections; using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; using System.Text; + +using Microsoft.Build.Evaluation; using Microsoft.Build.Framework; using Microsoft.Build.Internal; using Microsoft.Build.Shared; -using System.Collections; -using System.Globalization; -using System.IO; using ColorSetter = Microsoft.Build.Logging.ColorSetter; using ColorResetter = Microsoft.Build.Logging.ColorResetter; using WriteHandler = Microsoft.Build.Logging.WriteHandler; -using Microsoft.Build.Exceptions; namespace Microsoft.Build.BackEnd.Logging { @@ -329,6 +331,33 @@ internal void IsRunningWithCharacterFileType() /// internal bool IsVerbosityAtLeast(LoggerVerbosity checkVerbosity) => Verbosity >= checkVerbosity; + /// + /// Returns the minimum logger verbosity required to log a message with the given importance. + /// + /// The message importance. + /// True if the message should be rendered using lighter colored text. + /// The logger verbosity required to log a message of the given . + internal static LoggerVerbosity ImportanceToMinimumVerbosity(MessageImportance importance, out bool lightenText) + { + switch (importance) + { + case MessageImportance.High: + lightenText = false; + return LoggerVerbosity.Minimal; + case MessageImportance.Normal: + lightenText = true; + return LoggerVerbosity.Normal; + case MessageImportance.Low: + lightenText = true; + return LoggerVerbosity.Detailed; + + default: + ErrorUtilities.VerifyThrow(false, "Impossible"); + lightenText = false; + return LoggerVerbosity.Detailed; + } + } + /// /// Sets foreground color to color specified /// @@ -713,42 +742,56 @@ internal SortedList ExtractItemList(IEnumerable items) ///
internal virtual void OutputItems(string itemType, ArrayList itemTypeList) { - // Write each item, one per line - bool haveWrittenItemType = false; - setColor(ConsoleColor.DarkGray); - foreach (ITaskItem item in itemTypeList) + WriteItemType(itemType); + + foreach (var item in itemTypeList) { - if (!haveWrittenItemType) + string itemSpec = item switch { - setColor(ConsoleColor.Gray); - WriteLinePretty(itemType); - haveWrittenItemType = true; - setColor(ConsoleColor.DarkGray); - } - WriteLinePretty(" " /* indent slightly*/ + item.ItemSpec); + ITaskItem taskItem => taskItem.ItemSpec, + IItem iitem => iitem.EvaluatedInclude, + { } misc => Convert.ToString(misc), + null => "null" + }; - IDictionary metadata = item.CloneCustomMetadata(); + WriteItemSpec(itemSpec); - foreach (DictionaryEntry metadatum in metadata) + var metadata = item switch { - string valueOrError; - try - { - valueOrError = item.GetMetadata(metadatum.Key as string); - } - catch (InvalidProjectFileException e) + IMetadataContainer metadataContainer => metadataContainer.EnumerateMetadata(), + IItem iitem => iitem.Metadata?.Select(m => new KeyValuePair(m.Name, m.EvaluatedValue)), + _ => null + }; + + if (metadata != null) + { + foreach (var metadatum in metadata) { - valueOrError = e.Message; + WriteMetadata(metadatum.Key, metadatum.Value); } - - // A metadatum's "value" is its escaped value, since that's how we represent them internally. - // So unescape before returning to the world at large. - WriteLinePretty(" " + metadatum.Key + " = " + valueOrError); } } + resetColor(); } + protected virtual void WriteItemType(string itemType) + { + setColor(ConsoleColor.Gray); + WriteLinePretty(itemType); + setColor(ConsoleColor.DarkGray); + } + + protected virtual void WriteItemSpec(string itemSpec) + { + WriteLinePretty(" " + itemSpec); + } + + protected virtual void WriteMetadata(string name, string value) + { + WriteLinePretty(" " + name + " = " + value); + } + /// /// Returns a performance counter for a given scope (either task name or target name) /// from the given table. @@ -1030,6 +1073,12 @@ public virtual void Initialize(IEventSource eventSource) eventSource.MessageRaised += MessageHandler; eventSource.CustomEventRaised += CustomEventHandler; eventSource.StatusEventRaised += StatusEventHandler; + + bool logPropertiesAndItemsAfterEvaluation = Utilities.Traits.Instance.EscapeHatches.LogPropertiesAndItemsAfterEvaluation ?? true; + if (logPropertiesAndItemsAfterEvaluation && eventSource is IEventSource4 eventSource4) + { + eventSource4.IncludeEvaluationPropertiesAndItems(); + } } } diff --git a/src/Build/Logging/BinaryLogger/BinaryLogger.cs b/src/Build/Logging/BinaryLogger/BinaryLogger.cs index 6bcd2951e2d..dac426db8b4 100644 --- a/src/Build/Logging/BinaryLogger/BinaryLogger.cs +++ b/src/Build/Logging/BinaryLogger/BinaryLogger.cs @@ -48,7 +48,10 @@ public sealed class BinaryLogger : ILogger // version 13: // - don't log Message where it can be recovered // - log arguments for LazyFormattedBuildEventArgs - internal const int FileFormatVersion = 13; + // - TargetSkippedEventArgs: added OriginallySucceeded, Condition, EvaluatedCondition + // version 14: + // - TargetSkippedEventArgs: added SkipReason, OriginalBuildEventContext + internal const int FileFormatVersion = 14; private Stream stream; private BinaryWriter binaryWriter; diff --git a/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs b/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs index 47c1f6d8306..c6be1d59db3 100644 --- a/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs +++ b/src/Build/Logging/BinaryLogger/BuildEventArgsReader.cs @@ -51,8 +51,6 @@ public class BuildEventArgsReader : IDisposable typeof(BuildEventArgs).GetField("threadId", BindingFlags.Instance | BindingFlags.NonPublic); private static FieldInfo buildEventArgsFieldSenderName = typeof(BuildEventArgs).GetField("senderName", BindingFlags.Instance | BindingFlags.NonPublic); - private static FieldInfo buildEventArgsFieldTimestamp = - typeof(BuildEventArgs).GetField("timestamp", BindingFlags.Instance | BindingFlags.NonPublic); /// /// Initializes a new instance of BuildEventArgsReader using a BinaryReader instance @@ -297,15 +295,30 @@ private BuildEventArgs ReadTargetSkippedEventArgs() string condition = null; string evaluatedCondition = null; bool originallySucceeded = false; + TargetSkipReason skipReason = TargetSkipReason.None; + BuildEventContext originalBuildEventContext = null; if (fileFormatVersion >= 13) { condition = ReadOptionalString(); evaluatedCondition = ReadOptionalString(); originallySucceeded = ReadBoolean(); + + // Attempt to infer skip reason from the data we have + skipReason = condition != null ? + TargetSkipReason.ConditionWasFalse // condition expression only stored when false + : originallySucceeded ? + TargetSkipReason.PreviouslyBuiltSuccessfully + : TargetSkipReason.PreviouslyBuiltUnsuccessfully; } var buildReason = (TargetBuiltReason)ReadInt32(); + if (fileFormatVersion >= 14) + { + skipReason = (TargetSkipReason)ReadInt32(); + originalBuildEventContext = binaryReader.ReadOptionalBuildEventContext(); + } + var e = new TargetSkippedEventArgs( fields.Message, fields.Arguments); @@ -320,6 +333,8 @@ private BuildEventArgs ReadTargetSkippedEventArgs() e.Condition = condition; e.EvaluatedCondition = evaluatedCondition; e.OriginallySucceeded = originallySucceeded; + e.SkipReason = skipReason; + e.OriginalBuildEventContext = originalBuildEventContext; return e; } @@ -535,6 +550,8 @@ private BuildEventArgs ReadTaskStartedEventArgs() taskFile, taskName, fields.Timestamp); + e.LineNumber = fields.LineNumber; + e.ColumnNumber = fields.ColumnNumber; SetCommonFields(e, fields); return e; } @@ -658,7 +675,9 @@ private BuildEventArgs ReadTaskParameterEventArgs() itemType, items, logItemMetadata: true, - fields.Timestamp); + fields.Timestamp, + fields.LineNumber, + fields.ColumnNumber); e.ProjectFile = fields.ProjectFile; return e; } diff --git a/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs b/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs index 20cd4232cb2..20583b991b0 100644 --- a/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs +++ b/src/Build/Logging/BinaryLogger/BuildEventArgsWriter.cs @@ -370,7 +370,9 @@ private void Write(TargetFinishedEventArgs e) private void Write(TaskStartedEventArgs e) { Write(BinaryLogRecordKind.TaskStarted); - WriteBuildEventArgsFields(e, writeMessage: false); + WriteBuildEventArgsFields(e, writeMessage: false, writeLineAndColumn: true); + Write(e.LineNumber); + Write(e.ColumnNumber); WriteDeduplicatedString(e.TaskName); WriteDeduplicatedString(e.ProjectFile); WriteDeduplicatedString(e.TaskFile); @@ -390,6 +392,7 @@ private void Write(BuildErrorEventArgs e) { Write(BinaryLogRecordKind.Error); WriteBuildEventArgsFields(e); + WriteArguments(e.RawArguments); WriteDeduplicatedString(e.Subcategory); WriteDeduplicatedString(e.Code); WriteDeduplicatedString(e.File); @@ -404,6 +407,7 @@ private void Write(BuildWarningEventArgs e) { Write(BinaryLogRecordKind.Warning); WriteBuildEventArgsFields(e); + WriteArguments(e.RawArguments); WriteDeduplicatedString(e.Subcategory); WriteDeduplicatedString(e.Code); WriteDeduplicatedString(e.File); @@ -454,6 +458,8 @@ private void Write(TargetSkippedEventArgs e) WriteDeduplicatedString(e.EvaluatedCondition); Write(e.OriginallySucceeded); Write((int)e.BuildReason); + Write((int)e.SkipReason); + binaryWriter.WriteOptionalBuildEventContext(e.OriginalBuildEventContext); } private void Write(CriticalBuildMessageEventArgs e) @@ -512,9 +518,14 @@ private void Write(TaskParameterEventArgs e) WriteTaskItemList(e.Items, e.LogItemMetadata); } - private void WriteBuildEventArgsFields(BuildEventArgs e, bool writeMessage = true) + private void WriteBuildEventArgsFields(BuildEventArgs e, bool writeMessage = true, bool writeLineAndColumn = false) { var flags = GetBuildEventArgsFieldFlags(e, writeMessage); + if (writeLineAndColumn) + { + flags |= BuildEventArgsFieldFlags.LineNumber | BuildEventArgsFieldFlags.ColumnNumber; + } + Write((int)flags); WriteBaseFields(e, flags); } @@ -555,7 +566,7 @@ private void WriteBaseFields(BuildEventArgs e, BuildEventArgsFieldFlags flags) private void WriteMessageFields(BuildMessageEventArgs e, bool writeMessage = true, bool writeImportance = false) { var flags = GetBuildEventArgsFieldFlags(e, writeMessage); - flags = GetMessageFlags(e, flags, writeMessage, writeImportance); + flags = GetMessageFlags(e, flags, writeImportance); Write((int)flags); @@ -603,12 +614,7 @@ private void WriteMessageFields(BuildMessageEventArgs e, bool writeMessage = tru if ((flags & BuildEventArgsFieldFlags.Arguments) != 0) { - Write(e.RawArguments.Length); - for (int i = 0; i < e.RawArguments.Length; i++) - { - string argument = Convert.ToString(e.RawArguments[i], CultureInfo.CurrentCulture); - WriteDeduplicatedString(argument); - } + WriteArguments(e.RawArguments); } if ((flags & BuildEventArgsFieldFlags.Importance) != 0) @@ -617,7 +623,23 @@ private void WriteMessageFields(BuildMessageEventArgs e, bool writeMessage = tru } } - private static BuildEventArgsFieldFlags GetMessageFlags(BuildMessageEventArgs e, BuildEventArgsFieldFlags flags, bool writeMessage = true, bool writeImportance = false) + private void WriteArguments(object[] arguments) + { + if (arguments == null || arguments.Length == 0) + { + return; + } + + int count = arguments.Length; + Write(count); + for (int i = 0; i < count; i++) + { + string argument = Convert.ToString(arguments[i], CultureInfo.CurrentCulture); + WriteDeduplicatedString(argument); + } + } + + private static BuildEventArgsFieldFlags GetMessageFlags(BuildMessageEventArgs e, BuildEventArgsFieldFlags flags, bool writeImportance = false) { if (e.Subcategory != null) { @@ -659,11 +681,6 @@ private static BuildEventArgsFieldFlags GetMessageFlags(BuildMessageEventArgs e, flags |= BuildEventArgsFieldFlags.EndColumnNumber; } - if (writeMessage && e.RawArguments != null && e.RawArguments.Length > 0) - { - flags |= BuildEventArgsFieldFlags.Arguments; - } - if (writeImportance && e.Importance != MessageImportance.Low) { flags |= BuildEventArgsFieldFlags.Importance; @@ -688,6 +705,14 @@ private static BuildEventArgsFieldFlags GetBuildEventArgsFieldFlags(BuildEventAr if (writeMessage) { flags |= BuildEventArgsFieldFlags.Message; + + // We're only going to write the arguments for messages, + // warnings and errors. Only set the flag for these. + if (e is LazyFormattedBuildEventArgs { RawArguments: { Length: > 0 } } and + (BuildMessageEventArgs or BuildWarningEventArgs or BuildErrorEventArgs)) + { + flags |= BuildEventArgsFieldFlags.Arguments; + } } // no need to waste space for the default sender name @@ -696,12 +721,6 @@ private static BuildEventArgsFieldFlags GetBuildEventArgsFieldFlags(BuildEventAr flags |= BuildEventArgsFieldFlags.SenderName; } - // ThreadId never seems to be used or useful for anything. - //if (e.ThreadId > 0) - //{ - // flags |= BuildEventArgsFieldFlags.ThreadId; - //} - if (e.Timestamp != default(DateTime)) { flags |= BuildEventArgsFieldFlags.Timestamp; diff --git a/src/Build/Logging/ConsoleLogger.cs b/src/Build/Logging/ConsoleLogger.cs index 3af25e3ce19..c6358a9badb 100644 --- a/src/Build/Logging/ConsoleLogger.cs +++ b/src/Build/Logging/ConsoleLogger.cs @@ -164,8 +164,6 @@ private void InitializeBaseConsoleLogger() _parameters = null; } - - _consoleLogger.SkipProjectStartedText = _skipProjectStartedText; } @@ -477,6 +475,31 @@ public void CustomEventHandler(object sender, CustomBuildEventArgs e) _consoleLogger.CustomEventHandler(sender, e); } + /// + /// Returns the minimum importance of messages logged by this logger. + /// + /// + /// The minimum message importance corresponding to this logger's verbosity or (MessageImportance.High - 1) + /// if this logger does not log messages of any importance. + /// + internal MessageImportance GetMinimumMessageImportance() + { + if (Verbosity >= BaseConsoleLogger.ImportanceToMinimumVerbosity(MessageImportance.Low, out _)) + { + return MessageImportance.Low; + } + else if (Verbosity >= BaseConsoleLogger.ImportanceToMinimumVerbosity(MessageImportance.Normal, out _)) + { + return MessageImportance.Normal; + } + else if (Verbosity >= BaseConsoleLogger.ImportanceToMinimumVerbosity(MessageImportance.High, out _)) + { + return MessageImportance.High; + } + // The logger does not log messages of any importance. + return MessageImportance.High - 1; + } + #endregion } } diff --git a/src/Build/Logging/DistributedLoggers/ConfigurableForwardingLogger.cs b/src/Build/Logging/DistributedLoggers/ConfigurableForwardingLogger.cs index d530bd07264..83c2499aefa 100644 --- a/src/Build/Logging/DistributedLoggers/ConfigurableForwardingLogger.cs +++ b/src/Build/Logging/DistributedLoggers/ConfigurableForwardingLogger.cs @@ -75,7 +75,7 @@ public int NodeId /// private void InitializeForwardingTable() { - _forwardingTable = new Dictionary(15, StringComparer.OrdinalIgnoreCase); + _forwardingTable = new Dictionary(17, StringComparer.OrdinalIgnoreCase); _forwardingTable[BuildStartedEventDescription] = 0; _forwardingTable[BuildFinishedEventDescription] = 0; _forwardingTable[ProjectStartedEventDescription] = 0; @@ -258,6 +258,31 @@ private void SetForwardingBasedOnVerbosity() } } + /// + /// Returns the minimum importance of messages logged by this logger. + /// + /// + /// The minimum message importance corresponding to this logger's verbosity or (MessageImportance.High - 1) + /// if this logger does not log messages of any importance. + /// + internal MessageImportance GetMinimumMessageImportance() + { + if (_forwardingTable[LowMessageEventDescription] == 1) + { + return MessageImportance.Low; + } + if (_forwardingTable[NormalMessageEventDescription] == 1) + { + return MessageImportance.Normal; + } + if (_forwardingTable[HighMessageEventDescription] == 1) + { + return MessageImportance.High; + } + // The logger does not log messages of any importance. + return MessageImportance.High - 1; + } + /// /// Reset the states of per-build member variables. /// Used when a build is finished, but the logger might be needed for the next build. diff --git a/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs b/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs index edcbe439efe..26ed295bc8b 100644 --- a/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs +++ b/src/Build/Logging/ParallelLogger/ParallelConsoleLogger.cs @@ -540,20 +540,38 @@ public override void ProjectStartedHandler(object sender, ProjectStartedEventArg } } - ReadProjectConfigurationDescription(e.BuildEventContext, e.Items); + var projectKey = (e.BuildEventContext.NodeId, e.BuildEventContext.ProjectContextId); + + // If the value is available at all, it will be either in the items + // from ProjectStarted (old behavior), or the items from ProjectEvaluationFinished (new behavior). + // First try the old behavior, and fallback to the new behavior. + var result = ReadProjectConfigurationDescription(e.Items); + if (result != null) + { + // Found the items directly on ProjectStarted + propertyOutputMap[projectKey] = result; + } + else + { + // Try to see if we saw the items on the corresponding ProjectEvaluationFinished + var evaluationKey = GetEvaluationKey(e.BuildEventContext); + + // if the value was set from ProjectEvaluationFinished, copy it into the entry + // for this project + if (propertyOutputMap.TryGetValue(evaluationKey, out string value)) + { + propertyOutputMap[projectKey] = value; + } + } } - private void ReadProjectConfigurationDescription(BuildEventContext buildEventContext, IEnumerable items) + private string ReadProjectConfigurationDescription(IEnumerable items) { - if (buildEventContext == null || items == null) + if (items == null) { - return; + return null; } - // node and project context ids for the propertyOutputMap key. - int nodeID = buildEventContext.NodeId; - int projectContextId = buildEventContext.ProjectContextId; - ReuseableStringBuilder projectConfigurationDescription = null; Internal.Utilities.EnumerateItems(items, item => @@ -578,14 +596,27 @@ private void ReadProjectConfigurationDescription(BuildEventContext buildEventCon } }); - // Add the finished dictionary to propertyOutputMap. if (projectConfigurationDescription != null) { - propertyOutputMap.Add((nodeID, projectContextId), projectConfigurationDescription.ToString()); + var result = projectConfigurationDescription.ToString(); (projectConfigurationDescription as IDisposable)?.Dispose(); + return result; } + + return null; } + /// + /// In case the items are stored on ProjectEvaluationFinishedEventArgs + /// (new behavior), we first store the value per evaluation, and then + /// in ProjectStarted, find the value from the project's evaluation + /// and use that. + /// + private (int, int) GetEvaluationKey(BuildEventContext buildEventContext) + // note that we use a negative number for evaluations so that we don't conflict + // with project context ids. + => (buildEventContext.NodeId, -buildEventContext.EvaluationId); + /// /// Handler for project finished events /// @@ -751,44 +782,23 @@ internal void WriteItems(BuildEventArgs e, IEnumerable items) ShownBuildEventContext(e.BuildEventContext); } - internal override void OutputItems(string itemType, ArrayList itemTypeList) + protected override void WriteItemType(string itemType) { - // Write each item, one per line - bool haveWrittenItemType = false; - foreach (ITaskItem item in itemTypeList) - { - if (!haveWrittenItemType) - { - setColor(ConsoleColor.DarkGray); - WriteMessageAligned(itemType, false); - haveWrittenItemType = true; - } - setColor(ConsoleColor.Gray); - - // Indent the text by two tab lengths - StringBuilder result = new StringBuilder((2 * tabWidth) + item.ItemSpec.Length); - result.Append(' ', 2 * tabWidth).Append(item.ItemSpec); - WriteMessageAligned(result.ToString(), false); - - IDictionary metadata = item.CloneCustomMetadata(); + setColor(ConsoleColor.DarkGray); + WriteMessageAligned(itemType, prefixAlreadyWritten: false); + setColor(ConsoleColor.Gray); + } - foreach (DictionaryEntry metadatum in metadata) - { - string valueOrError; - try - { - valueOrError = item.GetMetadata(metadatum.Key as string); - } - catch (InvalidProjectFileException e) - { - valueOrError = e.Message; - } + protected override void WriteItemSpec(string itemSpec) + { + WriteMessageAligned(new string(' ', 2 * tabWidth) + itemSpec, prefixAlreadyWritten: false); + } - WriteMessageAligned($"{new string(' ', 4 * tabWidth)}{metadatum.Key} = {valueOrError}", false); - } - } - resetColor(); + protected override void WriteMetadata(string name, string value) + { + WriteMessageAligned($"{new string(' ', 4 * tabWidth)}{name} = {value}", prefixAlreadyWritten: false); } + /// /// Handler for target started events /// @@ -962,16 +972,16 @@ public override void TaskFinishedHandler(object sender, TaskFinishedEventArgs e) /// /// Finds the LogOutProperty string to be printed in messages. /// - /// Build event to extract context information from. + /// Build event to extract context information from. internal string FindLogOutputProperties(BuildEventArgs e) { string projectConfigurationDescription = String.Empty; if (e.BuildEventContext != null) { - int nodeId = e.BuildEventContext.NodeId; - int projectContextId = e.BuildEventContext.ProjectContextId; - propertyOutputMap.TryGetValue((nodeId, projectContextId), out projectConfigurationDescription); + var key = (e.BuildEventContext.NodeId, e.BuildEventContext.ProjectContextId); + propertyOutputMap.TryGetValue(key, out projectConfigurationDescription); } + return projectConfigurationDescription; } @@ -1090,23 +1100,8 @@ public override void MessageHandler(object sender, BuildMessageEventArgs e) } else { - switch (e.Importance) - { - case MessageImportance.High: - print = IsVerbosityAtLeast(LoggerVerbosity.Minimal); - break; - case MessageImportance.Normal: - print = IsVerbosityAtLeast(LoggerVerbosity.Normal); - lightenText = true; - break; - case MessageImportance.Low: - print = IsVerbosityAtLeast(LoggerVerbosity.Detailed); - lightenText = true; - break; - default: - ErrorUtilities.VerifyThrow(false, "Impossible"); - break; - } + LoggerVerbosity minimumVerbosity = ImportanceToMinimumVerbosity(e.Importance, out lightenText); + print = IsVerbosityAtLeast(minimumVerbosity); } if (print) @@ -1168,7 +1163,12 @@ public override void StatusEventHandler(object sender, BuildStatusEventArgs e) } } - ReadProjectConfigurationDescription(projectEvaluationFinished.BuildEventContext, projectEvaluationFinished.Items); + var value = ReadProjectConfigurationDescription(projectEvaluationFinished.Items); + if (value != null) + { + var evaluationKey = GetEvaluationKey(e.BuildEventContext); + propertyOutputMap[evaluationKey] = value; + } } } diff --git a/src/Build/Logging/SerialConsoleLogger.cs b/src/Build/Logging/SerialConsoleLogger.cs index c47c5ed976d..9deedd88b0e 100644 --- a/src/Build/Logging/SerialConsoleLogger.cs +++ b/src/Build/Logging/SerialConsoleLogger.cs @@ -497,28 +497,8 @@ public override void WarningHandler(object sender, BuildWarningEventArgs e) /// public override void MessageHandler(object sender, BuildMessageEventArgs e) { - bool print = false; - bool lightenText = false; - switch (e.Importance) - { - case MessageImportance.High: - print = IsVerbosityAtLeast(LoggerVerbosity.Minimal); - break; - - case MessageImportance.Normal: - print = IsVerbosityAtLeast(LoggerVerbosity.Normal); - lightenText = true; - break; - - case MessageImportance.Low: - print = IsVerbosityAtLeast(LoggerVerbosity.Detailed); - lightenText = true; - break; - - default: - ErrorUtilities.VerifyThrow(false, "Impossible"); - break; - } + LoggerVerbosity minimumVerbosity = ImportanceToMinimumVerbosity(e.Importance, out bool lightenText); + bool print = IsVerbosityAtLeast(minimumVerbosity); if (print) { diff --git a/src/Build/Microsoft.Build.csproj b/src/Build/Microsoft.Build.csproj index 43d9b794046..d08e337c5da 100644 --- a/src/Build/Microsoft.Build.csproj +++ b/src/Build/Microsoft.Build.csproj @@ -4,8 +4,8 @@ - net5.0 - $(FullFrameworkTFM);net5.0 + net6.0 + $(FullFrameworkTFM);net6.0 $(RuntimeOutputTargetFrameworks) Microsoft.Build Microsoft.Build @@ -20,7 +20,7 @@ true This package contains the $(MSBuildProjectName) assembly which is used to create, edit, and evaluate MSBuild projects. false - partial + full $(NoWarn);NU5104 @@ -157,6 +157,7 @@ + @@ -264,6 +265,8 @@ + + diff --git a/src/Build/Resources/Constants.cs b/src/Build/Resources/Constants.cs index 8d03cf5a8a2..a327906f7ef 100644 --- a/src/Build/Resources/Constants.cs +++ b/src/Build/Resources/Constants.cs @@ -51,6 +51,8 @@ internal static class ReservedPropertyNames internal const string programFiles32 = "MSBuildProgramFiles32"; internal const string localAppData = "LocalAppData"; internal const string assemblyVersion = "MSBuildAssemblyVersion"; + internal const string fileVersion = "MSBuildFileVersion"; + internal const string semanticVersion = "MSBuildSemanticVersion"; internal const string version = "MSBuildVersion"; internal const string osName = "OS"; internal const string frameworkToolsRoot = "MSBuildFrameworkToolsRoot"; diff --git a/src/Build/Resources/Strings.resx b/src/Build/Resources/Strings.resx index 9bce56de852..db27d0712c8 100644 --- a/src/Build/Resources/Strings.resx +++ b/src/Build/Resources/Strings.resx @@ -1285,13 +1285,15 @@ MSB4244: The SDK resolver assembly "{0}" could not be loaded. {1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} + + MSB4242: SDK Resolver Failure: "{0}" {StrBegin="MSB4242: "} + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} MSB4245: SDK Resolver manifest file is invalid. This may indicate a corrupt or invalid installation of MSBuild. Manifest file path '{0}'. Message: {1} @@ -1353,7 +1355,7 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} {StrBegin="MSB4213: "} @@ -1894,4 +1896,7 @@ Utilization: {0} Average Utilization: {1:###.0} Killing process with pid = {0}. + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + diff --git a/src/Build/Resources/xlf/Strings.cs.xlf b/src/Build/Resources/xlf/Strings.cs.xlf index f223fa5d580..bdf7ebe0340 100644 --- a/src/Build/Resources/xlf/Strings.cs.xlf +++ b/src/Build/Resources/xlf/Strings.cs.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ Počáteční hodnota vlastnosti: $({0})={1} Zdroj: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: Zakázání uzlu inproc způsobí snížení výkonu při používání modulů plug-in mezipaměti projektu, které vysílají žádosti o sestavení proxy serveru. + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: Selhání překladače sady SDK: {0} + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + Překladač sady SDK {0} selhal při pokusu o překlad sady SDK {1}. Výjimka: {2} + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: Projekt {0} přeskočil omezení izolace grafu v odkazovaném projektu {1}. @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: Určené spřažení požadavku {0} je v konfliktu s předchozím spřažením {1} určeným pro tento projekt. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: Určené spřažení požadavku {0} je v konfliktu s předchozím spřažením {1} určeným pro projekt {2} s globálními vlastnostmi {3}. {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Využití: Průměrné využití {0}: {1:###.0} MSB4244: Sestavení překladače sady SDK {0} nebylo možné načíst. {1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: Překladač sady SDK {0} se nepodařilo spustit. {1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: Překladač sady SDK založený na NuGet se nepodařilo spustit, protože sestavení NuGet se nenašla. Zkontrolujte instalaci nástroje MSBuild nebo nastavte proměnnou prostředí {0} na složku, která obsahuje požadovaná sestavení NuGet. {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + Překladač sady SDK založený na NuGet se nepodařilo spustit, protože sestavení NuGet se nenašla. Zkontrolujte instalaci nástroje MSBuild nebo nastavte proměnnou prostředí {0} na složku, která obsahuje požadovaná sestavení NuGet. {1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.de.xlf b/src/Build/Resources/xlf/Strings.de.xlf index 14323a1cb89..189016ff8b6 100644 --- a/src/Build/Resources/xlf/Strings.de.xlf +++ b/src/Build/Resources/xlf/Strings.de.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ Anfangswert der Eigenschaft: $({0})="{1}", Quelle: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: Das Deaktivieren des In-Process-Knotens führt zu Leistungseinbußen bei der Verwendung von Projektcache-Plug-Ins, die Proxybuildanforderungen ausgeben. + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: Fehler bei SDK-Resolver: "{0}" + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + Ausfall beim Versuch des SDK-Resolver "{0}", das SDK "{1}" aufzulösen. Ausnahme: "{2}" + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: Das Projekt "{0}" hat Graphisolationseinschränkungen für das referenzierte Projekt "{1}" übersprungen. @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: Die angegebene Anforderungsaffinität {0} steht mit einer früheren Affinität {1} in Konflikt, die für dieses Projekt angegeben wurde. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: Die angegebene Anforderungsaffinität {0} verursacht einen Konflikt mit einer vorherigen Affinität {1} speziell für das Projekt {2} mit globalen Eigenschaften {3} {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Auslastung: {0} Durchschnittliche Auslastung: {1:###.0} MSB4244: Die SDK-Resolverassembly "{0}" konnte nicht geladen werden. {1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: Der SDK-Resolver "{0}" konnte nicht ausgeführt werden. {1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: Fehler beim NuGet-basierten SDK-Resolver, weil die NuGet-Assemblys nicht gefunden wurden. Überprüfen Sie Ihre Installation von MSBuild, oder legen Sie die Umgebungsvariable "{0}" auf den Ordner fest, der die erforderlichen NuGet-Assemblys enthält. {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + Fehler beim NuGet-basierten SDK-Resolver, weil die NuGet-Assemblies nicht gefunden wurden. Überprüfen Sie Ihre Installation von MSBuild, oder legen Sie die Umgebungsvariable "{0}" auf den Ordner fest, der die erforderlichen NuGet-Assemblies enthält. {1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.en.xlf b/src/Build/Resources/xlf/Strings.en.xlf index 035b1fdf1cb..cff665d540f 100644 --- a/src/Build/Resources/xlf/Strings.en.xlf +++ b/src/Build/Resources/xlf/Strings.en.xlf @@ -257,6 +257,21 @@ Property initial value: $({0})="{1}" Source: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: SDK Resolver Failure: "{0}" + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" @@ -1865,8 +1880,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} {StrBegin="MSB4213: "} @@ -2394,15 +2409,10 @@ Utilization: {0} Average Utilization: {1:###.0} Done writing report. - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: The SDK resolver "{0}" failed to run. {1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + MSB4240: Multiple versions of the same SDK "{0}" cannot be specified. The previously resolved SDK version "{1}" from location "{2}" will be used and the version "{3}" will be ignored. diff --git a/src/Build/Resources/xlf/Strings.es.xlf b/src/Build/Resources/xlf/Strings.es.xlf index 3ada5cb3c4a..01daa41d62b 100644 --- a/src/Build/Resources/xlf/Strings.es.xlf +++ b/src/Build/Resources/xlf/Strings.es.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ Valor inicial de la propiedad: $({0})="{1}" Origen: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: Al deshabilitar el nodo InProc, se degrada el rendimiento cuando use los complementos de caché de proyectos que emiten solicitudes de compilación de proxy. + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: Error del solucionador del SDK: "{0}" + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + Error en el solucionador del SDK "{0}" al intentar resolver el SDK "{1}". Excepción: "{2}". + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: El proyecto "{0}" ha omitido las restricciones de aislamiento de gráficos en el proyecto "{1}" al que se hace referencia. @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: La afinidad de solicitud {0} especificada está en conflicto con una afinidad {1} anterior especificada para este proyecto. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: La afinidad de solicitud {0} especificada está en conflicto con una afinidad {1} anterior especificada para el proyecto {2} con las propiedades globales {3}. {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Utilización: Utilización media de {0}: {1:###.0} MSB4244: El ensamblado de la resolución del SDK "{0}" no se pudo cargar. {1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: La resolución del SDK "{0}" no se pudo ejecutar. {1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: La resolución del SDK basado en NuGet no se pudo ejecutar porque no se encontraron los ensamblados de NuGet. Compruebe su instalación de MSBuild o configure la variable del entorno "{0}" en la carpeta que contiene los ensamblados de NuGet requeridos. {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + No se pudo ejecutar la resolución del SDK basado en NuGet porque no se encontraron los ensamblados de NuGet. Compruebe su instalación de MSBuild o configure la variable del entorno "{0}" en la carpeta que contiene los ensamblados de NuGet requeridos. {1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.fr.xlf b/src/Build/Resources/xlf/Strings.fr.xlf index 90105ef4478..84593fc74d6 100644 --- a/src/Build/Resources/xlf/Strings.fr.xlf +++ b/src/Build/Resources/xlf/Strings.fr.xlf @@ -1,4 +1,4 @@ - + @@ -221,7 +221,7 @@ MSB4273: The project cache threw an unhandled exception from the {0} method. - MSB4273: The project cache threw an unhandled exception from the {0} method. + MSB4273: le cache de projet a levé une exception non gérée à partir de la méthode {0}. @@ -257,6 +257,21 @@ Valeur initiale de la propriété : $({0})="{1}" Source : {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: la désactivation du nœud inproc entraîne une détérioration des performances lors de l’utilisation de plug-ins de cache de projet qui émettent des requêtes de build proxy. + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: Échec du Programme de Résolution SDK : «{0}» + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + Échec du programme de résolution SDK «{0}» lors de la tentative de résolution du kit de développement logiciel (SDK) «{1}». Exception : "{2}" + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: le projet "{0}" a ignoré les contraintes d'isolement de graphe dans le projet référencé "{1}" @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: L'affinité de requête spécifiée {0} est en conflit avec une affinité précédente {1} spécifiée pour ce projet. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: L'affinité de requête spécifiée {0} est en conflit avec une affinité précédente {1} spécifiée pour ce projet {2} avec des propriétés globales {3} {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Utilisation : {0} Utilisation moyenne : {1:###.0} MSB4244: Impossible de charger l'assembly de résolution de SDK "{0}". {1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: Impossible d'exécuter la résolution de SDK "{0}". {1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: Impossible d'exécuter la résolution de SDK NuGet parce que les assemblys NuGet n'ont pas pu être localisés. Vérifiez votre installation de MSBuild ou définissez la variable d'environnement "{0}" dans le dossier qui contient les assemblys NuGet obligatoires. {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + Le résolveur SDK basé sur NuGet n'a pas pu s'exécuter car les assemblys NuGet n'ont pas pu être localisés. Vérifiez votre installation de MSBuild ou définissez la variable d'environnement "{0}" dans le dossier qui contient les assemblys NuGet obligatoires. {1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.it.xlf b/src/Build/Resources/xlf/Strings.it.xlf index 6711613ae06..a3c2a92531e 100644 --- a/src/Build/Resources/xlf/Strings.it.xlf +++ b/src/Build/Resources/xlf/Strings.it.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ Valore iniziale della proprietà: $({0})="{1}". Origine: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: la disabilitazione del nodo InProc porta a una riduzione del livello delle prestazioni quando si usano plug-in della cache del progetto che emettono richieste di compilazione proxy. + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: errore sistema di risoluzione SDK: "{0}" + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + Il sistema di risoluzione SDK "{0}" non è riuscito durante il tentativo di risolvere l'SDK "{1}". Eccezione: "{2}" + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: il progetto "{0}" ha ignorato i vincoli di isolamento del grafico nel progetto di riferimento "{1}" @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: l'affinità della richiesta specificata {0} è in conflitto con l'affinità {1} precedentemente specificata per il progetto. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: l'affinità della richiesta specificata {0} è in conflitto con l'affinità {1} precedentemente specificata per il progetto {2} con le proprietà globali {3} {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Utilizzo: {0} Utilizzo medio: {1:###.0} MSB4244: non è stato possibile caricare l'assembly "{0}" del resolver SDK. {1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: non è stato possibile eseguire il resolver SDK "{0}". {1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: non è stato possibile eseguire il resolver SDK basato su NuGet perché non sono stati trovati assembly NuGet. Controllare l'installazione di MSBuild oppure impostare la variabile di ambiente "{0}" sulla cartella che contiene gli assembly NuGet richiesti. {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + Non è stato possibile eseguire il resolver SDK basato su NuGet perché non sono stati trovati assembly NuGet. Controllare l'installazione di MSBuild oppure impostare la variabile di ambiente "{0}" sulla cartella che contiene gli assembly NuGet richiesti. {1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.ja.xlf b/src/Build/Resources/xlf/Strings.ja.xlf index aa758e3a206..66e3a864ded 100644 --- a/src/Build/Resources/xlf/Strings.ja.xlf +++ b/src/Build/Resources/xlf/Strings.ja.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ プロパティの初期値: $({0})="{1}" ソース: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: プロキシ・ビルド要求を出すプロジェクト キャッシュ プラグインを使用する場合、InProc ノードを無効にするとパフォーマンスが低下します。 + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: SDK リゾルバー エラー: "{0}" + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + SDK "{1}" を解決しようとしているときに、SDK リゾルバー "{0}" に失敗しました。例外: "{2}" + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: プロジェクト "{0}" は、参照先のプロジェクト "{1}" で、グラフの分離制約をスキップしました @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: 指定された要求の関係 {0} は、このプロジェクトに対して以前に指定された関係 {1} と競合しています。 + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: 指定された要求の関係 {0} は、グローバル プロパティ {3} を持つプロジェクト {2} に対して以前に指定された関係 {1} と競合しています。 {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Utilization: {0} Average Utilization: {1:###.0} MSB4244: SDK 競合回避モジュールのアセンブリ "{0}" を読み込めませんでした。{1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: SDK 競合回避モジュール "{0}" を実行できませんでした。{1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: NuGet アセンブリを見つけることができなかったため、NuGet ベースの SDK 競合回避モジュールを実行できませんでした。MSBuild のインストールを確認するか、環境変数 "{0}" を、必要な NuGet アセンブリが含まれているフォルダーに設定してください。{1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + NuGet アセンブリを見つけることができなかったため、NuGet ベースの SDK 競合回避モジュールを実行できませんでした。MSBuild のインストールを確認するか、環境変数 "{0}" を、必要な NuGet アセンブリが含まれているフォルダーに設定してください。{1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.ko.xlf b/src/Build/Resources/xlf/Strings.ko.xlf index af1e71f0f9a..a7d18597b51 100644 --- a/src/Build/Resources/xlf/Strings.ko.xlf +++ b/src/Build/Resources/xlf/Strings.ko.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ 속성 초기 값: $({0})="{1}" 소스: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: 프록시 빌드 요청을 내보내는 프로젝트 캐시 플러그 인을 사용할 때 inproc 노드를 사용하지 않도록 설정하면 성능이 저하됩니다. + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: SDK 해결 프로그램 오류: "{0}" + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + SDK "{1}"을(를) 확인하는 동안 SDK 확인자 "{0}"이(가) 실패했습니다. 예외: "{2}" + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: 프로젝트 "{0}"에서 참조된 프로젝트 "{1}"의 그래프 격리 제약 조건을 건너뛰었습니다. @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: 지정한 요청 선호도 {0}이(가) 이 프로젝트에 대해 이전에 지정한 선호도 {1}과(와) 충돌합니다. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: 지정된 요청 선호도 {0}이(가) 전역 속성이 {3}인 프로젝트 {2}에 대해 지정된 이전 선호도 {1}와(과) 충돌합니다. {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Utilization: {0} Average Utilization: {1:###.0} MSB4244: SDK 확인자 어셈블리 "{0}"을(를) 로드할 수 없습니다. {1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: SDK 확인자 "{0}"을(를) 실행하지 못했습니다. {1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: NuGet 어셈블리를 찾을 수 없어 NuGet 기반 SDK 확인자를 실행하지 못했습니다. MSBuild 설치를 확인하거나 환경 변수 "{0}"을(를) 필요한 NuGet 어셈블리가 포함된 폴더로 설정하세요. {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + NuGet 어셈블리를 찾을 수 없어 NuGet 기반 SDK 확인자를 실행하지 못했습니다. MSBuild 설치를 확인하거나 환경 변수 "{0}"을(를) 필요한 NuGet 어셈블리가 포함된 폴더로 설정하세요. {1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.pl.xlf b/src/Build/Resources/xlf/Strings.pl.xlf index 791c924d51e..7430b42c9b1 100644 --- a/src/Build/Resources/xlf/Strings.pl.xlf +++ b/src/Build/Resources/xlf/Strings.pl.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ Wartość początkowa właściwości: $({0})=„{1}” Źródło: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: wyłączenie węzła InProc prowadzi do obniżenia wydajności, gdy używane są wtyczki pamięci podręcznej projektu, które emitują żądania kompilowania serwera proxy. + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: niepowodzenia programu do rozpoznawania zestawu SDK: „{0}” + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + Wystąpił błąd programu do rozpoznawania zestawu SDK „{0}” podczas próby rozpoznania zestawu SDK „{1}”. Wyjątek: „{2}” + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: W przypadku projektu „{0}” pominięto ograniczenia izolacji grafu dla przywoływanego projektu „{1}” @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: Podana koligacja żądania {0} jest w konflikcie z poprzednią koligacją {1} określoną dla tego projektu. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: podana koligacja żądania {0} jest w konflikcie z poprzednią koligacją {1} określoną dla projektu {2} za pomocą właściwości globalnych {3} {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Wykorzystanie: Średnie wykorzystanie {0}: {1:###.0} MSB4244: Nie można załadować zestawu programu rozpoznawania zestawu SDK „{0}”. {1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: Nie można uruchomić programu rozpoznawania zestawu SDK „{0}”. {1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: Nie można uruchomić programu rozpoznawania zestawu SDK opartego na narzędziu NuGet, ponieważ nie można zlokalizować zestawów NuGet. Sprawdź instalację programu MSBuild lub ustaw zmienną środowiskową „{0}” na folder zawierający wymagane zestawy NuGet. {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + Nie można uruchomić programu do rozpoznawania zestawu SDK opartego na narzędziu NuGet, ponieważ nie można zlokalizować zestawów NuGet. Sprawdź instalację programu MSBuild lub ustaw zmienną środowiskową „{0}” na folder zawierający wymagane zestawy NuGet. {1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.pt-BR.xlf b/src/Build/Resources/xlf/Strings.pt-BR.xlf index a3c9bafe4a3..511d8aa41d5 100644 --- a/src/Build/Resources/xlf/Strings.pt-BR.xlf +++ b/src/Build/Resources/xlf/Strings.pt-BR.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ Valor inicial da propriedade: $({0})="{1}" Origem: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: desativar o nó inproc leva à degradação do desempenho ao usar plug-ins de cache de projeto que emitem solicitações de construção de proxy. + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: Falha no Resolvedor do SDK: "{0}" + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + O resolvedor do SDK "{0}" falhou ao tentar resolver o SDK "{1}". Exceção: "{2}" + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: o projeto "{0}" ignorou as restrições de isolamento do gráfico no projeto referenciado "{1}" @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: A afinidade de solicitação especificada {0} está em conflito com uma afinidade anterior {1} especificada para este projeto. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: A afinidade de solicitação especificada {0} está em conflito com uma afinidade anterior {1} especificada para o projeto {2} com propriedades globais {3} {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Utilização: {0} Utilização Média: {1:###.0} MSB4244: não foi possível carregar o assembly do resolvedor "{0}" do SDK. {1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: falha ao executar o resolvedor "{0}" do SDK. {1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: falha ao executar o resolvedor do SDK baseado em NuGet porque não foi possível localizar os assemblies do NuGet. Verifique a instalação do MSBuild ou defina a variável de ambiente "{0}" para a pasta que contém os assemblies necessários do NuGet. {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + O resolvedor do SDK baseado em NuGet falhou ao executar porque os assemblies do NuGet não puderam ser localizados. Verifique sua instalação do MSBuild ou defina a variável de ambiente "{0}" para a pasta que contém os assemblies do NuGet necessários. {1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.ru.xlf b/src/Build/Resources/xlf/Strings.ru.xlf index ea7ac3894a8..f19945c2240 100644 --- a/src/Build/Resources/xlf/Strings.ru.xlf +++ b/src/Build/Resources/xlf/Strings.ru.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ Начальное значение свойства: $({0})="{1}" Источник: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: Отключение внутрипроцессного узла приводит к замедлению при использовании плагинов кэша проекта, которые создают запросы на сборку прокси-сервера. + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: сбой сопоставителя SDK: "{0}" + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + Сбой сопоставителя SDK "{0}" при попытке сопоставить пакет SDK "{1}". Исключение: "{2}" + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: проект "{0}" пропустил ограничения изоляции графа в проекте "{1}", на который указывает ссылка. @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: указанное сходство запроса {0} конфликтует с предыдущим сходством {1}, заданным для данного проекта. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: указанное сходство запроса {0} конфликтует с предыдущим сходством {1}, заданным для проекта {2} с глобальными свойствами {3} {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Utilization: {0} Average Utilization: {1:###.0} MSB4244: не удалось загрузить сборку сопоставителя SDK типа "{0}". {1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: не удалось запустить сопоставитель SDK "{0}". {1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: не удалось запустить сопоставитель SDK на базе NuGet, так как не удалось найти сборки NuGet. Проверьте свою установку MSBuild или укажите папку, содержащую требуемые сборки NuGet, в качестве значения переменной среды "{0}". {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + Не удалось запустить сопоставитель SDK на базе NuGet, так как не удалось найти сборки NuGet. Проверьте свою установку MSBuild или укажите папку, содержащую требуемые сборки NuGet, в качестве значения переменной среды "{0}". {1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.tr.xlf b/src/Build/Resources/xlf/Strings.tr.xlf index 095ce39a05d..6ba8540f344 100644 --- a/src/Build/Resources/xlf/Strings.tr.xlf +++ b/src/Build/Resources/xlf/Strings.tr.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ Özellik başlangıç değeri: $({0})="{1}" Kaynak: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: InProc düğümünün devre dışı bırakılması, ara sunucu oluşturma istekleri gönderen proje önbelleği eklentileri kullanılırken performans düşüşüne yol açar. + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: SDK Çözümleyici Hatası: "{0}" + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + "{0}" SDK çözümleyicisi, "{1}" SDK'sını çözümlemeye çalışırken başarısız oldu. İstisna: "{2}" + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: "{0}" projesi, başvurulan "{1}" projesindeki graf yalıtımı kısıtlamalarını atladı @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: Belirtilen istek benzeşimi {0} bu proje için daha önce belirtilen {1} benzeşimi ile çakışıyor. + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: Belirtilen istek benzeşimi {0}, {3} genel özelliklerine sahip {2} projesi için belirtilen önceki bir {1} benzeşimiyle çakışıyor {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Kullanım: {0} Ortalama Kullanım: {1:###.0} MSB4244: "{0}" SDK çözümleyicisi bütünleştirilmiş kodu yüklenemedi. {1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: "{0}" SDK çözümleyicisi çalıştırılamadı. {1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: NuGet bütünleştirilmiş kodları bulunamadığından NuGet tabanlı SDK çözümleyicisi çalıştırılamadı. MSBuild yüklemenizi denetleyin veya "{0}" ortam değişkenini gerekli NuGet bütünleştirilmiş kodlarını içeren klasör olarak ayarlayın. {1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + NuGet derlemeleri bulunamadığından NuGet tabanlı SDK çözümleyicisi çalıştırılamadı. MSBuild yüklemenizi kontrol edin veya "{0}" ortam değişkenini gerekli NuGet derlemelerini içeren klasöre ayarlayın. {1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.zh-Hans.xlf b/src/Build/Resources/xlf/Strings.zh-Hans.xlf index b976c8e4d9d..bed465c1a19 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hans.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ 属性初始值: $({0})=“{1}”,源: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: 使用发出代理构建请求的项目缓存插件时,禁用 inproc 节点会导致性能下降。 + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: SDK 解析程序失败: "{0}" + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + 尝试解析 SDK "{1}" 时,SDK 解析程序 "{0}" 失败。异常: "{2}" + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: 项目“{0}”已跳过所引用的项目“{1}”上的图形隔离约束 @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: 指定的请求关联 {0} 与先前为此项目指定的关联 {1} 冲突。 + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: 指定的请求关联 {0} 与先前为具有全局属性 {3} 的项目 {2} 指定的关联 {1} 冲突。 {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Utilization: {0} Average Utilization: {1:###.0} MSB4244: 无法加载 SDK 解析程序程序集“{0}”。{1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: SDK 解析程序“{0}”运行失败。{1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: 基于 NuGet 的 SDK 解析程序运行失败,因为无法找到 NuGet 程序集。请检查你安装的 MSBuild 或将环境变量“{0}”设置为包含所需 NuGet 程序集的文件夹。{1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + 基于 NuGet 的 SDK 解析程序运行失败,因为无法找到 NuGet 程序集。请检查你安装的 MSBuild 或将环境变量“{0}”设置为包含所需 NuGet 程序集的文件夹。{1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/Resources/xlf/Strings.zh-Hant.xlf b/src/Build/Resources/xlf/Strings.zh-Hant.xlf index ddebd381fbe..fa8a07b3ae3 100644 --- a/src/Build/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Build/Resources/xlf/Strings.zh-Hant.xlf @@ -1,4 +1,4 @@ - + @@ -257,6 +257,21 @@ 屬性初始值: $({0})="{1}" 來源: {2} + + MSB4274: Disabling the inproc node leads to performance degradation when using project cache plugins that emit proxy build requests. + MSB4274: 停用 inproc 節點會在使用可發出 proxy 組建要求的專案快取外掛程式時,導致效能降低。 + + + + MSB4242: SDK Resolver Failure: "{0}" + MSB4242: SDK 解析程式失敗: "{0}" + {StrBegin="MSB4242: "} + + + The SDK resolver "{0}" failed while attempting to resolve the SDK "{1}". Exception: "{2}" + SDK 解析程式 "{0}" 在嘗試解析 SDK "{1}" 時失敗。例外狀況: "{2}" + + MSB4260: Project "{0}" skipped graph isolation constraints on referenced project "{1}" MSB4260: 專案 "{0}" 已跳過參考專案 "{1}" 上的圖形隔離條件約束 @@ -1785,8 +1800,8 @@ {StrBegin="MSB4209: "} - MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for this project. - MSB4213: 指定的要求親和性 {0} 與先前為這個專案指定的親和性 {1} 衝突。 + MSB4213: The specified request affinity {0} conflicts with a previous affinity {1} specified for project {2} with global properties {3} + MSB4213: 指定的要求親和性 {0} 與先前為專案 {2} 指定、具有全域屬性 {3} 的親和性 {1} 衝突。 {StrBegin="MSB4213: "} @@ -2374,15 +2389,10 @@ Utilization: {0} Average Utilization: {1:###.0} MSB4244: 無法載入 SDK 解析程式組件 "{0}"。{1} {StrBegin="MSB4244: "} - - MSB4242: The SDK resolver "{0}" failed to run. {1} - MSB4242: SDK 解析程式 "{0}" 無法執行。{1} - {StrBegin="MSB4242: "} - - MSB4243: The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} - MSB4243: 因為找不到 NuGet 組件,所以 NuGet 型 SDK 解析程式無法執行。請檢查您的 MSBuild 安裝,或將環境變數 "{0}" 設定為包含必要 NuGet 組件的資料夾。{1} - {StrBegin="MSB4243: "} + The NuGet-based SDK resolver failed to run because NuGet assemblies could not be located. Check your installation of MSBuild or set the environment variable "{0}" to the folder that contains the required NuGet assemblies. {1} + 因為找不到 NuGet 組件,NuGet 型 SDK 解析程式無法執行。請檢查您的 MSBuild 安裝,或將環境變數 "{0}" 設定為包含必要 NuGet 組件的資料夾。{1} + Project "{0}" was not imported by "{1}" at ({2},{3}), due to the file being invalid. diff --git a/src/Build/System.Text.Encodings.Web.pkgdef b/src/Build/System.Text.Encodings.Web.pkgdef deleted file mode 100644 index bee4d6921fe..00000000000 --- a/src/Build/System.Text.Encodings.Web.pkgdef +++ /dev/null @@ -1,7 +0,0 @@ -[$RootKey$\RuntimeConfiguration\dependentAssembly\bindingRedirection\{1A1A9DA4-9F25-4AC8-89BF-BCEF74875CA8}] -"name"="System.Text.Encodings.Web" -"codeBase"="$BaseInstallDir$\MSBuild\Current\Bin\System.Text.Encodings.Web.dll" -"publicKeyToken"="cc7b13ffcd2ddd51" -"culture"="neutral" -"oldVersion"="0.0.0.0-4.0.5.0" -"newVersion"="4.0.5.0" diff --git a/src/Build/System.Text.Json.pkgdef b/src/Build/System.Text.Json.pkgdef deleted file mode 100644 index f20fee293fd..00000000000 --- a/src/Build/System.Text.Json.pkgdef +++ /dev/null @@ -1,7 +0,0 @@ -[$RootKey$\RuntimeConfiguration\dependentAssembly\bindingRedirection\{1F1A9DA4-9F25-4AB8-89BF-BCEF73875178}] -"name"="System.Text.Json" -"codeBase"="$BaseInstallDir$\MSBuild\Current\Bin\System.Text.Json.dll" -"publicKeyToken"="cc7b13ffcd2ddd51" -"culture"="neutral" -"oldVersion"="0.0.0.0-4.0.1.0" -"newVersion"="4.0.1.0" diff --git a/src/Build/Utilities/EngineFileUtilities.cs b/src/Build/Utilities/EngineFileUtilities.cs index 02b46c31efa..da8165d3369 100644 --- a/src/Build/Utilities/EngineFileUtilities.cs +++ b/src/Build/Utilities/EngineFileUtilities.cs @@ -168,7 +168,7 @@ private string[] GetFileList if (returnEscaped) { - // We must now go back and make sure all special characters are escaped because we always + // We must now go back and make sure all special characters are escaped because we always // store data in the engine in escaped form so it doesn't interfere with our parsing. // Note that this means that characters that were not escaped in the original filespec // may now be escaped, but that's not easy to avoid. diff --git a/src/Build/Utilities/FileSpecMatchTester.cs b/src/Build/Utilities/FileSpecMatchTester.cs index 41aaea15e97..281b0278888 100644 --- a/src/Build/Utilities/FileSpecMatchTester.cs +++ b/src/Build/Utilities/FileSpecMatchTester.cs @@ -15,7 +15,7 @@ internal readonly struct FileSpecMatcherTester private readonly string _unescapedFileSpec; private readonly string _filenamePattern; private readonly Regex _regex; - + private FileSpecMatcherTester(string currentDirectory, string unescapedFileSpec, string filenamePattern, Regex regex) { Debug.Assert(!string.IsNullOrEmpty(unescapedFileSpec)); @@ -25,6 +25,13 @@ private FileSpecMatcherTester(string currentDirectory, string unescapedFileSpec, _unescapedFileSpec = unescapedFileSpec; _filenamePattern = filenamePattern; _regex = regex; + + if (_regex == null && _filenamePattern == null) + { + // We'll be testing files by comparing their normalized paths. Normalize our file spec right away + // to avoid doing this work on each IsMatch call. + _unescapedFileSpec = FileUtilities.NormalizePathForComparisonNoThrow(_unescapedFileSpec, _currentDirectory); + } } public static FileSpecMatcherTester Parse(string currentDirectory, string fileSpec) @@ -41,31 +48,52 @@ public static FileSpecMatcherTester Parse(string currentDirectory, string fileSp return new FileSpecMatcherTester(currentDirectory, unescapedFileSpec, filenamePattern, regex); } + /// + /// Returns true if the given file matches this file spec. + /// public bool IsMatch(string fileToMatch) { Debug.Assert(!string.IsNullOrEmpty(fileToMatch)); + // Historically we've used slightly different normalization logic depending on the type of matching + // performed in IsMatchNormalized. We have to keep doing it for compat. + if (_regex == null && _filenamePattern == null) + { + fileToMatch = FileUtilities.NormalizePathForComparisonNoThrow(fileToMatch, _currentDirectory); + } + else + { + fileToMatch = FileUtilities.GetFullPathNoThrow(Path.Combine(_currentDirectory, fileToMatch)); + } + return IsMatchNormalized(fileToMatch); + } + + /// + /// Same as but the argument is expected to be a normalized path. + /// + public bool IsMatchNormalized(string normalizedFileToMatch) + { + Debug.Assert(!string.IsNullOrEmpty(normalizedFileToMatch)); + // We do the matching using one of three code paths, depending on the value of _filenamePattern and _regex. if (_regex != null) { - string normalizedFileToMatch = FileUtilities.GetFullPathNoThrow(Path.Combine(_currentDirectory, fileToMatch)); return _regex.IsMatch(normalizedFileToMatch); } if (_filenamePattern != null) { // Check file name first as it's more likely to not match. - string filename = Path.GetFileName(fileToMatch); + string filename = Path.GetFileName(normalizedFileToMatch); if (!FileMatcher.IsMatch(filename, _filenamePattern)) { return false; } - var normalizedFileToMatch = FileUtilities.GetFullPathNoThrow(Path.Combine(_currentDirectory, fileToMatch)); return normalizedFileToMatch.StartsWith(_currentDirectory, StringComparison.OrdinalIgnoreCase); } - return FileUtilities.ComparePathsNoThrow(_unescapedFileSpec, fileToMatch, _currentDirectory, alwaysIgnoreCase: true); + return string.Equals(_unescapedFileSpec, normalizedFileToMatch, StringComparison.OrdinalIgnoreCase); } // this method parses the glob and extracts the fixed directory part in order to normalize it and make it absolute diff --git a/src/Build/Xml/XmlReaderExtension.cs b/src/Build/Xml/XmlReaderExtension.cs index 424e7dea8a9..4bf4944e94c 100644 --- a/src/Build/Xml/XmlReaderExtension.cs +++ b/src/Build/Xml/XmlReaderExtension.cs @@ -1,11 +1,8 @@ using System; -using System.Diagnostics; using System.IO; -using System.Reflection; using System.Text; using System.Xml; using Microsoft.Build.Shared; -using Microsoft.Build.Utilities; namespace Microsoft.Build.Internal { @@ -29,16 +26,6 @@ internal static XmlReaderExtension Create(string filePath, bool loadAsReadOnly) private readonly Stream _stream; private readonly StreamReader _streamReader; - /// - /// Caches a representing the "Normalization" internal property on the -derived - /// type returned from . The cache is process/AppDomain-wide - /// and lock-free, so we use volatile access for thread safety, i.e. to ensure that when the field is updated the PropertyInfo - /// it's pointing to is seen as fully initialized by all CPUs. - /// - private static volatile PropertyInfo _normalizationPropertyInfo; - - private static bool _disableReadOnlyLoad; - private XmlReaderExtension(string file, bool loadAsReadOnly) { try @@ -84,61 +71,15 @@ public void Dispose() _stream?.Dispose(); } - /// - /// Returns of the "Normalization" internal property on the given -derived type. - /// - private static PropertyInfo GetNormalizationPropertyInfo(Type xmlReaderType) - { - PropertyInfo propertyInfo = _normalizationPropertyInfo; - if (propertyInfo == null) - { - BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance; - propertyInfo = xmlReaderType.GetProperty("Normalization", bindingFlags); - _normalizationPropertyInfo = propertyInfo; - } - - return propertyInfo; - } - private static XmlReader GetXmlReader(string file, StreamReader input, bool loadAsReadOnly, out Encoding encoding) { string uri = new UriBuilder(Uri.UriSchemeFile, string.Empty) { Path = file }.ToString(); - XmlReader reader = null; - if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_10) && loadAsReadOnly && !_disableReadOnlyLoad) - { - // Create an XML reader with IgnoreComments and IgnoreWhitespace set if we know that we won't be asked - // to write the DOM back to a file. This is a performance optimization. - XmlReaderSettings settings = new XmlReaderSettings - { - DtdProcessing = DtdProcessing.Ignore, - IgnoreComments = true, - IgnoreWhitespace = true, - }; - reader = XmlReader.Create(input, settings, uri); - - // Try to set Normalization to false. We do this to remain compatible with earlier versions of MSBuild - // where we constructed the reader with 'new XmlTextReader()' which has normalization enabled by default. - PropertyInfo normalizationPropertyInfo = GetNormalizationPropertyInfo(reader.GetType()); - if (normalizationPropertyInfo != null) - { - normalizationPropertyInfo.SetValue(reader, false); - } - else - { - // Fall back to using XmlTextReader if the prop could not be bound. - Debug.Fail("Could not set Normalization to false on the result of XmlReader.Create"); - _disableReadOnlyLoad = true; - - reader.Dispose(); - reader = null; - } - } - - if (reader == null) - { - reader = new XmlTextReader(uri, input) { DtdProcessing = DtdProcessing.Ignore }; - } + + // Ignore loadAsReadOnly for now; using XmlReader.Create results in whitespace changes + // of attribute text, specifically newline removal. + // https://github.com/Microsoft/msbuild/issues/4210 + XmlReader reader = new XmlTextReader(uri, input) { DtdProcessing = DtdProcessing.Ignore }; reader.Read(); encoding = input.CurrentEncoding; diff --git a/src/Deprecated/Conversion/AssemblyInfo.cs b/src/Deprecated/Conversion/AssemblyInfo.cs index e9b874dd1c5..fd4bc5df27b 100644 --- a/src/Deprecated/Conversion/AssemblyInfo.cs +++ b/src/Deprecated/Conversion/AssemblyInfo.cs @@ -23,11 +23,4 @@ [assembly: CLSCompliant(true)] -// Needed for the "hub-and-spoke model to locate and retrieve localized resources": https://msdn.microsoft.com/en-us/library/21a15yht(v=vs.110).aspx -// We want "en" to require a satellite assembly for debug builds in order to flush out localization -// issues, but we want release builds to work without it. Also, .net core does not have resource fallbacks -#if (DEBUG && !RUNTIME_TYPE_NETCORE) -[assembly: NeutralResourcesLanguage("en", UltimateResourceFallbackLocation.Satellite)] -#else [assembly: NeutralResourcesLanguage("en")] -#endif diff --git a/src/Deprecated/Conversion/Resources/xlf/Strings.en.xlf b/src/Deprecated/Conversion/Resources/xlf/Strings.en.xlf deleted file mode 100644 index 73f4ab526f7..00000000000 --- a/src/Deprecated/Conversion/Resources/xlf/Strings.en.xlf +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - MSB2013: The project-to-project reference with GUID {0} could not be converted because a valid .SLN file containing all projects could not be found. - MSB2013: The project-to-project reference with GUID {0} could not be converted because a valid .SLN file containing all projects could not be found. - {StrBegin="MSB2013: "} - - - MSB2016: Found an empty .RESX file in the project ({0}). Removing it from the converted project. - MSB2016: Found an empty .RESX file in the project ({0}). Removing it from the converted project. - {StrBegin="MSB2016: "} - - - MSB2015: Found an <Exclude> element in the original project file. This cannot be converted to Visual Studio .NET and is being ignored. - MSB2015: Found an <Exclude> element in the original project file. This cannot be converted to Visual Studio .NET and is being ignored. - {StrBegin="MSB2015: "} - - - MSB2001: Element <{0}> does not contain the required attribute "{1}". - MSB2001: Element <{0}> does not contain the required attribute "{1}". - {StrBegin="MSB2001: "}It appears we've been asked to convert a project that either wasn't created by Visual Studio, or that has been hand-modified or corrupted. - - - MSB2002: The file name of the new project must be specified. - MSB2002: The file name of the new project must be specified. - {StrBegin="MSB2002: "}This error shouldn't be possible when converting through Visual Studio, but we have it just in case. - - - MSB2003: The file name of the old project must be specified. - MSB2003: The file name of the old project must be specified. - {StrBegin="MSB2003: "}This error shouldn't be possible when converting through Visual Studio, but we have it just in case. - - - MSB2004: Element <{0}> cannot contain more than one language node. - MSB2004: Element <{0}> cannot contain more than one language node. - {StrBegin="MSB2004: "}It appears we've been asked to convert a project that either wasn't created by Visual Studio, or that has been hand-modified or corrupted. The {0} in this case is going to be "VisualStudioProject". - - - MSB2005: Element <{0}> cannot contain attributes. - MSB2005: Element <{0}> cannot contain attributes. - {StrBegin="MSB2005: "}It appears we've been asked to convert a project that either wasn't created by Visual Studio, or that has been hand-modified or corrupted. - - - MSB2006: The project file does not contain the root element <{0}>. - MSB2006: The project file does not contain the root element <{0}>. - {StrBegin="MSB2006: "}It appears we've been asked to convert a project that either wasn't created by Visual Studio, or that has been hand-modified or corrupted. - - - MSB2007: Visual Studio cannot find the project file "{0}". - MSB2007: Visual Studio cannot find the project file "{0}". - {StrBegin="MSB2007: "}This error shouldn't be possible when converting through Visual Studio, but we have it just in case. - - - MSB2014: The project-to-project reference with GUID {0} cannot be converted because it is not listed in the file '{1}'. - MSB2014: The project-to-project reference with GUID {0} cannot be converted because it is not listed in the file '{1}'. - {StrBegin="MSB2014: "} - - - MSB2008: This Visual Studio project system cannot convert "{0}" projects. It can only be used to convert C#, VB, and VJ# client projects. - MSB2008: This Visual Studio project system cannot convert "{0}" projects. It can only be used to convert C#, VB, and VJ# client projects. - {StrBegin="MSB2008: "}It appears we've been asked to convert a project that either wasn't created by the Visual Studio managed client project system (C#, VB, J#), or that has been hand-modified or corrupted. You would get this error if this conversion utility was invoked on a VC++ project (.VCPROJ) for example. In this case, the {0} would be replaced with "Visual C++". - - - MSB2009: Attribute "{0}" of element <{1}> is not valid. - MSB2009: Attribute "{0}" of element <{1}> is not valid. - {StrBegin="MSB2009: "}It appears we've been asked to convert a project that either wasn't created by Visual Studio, or that has been hand-modified or corrupted. - - - MSB2010: Child element <{0}> of element <{1}> is not valid. - MSB2010: Child element <{0}> of element <{1}> is not valid. - {StrBegin="MSB2010: "}It appears we've been asked to convert a project that either wasn't created by Visual Studio, or that has been hand-modified or corrupted. - - - MSB2011: Element <{0}> is not valid. - MSB2011: Element <{0}> is not valid. - {StrBegin="MSB2011: "}It appears we've been asked to convert a project that either wasn't created by Visual Studio, or that has been hand-modified or corrupted. - - - MSB2012: Project-to-project references to web projects are no longer supported and therefore cannot be converted. Please remove the reference to project {0} and add it again. - MSB2012: Project-to-project references to web projects are no longer supported and therefore cannot be converted. Please remove the reference to project {0} and add it again. - {StrBegin="MSB2012: "} - - - - \ No newline at end of file diff --git a/src/Deprecated/Conversion/Resources/xlf/Strings.zh-Hans.xlf b/src/Deprecated/Conversion/Resources/xlf/Strings.zh-Hans.xlf index 4134008b926..e49ab19ee4d 100644 --- a/src/Deprecated/Conversion/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Deprecated/Conversion/Resources/xlf/Strings.zh-Hans.xlf @@ -1,4 +1,4 @@ - +
diff --git a/src/Deprecated/Conversion/Resources/xlf/Strings.zh-Hant.xlf b/src/Deprecated/Conversion/Resources/xlf/Strings.zh-Hant.xlf index c5ae79dc07c..e99a3a187a1 100644 --- a/src/Deprecated/Conversion/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Deprecated/Conversion/Resources/xlf/Strings.zh-Hant.xlf @@ -1,4 +1,4 @@ - +
diff --git a/src/Deprecated/Engine.UnitTests/Project_Tests.cs b/src/Deprecated/Engine.UnitTests/Project_Tests.cs index 38f2844b922..1144ce87b6f 100644 --- a/src/Deprecated/Engine.UnitTests/Project_Tests.cs +++ b/src/Deprecated/Engine.UnitTests/Project_Tests.cs @@ -26,8 +26,8 @@ public class AddItem { /// /// This loads an existing project, and uses the MSBuild object model to - /// add a new item (Type="Compile" Include="c.cs") to the project. Then - /// it compares the final project XML to make sure the item was added in + /// add a new item (Type="Compile" Include="c.cs") to the project. Then + /// it compares the final project XML to make sure the item was added in /// the correct place. /// /// @@ -49,7 +49,7 @@ string newItemInclude // The project shouldn't be marked dirty yet. Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate); - // Add a new item (Type="Compile", Include="c.cs") to the project using + // Add a new item (Type="Compile", Include="c.cs") to the project using // the object model. BuildItem newItem = project.AddNewItem(newItemType, newItemInclude); @@ -143,7 +143,7 @@ public void AddNewItemToNewItemGroup() - + "; @@ -151,7 +151,7 @@ public void AddNewItemToNewItemGroup() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" @@ -170,7 +170,7 @@ public void AddNewItemToNewItemGroup() - + "; @@ -179,7 +179,7 @@ public void AddNewItemToNewItemGroup() } /// - /// This loads an existing project that did not contain any items previously. + /// This loads an existing project that did not contain any items previously. /// It then uses the MSBuild object model to /// add a new item to the project. Then it compares the final project /// XML to make sure the item was added in the correct place. @@ -267,9 +267,9 @@ public void AddNewItemAndQueryForNonExistentMetadata() } /// - /// Add a new item of the same name and include path of an item that already + /// Add a new item of the same name and include path of an item that already /// exists in the project. Current behavior is that we add the duplicated item, - /// although there's no great reason for this. If we wanted, we could have + /// although there's no great reason for this. If we wanted, we could have /// made it so that adding a dup results in a no-op to the project file. /// /// RGoel @@ -701,7 +701,7 @@ public void AddNewItemThatMatchesWildcardWithMetadata() /// /// There's a wildcard in the project already, but it's part of a semicolon-separated - /// list of items. Now the user tries to add an item that matches that wildcard. + /// list of items. Now the user tries to add an item that matches that wildcard. /// In this case, we don't touch the project at all. /// /// RGoel @@ -741,7 +741,7 @@ public void AddNewItemThatMatchesWildcardInSemicolonList() /// /// There's a wildcard in the project already, but it's part of a semicolon-separated - /// list of items, and it uses a property reference. Now the user tries to add a new + /// list of items, and it uses a property reference. Now the user tries to add a new /// item that matches that wildcard. In this case, we don't touch the project at all. /// We're so smart. /// @@ -803,7 +803,7 @@ public void AddNewItemGroup() // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" @@ -818,7 +818,7 @@ public void AddNewItemGroup() - + "; @@ -826,7 +826,7 @@ public void AddNewItemGroup() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" @@ -843,7 +843,7 @@ public void AddNewItemGroup() - + "; @@ -865,17 +865,17 @@ public class RemoveItem /// /// This loads an existing project, and uses the MSBuild object model to /// remove an item of a particular item spec (e.g., "b.cs"). It then - /// compares the final project XML to make sure the item was added in + /// compares the final project XML to make sure the item was added in /// the correct place. /// /// /// /// /// RGoel - private void RemoveItemHelper + private void RemoveItemHelper ( - string originalProjectContents, - string newExpectedProjectContents, + string originalProjectContents, + string newExpectedProjectContents, string itemSpecToRemove ) { @@ -889,7 +889,7 @@ string itemSpecToRemove // The VS IDE does a few re-evaluations with different sets of global properties // (i.e., Configuration=Debug, Configuration=Release, etc.). This is to simulate - // that. If there's a bug in the Project object, then re-evaluation can + // that. If there's a bug in the Project object, then re-evaluation can // potentially mess up the number of items hanging around. project.MarkProjectAsDirty (); BuildItemGroup evaluatedItems2 = project.EvaluatedItemsIgnoringCondition; @@ -936,9 +936,9 @@ public void RemoveItemBySpec() - + - + "; @@ -957,12 +957,12 @@ public void RemoveItemBySpec() - + - + "; - + this.RemoveItemHelper (projectOriginalContents, projectNewExpectedContents, "b.cs"); } @@ -978,7 +978,7 @@ public void RemoveItemBySpecFromMultiItemSpec() // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" @@ -993,9 +993,9 @@ public void RemoveItemBySpecFromMultiItemSpec() - + - + "; @@ -1003,7 +1003,7 @@ public void RemoveItemBySpecFromMultiItemSpec() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" @@ -1019,12 +1019,12 @@ public void RemoveItemBySpecFromMultiItemSpec() - + - + "; - + this.RemoveItemHelper (projectOriginalContents, projectNewExpectedContents, "b.cs"); } @@ -1042,7 +1042,7 @@ public void RemoveItemBySpecFromMultiItemSpecWithMetadata() // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" @@ -1052,7 +1052,7 @@ public void RemoveItemBySpecFromMultiItemSpecWithMetadata() - + "; @@ -1060,7 +1060,7 @@ public void RemoveItemBySpecFromMultiItemSpecWithMetadata() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" @@ -1073,10 +1073,10 @@ public void RemoveItemBySpecFromMultiItemSpecWithMetadata() - + "; - + this.RemoveItemHelper (projectOriginalContents, projectNewExpectedContents, "b.cs"); } @@ -1101,7 +1101,7 @@ public void RemoveItemBySpecWhenMultiItemSpecExists() - + "; @@ -1119,10 +1119,10 @@ public void RemoveItemBySpecWhenMultiItemSpecExists() - + "; - + this.RemoveItemHelper (projectOriginalContents, projectNewExpectedContents, "d.cs"); } @@ -1153,7 +1153,7 @@ public void RemoveSpecificItem() - + "; @@ -1174,7 +1174,7 @@ public void RemoveSpecificItem() - + "; @@ -1226,7 +1226,7 @@ public void RemoveItemsByName() - + "; @@ -1246,7 +1246,7 @@ public void RemoveItemsByName() - + "; @@ -1287,7 +1287,7 @@ public void RemoveItemGroup() - + "; @@ -1303,7 +1303,7 @@ public void RemoveItemGroup() - + "; @@ -1667,8 +1667,8 @@ public class ModifyItem { /// /// This loads an existing project, and uses the MSBuild object model to - /// modify the "Include" attribute of an item of a particular item spec (e.g., - /// "b.cs"). It then compares the final project XML to make sure the item was + /// modify the "Include" attribute of an item of a particular item spec (e.g., + /// "b.cs"). It then compares the final project XML to make sure the item was /// modified correctly. /// /// @@ -1676,10 +1676,10 @@ public class ModifyItem /// /// /// RGoel - internal static void ModifyItemIncludeHelper + internal static void ModifyItemIncludeHelper ( - string originalProjectContents, - string newExpectedProjectContents, + string originalProjectContents, + string newExpectedProjectContents, string oldItemSpec, string newIncludePath ) @@ -1694,7 +1694,7 @@ string newIncludePath // The VS IDE does a few re-evaluations with different sets of global properties // (i.e., Configuration=Debug, Configuration=Release, etc.). This is to simulate - // that. If there's a bug in the Project object, then re-evaluation can + // that. If there's a bug in the Project object, then re-evaluation can // potentially mess up the number of items hanging around. project.MarkProjectAsDirty (); BuildItemGroup evaluatedItems2 = project.EvaluatedItemsIgnoringCondition; @@ -1729,7 +1729,7 @@ public void ModifyItemIncludeWithEmbeddedProperty() // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" @@ -1749,7 +1749,7 @@ public void ModifyItemIncludeWithEmbeddedProperty() - + "; @@ -1757,7 +1757,7 @@ public void ModifyItemIncludeWithEmbeddedProperty() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" @@ -1777,7 +1777,7 @@ public void ModifyItemIncludeWithEmbeddedProperty() - + "; @@ -1796,7 +1796,7 @@ public void ModifyItemIncludeWithinMultiItemSpec() // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" @@ -1809,7 +1809,7 @@ public void ModifyItemIncludeWithinMultiItemSpec() - + "; @@ -1817,7 +1817,7 @@ public void ModifyItemIncludeWithinMultiItemSpec() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" @@ -1836,7 +1836,7 @@ public void ModifyItemIncludeWithinMultiItemSpec() - + "; @@ -1888,17 +1888,17 @@ public void ModifyItemIncludeWithinNonMatchingWildcard() { // Populate the project directory with three physical files on disk -- a.weirdo, b.weirdo, c.weirdo. CreateThreeWeirdoFilesHelper(); - + // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" - + "; @@ -1906,7 +1906,7 @@ public void ModifyItemIncludeWithinNonMatchingWildcard() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" @@ -1914,7 +1914,7 @@ public void ModifyItemIncludeWithinNonMatchingWildcard() - + "; @@ -1940,13 +1940,13 @@ public void ModifyItemIncludeWithinMatchingWildcard() // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" - + "; @@ -1954,13 +1954,13 @@ public void ModifyItemIncludeWithinMatchingWildcard() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" - + "; @@ -1988,13 +1988,13 @@ public void ModifyRawItemIncludeWithinMatchingWildcard() // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" - + "; @@ -2002,13 +2002,13 @@ public void ModifyRawItemIncludeWithinMatchingWildcard() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" - + "; @@ -2073,8 +2073,8 @@ string itemSpec /// /// This loads an existing project, and uses the MSBuild object model to - /// modify the Name of an item of a particular item spec (e.g., - /// "b.cs"). It then compares the final project XML to make sure the item was + /// modify the Name of an item of a particular item spec (e.g., + /// "b.cs"). It then compares the final project XML to make sure the item was /// modified correctly. /// /// @@ -2100,7 +2100,7 @@ string newItemType // The VS IDE does a few re-evaluations with different sets of global properties // (i.e., Configuration=Debug, Configuration=Release, etc.). This is to simulate - // that. If there's a bug in the Project object, then re-evaluation can + // that. If there's a bug in the Project object, then re-evaluation can // potentially mess up the number of items hanging around. project.MarkProjectAsDirty(); BuildItemGroup evaluatedItems2 = project.EvaluatedItemsIgnoringCondition; @@ -2270,7 +2270,7 @@ public void ModifyItemMetadata() // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" @@ -2285,7 +2285,7 @@ public void ModifyItemMetadata() - + "; @@ -2293,7 +2293,7 @@ public void ModifyItemMetadata() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" @@ -2311,7 +2311,7 @@ public void ModifyItemMetadata() - + "; @@ -2338,7 +2338,7 @@ public void ModifyItemMetadata() public class AddProperty { /// - /// Tests that the object model correctly adds a new property to the correct + /// Tests that the object model correctly adds a new property to the correct /// existing PropertyGroup. /// /// RGoel @@ -2348,23 +2348,23 @@ public void SetPropertyOnNewPropertyInExistingPropertyGroup() // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" c:\blah - + 1 - + true - + - + "; @@ -2372,24 +2372,24 @@ public void SetPropertyOnNewPropertyInExistingPropertyGroup() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" c:\blah - + 1 woohoo - + true - + - + "; @@ -2398,7 +2398,7 @@ public void SetPropertyOnNewPropertyInExistingPropertyGroup() // The project shouldn't be marked dirty yet. Assertion.Assert("Project shouldn't be dirty", !project.IsDirtyNeedToReevaluate); - // Set the given new property in the project file using + // Set the given new property in the project file using // the object model. project.SetProperty("MyNewProperty", "woohoo", ""); @@ -2419,7 +2419,7 @@ public void AddNewPropertyThroughPropertyGroup() // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" @@ -2434,7 +2434,7 @@ public void AddNewPropertyThroughPropertyGroup() - + "; @@ -2442,7 +2442,7 @@ public void AddNewPropertyThroughPropertyGroup() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" @@ -2458,7 +2458,7 @@ public void AddNewPropertyThroughPropertyGroup() - + "; @@ -2484,7 +2484,7 @@ public void AddNewPropertyGroup() // ************************************ // BEFORE // ************************************ - string projectOriginalContents = @" + string projectOriginalContents = @" @@ -2499,7 +2499,7 @@ public void AddNewPropertyGroup() - + "; @@ -2507,7 +2507,7 @@ public void AddNewPropertyGroup() // ************************************ // AFTER // ************************************ - string projectNewExpectedContents = @" + string projectNewExpectedContents = @" @@ -2524,7 +2524,7 @@ public void AddNewPropertyGroup() - + "; @@ -2640,7 +2640,7 @@ public void RemovePropertyByName() - + "; @@ -2663,7 +2663,7 @@ public void RemovePropertyByName() - + "; @@ -2914,7 +2914,7 @@ public void RemoveAllPropertyGroupsByConditionWithChoose() c:\foobar - + c:\foobar @@ -2923,7 +2923,7 @@ public void RemoveAllPropertyGroupsByConditionWithChoose() c:\foobar - + c:\foobar @@ -3112,7 +3112,7 @@ public void SetPropertyOnExistingProperty() - + "; @@ -3135,7 +3135,7 @@ public void SetPropertyOnExistingProperty() - + "; @@ -3446,7 +3446,7 @@ public void VerifyMsbuildProgramFiles32ReservedProperty() $(MsBuildProgramFiles32) - + @@ -3513,7 +3513,7 @@ public void ModifyPropertyInImportedProjectFileAfterRename() Assertion.AssertEquals(@"c:\boobah", importedProj.EvaluatedProperties["ReferencePath"].FinalValueEscaped); importedProj.Save(Path.Combine(ObjectModelHelpers.TempProjectDir, "newimported.proj")); - + // Now we add a new imported property to the main file, into an existing imported // property group. mainProj.SetImportedProperty("ReferencePath", @"c:\hoohah", null, importedProj); @@ -3785,7 +3785,7 @@ public void RegistryProperties() Project p = ObjectModelHelpers.CreateInMemoryProject(@" - +

$(Registry:HKEY_CURRENT_USER\" + testRegistryPath + @"@Foo)

QValue @@ -3812,7 +3812,7 @@ public void RegistryPropertiesWithEscapedCharactersInValue() Project p = ObjectModelHelpers.CreateInMemoryProject(@" - +

$(Registry:HKEY_CURRENT_USER\" + testRegistryPath + @"@Foo)

QValue @@ -3834,7 +3834,7 @@ public class QueryProjectState { /// /// This tests the Project.EvaluatedItemsIgnoringCondition property. This - /// property should return the list of evaluated items in the project, + /// property should return the list of evaluated items in the project, /// pretending that all "Condition"s evaluated to true. /// /// RGoel @@ -3856,7 +3856,7 @@ public void GetEvaluatedItemsIgnoringCondition() - +
"; @@ -4098,7 +4098,7 @@ public void ReplaceImport() public class Evaluation { /// - /// Relative paths in 'exists' on conditions should be evalauted relative to the + /// Relative paths in 'exists' on conditions should be evalauted relative to the /// project directory. /// [Test] @@ -4142,7 +4142,7 @@ public void ImportConditionsEvaluatedUsingProjectsDirectory() } /// - /// Relative paths in 'exists' on conditions should be evalauted relative to the + /// Relative paths in 'exists' on conditions should be evalauted relative to the /// project directory. /// [Test] @@ -4181,7 +4181,7 @@ public void PropertyConditionsEvaluatedUsingProjectsDirectory() } /// - /// Relative paths in 'exists' on conditions should be evalauted relative to the + /// Relative paths in 'exists' on conditions should be evalauted relative to the /// project directory. /// [Test] @@ -4387,7 +4387,7 @@ public void InvalidMetadataName() foo - +
"; @@ -4432,7 +4432,7 @@ public void IllegalCharactersInUsingTaskAssemblyFile() Project project = ObjectModelHelpers.CreateInMemoryProject(original); } - + /// /// Unknown attribute on UsingTask should throw /// @@ -4449,7 +4449,7 @@ public void UnknownAttributeInUsingTask() // Should throw Project project = ObjectModelHelpers.CreateInMemoryProject(original); - } + } /// /// RequiredRuntime attribute on UsingTask should be ignored @@ -4467,10 +4467,10 @@ public void RequiredRuntimeAttributeInUsingTask() // Should not throw Project project = ObjectModelHelpers.CreateInMemoryProject(original); - } + } /// - /// Tests that putting invalid characters in the path results in a + /// Tests that putting invalid characters in the path results in a /// InvalidProjectFileException. /// /// RGoel @@ -4589,7 +4589,7 @@ public void SetNewGlobalProperty() } /// - /// This tests that the project is NOT marked as dirty when we set a + /// This tests that the project is NOT marked as dirty when we set a /// global property to the exact same value it had before. /// /// RGoel @@ -4649,7 +4649,7 @@ public void MSBuildExtensionsPathDefault() { expectedValue = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); } - + Assertion.AssertEquals(expectedValue + @"\MSBuild", (string)myProject.EvaluatedProperties[specialPropertyName]); @@ -4664,7 +4664,7 @@ public void MSBuildExtensionsPathDefault() public void MSBuildExtensionsPathDefault_Legacy() { string specialPropertyName = "MSBuildExtensionsPath"; - + // Save the old copy of the MSBuildExtensionsPath, so we can restore it when the unit test is done. string backupMSBuildExtensionsPath = Environment.GetEnvironmentVariable(specialPropertyName); string backupMagicSwitch = Environment.GetEnvironmentVariable("MSBUILDLEGACYEXTENSIONSPATH"); @@ -4753,7 +4753,7 @@ public void MSBuildExtensionsPathWithGlobalOverride() } /// - /// The default value for $(MSBuildExtensionsPath32) should point to "c:\program files (x86)\msbuild" on a 64 bit machine. + /// The default value for $(MSBuildExtensionsPath32) should point to "c:\program files (x86)\msbuild" on a 64 bit machine. /// We can't test that directly since tests generally don't run on 64 bit boxes. However we can set the "ProgramFiles(x86)" /// environment variable and make sure that that's the value used. /// @@ -5006,7 +5006,7 @@ public void MSBuildStartupDirectory() public class LoadAndSave { /// - /// Just load an MSBuild project by passing in a TextReader, and get back the contents to + /// Just load an MSBuild project by passing in a TextReader, and get back the contents to /// make sure the project was read in correctly. /// /// RGoel @@ -5206,7 +5206,7 @@ public void RemoveMissingImportAndLoadNormally() // Save the modified project StringWriter writer = new StringWriter(); project.Save(writer); - + // Load the modified project into a new project object xmldoc = new XmlDocument(); xmldoc.LoadXml(writer.ToString()); @@ -5258,7 +5258,7 @@ public void FailingTargetsDoNotHaveOutputs() } /// - /// Checks to make sure that passing in the DoNotResetPreviouslyBuiltTargets flag + /// Checks to make sure that passing in the DoNotResetPreviouslyBuiltTargets flag /// works as expected. /// /// JomoF @@ -5287,7 +5287,7 @@ public void CheckDoNotResetPreviouslyBuiltTargets() // At this point, the property $(FileExists) should be 'true' Assertion.AssertEquals("true", p.GetEvaluatedProperty("FileExists")); - // Delete the file + // Delete the file File.Delete(tempFile); // Build again. The result should still be 'true' because the target won't be reevaluated. @@ -5353,7 +5353,7 @@ public void RunInitialTargetsInMainProject() // Build the target. p.Build(null, null); - + Assertion.Assert("Build target should have been run.", myLogger.FullLog.Contains("BuildTargetExecuted")); Assertion.Assert("CheckForErrors target should have been run.", myLogger.FullLog.Contains("CheckForErrorsTargetExecuted")); } @@ -5457,14 +5457,14 @@ public void RunInitialTargetsInMainAndImportedProjects() ", importedProject1, importedProject2)); - Assertion.AssertEquals("Check all InitialTargets", "CheckForBadUser; CheckForBadProperties; CheckForBadConfigurations", + Assertion.AssertEquals("Check all InitialTargets", "CheckForBadUser; CheckForBadProperties; CheckForBadConfigurations", p.InitialTargets); // Build the default target. p.Build(null, null); DumpBuildItemGroup(p.GetEvaluatedItemsByName("TargetOrder")); - + // The following method will ensure that the targets were executed in the correct order. EngineHelpers.AssertItemsMatch(@" CheckForBadUser_Executed @@ -5479,14 +5479,14 @@ public void RunInitialTargetsInMainAndImportedProjects() // Change the InitialTargets on the main project to be "NewChecks", but do it via an environment variable. p.InitialTargets = "$(MyNewChecks)"; - Assertion.AssertEquals("Check all InitialTargets", "NewChecks; CheckForBadProperties; CheckForBadConfigurations", + Assertion.AssertEquals("Check all InitialTargets", "NewChecks; CheckForBadProperties; CheckForBadConfigurations", p.InitialTargets); // Build the default target. p.Build(null, null); DumpBuildItemGroup(p.GetEvaluatedItemsByName("TargetOrder")); - + // The following method will ensure that the targets were executed in the correct order. EngineHelpers.AssertItemsMatch(@" NewChecks_Executed @@ -5555,7 +5555,7 @@ public void ModifyInitialTargetsInMainProject() // Build the default target. p.Build(null, null); - + Assertion.Assert("Build target should have been run.", myLogger.FullLog.Contains("BuildTargetExecuted")); Assertion.Assert("CheckForErrors target should have been run.", myLogger.FullLog.Contains("CheckForErrorsTargetExecuted")); } @@ -5680,7 +5680,7 @@ public void SetGetProjectExtensions() } /// - /// There is a certain error that the MSBuild engine fires when you try to do a build on + /// There is a certain error that the MSBuild engine fires when you try to do a build on /// a project that has had its targets disabled because of security. However, the project /// system doesn't want to show this error to the user because it's not actionable for /// the user. So it looks for code MSB4112 to throw away this error. Here we're just @@ -5693,10 +5693,10 @@ public void VerifySecurityErrorHasCodeMSB4112() { ResourceManager resourceManager = new ResourceManager("Microsoft.Build.Engine.Resources.Strings", typeof(Project).Assembly); string securityMessage = resourceManager.GetString("SecurityProjectBuildDisabled", CultureInfo.CurrentUICulture); - - Assertion.Assert( + + Assertion.Assert( "Security message about disabled targets need to have code MSB4112, because code in the VS Core project system depends on this. See DesignTimeBuildFeedback.cpp.", - securityMessage.Contains("MSB4112") + securityMessage.Contains("MSB4112") ); } @@ -5790,12 +5790,12 @@ public void RegressVsWhidbey579075() ", logger); - + // Set a property and force project evaluation project.SetProperty("Configuration", "Release"); BuildPropertyGroup evaluatedProperties = project.EvaluatedProperties; - // Set a different value of the property and build without forced reevaluation, + // Set a different value of the property and build without forced reevaluation, // check if the new value is passed to the logger project.SetProperty("Configuration", "Debug"); project.Build(); @@ -5815,7 +5815,7 @@ public void VersionBasedMSBuildBinPathDefault() ", null); - Assertion.AssertEquals("Nonexistent ToolsVersion should evaluate to the default version", + Assertion.AssertEquals("Nonexistent ToolsVersion should evaluate to the default version", Constants.defaultToolsVersion, project.ToolsVersion); Assertion.AssertEquals("Nonexistent ToolsVersion should mean ToolsVersionAttribute is the default version", @@ -5823,7 +5823,7 @@ public void VersionBasedMSBuildBinPathDefault() Assertion.AssertEquals("BinPath is the MSBuildBinPath for the default version", "www.msbuild.org", project.EvaluatedProperties[ReservedPropertyNames.binPath].FinalValue); - + Assertion.AssertEquals("BinPath is the MSBuildToolsPath for the default version", "www.msbuild.org", project.EvaluatedProperties[ReservedPropertyNames.toolsPath].FinalValue); } @@ -5839,7 +5839,7 @@ public void VersionBasedMSBuildBinPathExplicit() ", null); - Assertion.AssertEquals("ToolsVersion should have been picked up from the project attribute", + Assertion.AssertEquals("ToolsVersion should have been picked up from the project attribute", "myValidToolsVersion", project.ToolsVersion); Assertion.AssertEquals("ToolsVersionAttribute should have been picked up from the project attribute", @@ -6010,7 +6010,7 @@ public void MSBuildToolsVersionProperty() Project project = ObjectModelHelpers.CreateInMemoryProject(e, ObjectModelHelpers.CleanupFileContents(@" - + @@ -6031,7 +6031,7 @@ public void MSBuildToolsVersionProperty2() Project project = ObjectModelHelpers.CreateInMemoryProject(e, ObjectModelHelpers.CleanupFileContents(@" - + @@ -6054,7 +6054,7 @@ public void SetEffectiveToolsVersionAttribute() Project project = ObjectModelHelpers.CreateInMemoryProject(e, ObjectModelHelpers.CleanupFileContents(@" - + $(MSBuildToolsVersion) @@ -6130,4 +6130,3 @@ public void PropertiesFromToolsetAppliedToProjectWhenToolsVersionOverridden() } } } - diff --git a/src/Deprecated/Engine/AssemblyInfo.cs b/src/Deprecated/Engine/AssemblyInfo.cs index f01f747da28..bf94342c24e 100644 --- a/src/Deprecated/Engine/AssemblyInfo.cs +++ b/src/Deprecated/Engine/AssemblyInfo.cs @@ -17,11 +17,4 @@ [assembly: CLSCompliant(true)] -// Needed for the "hub-and-spoke model to locate and retrieve localized resources": https://msdn.microsoft.com/en-us/library/21a15yht(v=vs.110).aspx -// We want "en" to require a satellite assembly for debug builds in order to flush out localization -// issues, but we want release builds to work without it. Also, .net core does not have resource fallbacks -#if (DEBUG && !RUNTIME_TYPE_NETCORE) -[assembly: NeutralResourcesLanguage("en", UltimateResourceFallbackLocation.Satellite)] -#else [assembly: NeutralResourcesLanguage("en")] -#endif diff --git a/src/Deprecated/Engine/Engine/Node.cs b/src/Deprecated/Engine/Engine/Node.cs index 5a5ff319324..da393b51c0c 100644 --- a/src/Deprecated/Engine/Engine/Node.cs +++ b/src/Deprecated/Engine/Engine/Node.cs @@ -435,7 +435,7 @@ internal void ReportUnhandledError(Exception originalException) } catch (Exception ex) { - // If an error occurred while trying to send the original exception to the parent + // If an error occurred while trying to send the original exception to the parent // rethrow the original exception string message = ResourceUtilities.FormatResourceString("FatalErrorOnChildNode", nodeId, ex.Message); @@ -630,7 +630,7 @@ private void NodeLocalEngineLoop() private ManualResetEvent exitNodeEvent; // The engine being used to process build requests private Engine localEngine; - // The queue of build requests arriving from the parent. The queue is needed to buffer the requests while the local engine is + // The queue of build requests arriving from the parent. The queue is needed to buffer the requests while the local engine is // being created and initialized private Queue buildRequests; // This flag is true if the thread that will be running the Engine.BuildLoop has been launched diff --git a/src/Deprecated/Engine/Introspector/Introspector.cs b/src/Deprecated/Engine/Introspector/Introspector.cs index a4c0f0cfc87..203896320f5 100644 --- a/src/Deprecated/Engine/Introspector/Introspector.cs +++ b/src/Deprecated/Engine/Introspector/Introspector.cs @@ -26,7 +26,7 @@ internal Introspector(Engine parentEngine, ProjectManager projectManager, NodeMa /// /// This method is called when the parent engine doesn't see activity for a preset time period to /// determine if the whole system is making forward progress. In order to that, status is collected - /// from every node in the system. If no node is making forward progress then the graph of all the + /// from every node in the system. If no node is making forward progress then the graph of all the /// inprogress targets is analyzed for cycles. If a cycle is found the appropriate node is instructed /// to break it. If no cause for deadlock can be determined the system is shutdown. /// @@ -90,7 +90,7 @@ internal int DetectDeadlock( int queueCounts, long lastLoopActivity, int current } else if (nodeStatus[i].HasExited) { - // A node has exited prematurely. The only option is to shutdown + // A node has exited prematurely. The only option is to shutdown LogOrDumpError("ChildExitedPrematurely", i + 1); SystemShutdown(); @@ -142,7 +142,7 @@ internal int DetectDeadlock( int queueCounts, long lastLoopActivity, int current cycleDetector.CycleEdgeParent); // Use the amount of time it took us to receive the NodeStatus and buffer it a little because node status is sent via a faster code path ignoreTimeout = DateTime.Now.Ticks + requestDurationTime + (cycleBreakTimeout * TimeSpan.TicksPerMillisecond); - return currentTimeout; + return currentTimeout; } // The system doesn't appear to be making progress. Switch to a largest sampling interval. @@ -208,7 +208,7 @@ private void LogOrDumpError(string resourceName, params object[] args) } /// - /// Adds a set of nodeStatus's to the cycle graph + /// Adds a set of nodeStatus's to the cycle graph /// private void AddTargetStatesToCycleDetector(NodeStatus[] nodeStatus, TargetCycleDetector cycleDetector) { @@ -229,7 +229,7 @@ private void GatherNodeInformationForShutdown(NodeStatus[] nodeStatus, NodeStatu { TimeSpan timeSinceLastNodeTaskActivity = new TimeSpan(nodeStatus[i].TimeSinceLastTaskActivity); TimeSpan timeSinceLastNodeLoopActivity = new TimeSpan(nodeStatus[i].TimeSinceLastLoopActivity); - + Console.WriteLine("Status: " + i + " Task Activity " + timeSinceLastNodeTaskActivity.TotalMilliseconds + " Loop Activity " + timeSinceLastNodeLoopActivity.TotalMilliseconds + " Queue depth " + nodeStatus[i].QueueDepth); @@ -262,11 +262,11 @@ internal void SystemShutdown() /// - /// This function is called to break the link between two targets that creates a cycle. The link could be + /// This function is called to break the link between two targets that creates a cycle. The link could be /// due to depends/onerror relationship between parent and child. In that case both parent and child are - /// on the same node and within the same project. Or the link could be formed by an IBuildEngine callback - /// (made such by tasks such as MSBuild or CallTarget) in which case there maybe multiple requests forming - /// same link between parent and child. Also in that case parent and child maybe on different nodes and/or in + /// on the same node and within the same project. Or the link could be formed by an IBuildEngine callback + /// (made such by tasks such as MSBuild or CallTarget) in which case there maybe multiple requests forming + /// same link between parent and child. Also in that case parent and child maybe on different nodes and/or in /// different projects. In either case the break is forced by finding the correct builds states and causing /// them to fail. /// @@ -291,7 +291,7 @@ internal void BreakCycle(TargetInProgessState child, TargetInProgessState parent { parentStates[i].CurrentBuildContextState = ProjectBuildState.BuildContextState.CycleDetected; TaskExecutionContext taskExecutionContext = - new TaskExecutionContext(parentProject, childTarget, null, parentStates[i], EngineCallback.invalidEngineHandle, + new TaskExecutionContext(parentProject, childTarget, null, parentStates[i], EngineCallback.invalidEngineHandle, EngineCallback.inProcNode, null); parentEngine.PostTaskOutputUpdates(taskExecutionContext); @@ -305,7 +305,7 @@ internal void BreakCycle(TargetInProgessState child, TargetInProgessState parent /// internal List FindConnectingContexts ( - TargetInProgessState child, + TargetInProgessState child, TargetInProgessState parent, Target childTarget, List waitingStates, diff --git a/src/Deprecated/Engine/LocalProvider/LocalNodeProvider.cs b/src/Deprecated/Engine/LocalProvider/LocalNodeProvider.cs index 07cc648695e..eb73adeb434 100644 --- a/src/Deprecated/Engine/LocalProvider/LocalNodeProvider.cs +++ b/src/Deprecated/Engine/LocalProvider/LocalNodeProvider.cs @@ -59,7 +59,7 @@ string startupDirectory } /* If we dont get a path passed in as a parameter, we can only assume that our path - is in the current appdomain basedirectory, this is the base directory + is in the current appdomain basedirectory, this is the base directory that the assembly resolver uses to probe for assemblies */ if (string.IsNullOrEmpty(this.locationOfMSBuildExe)) @@ -90,7 +90,7 @@ string startupDirectory lastUsedNodeNumber = nodeData[i].NodeNumber + 1; } - // Set up the callback + // Set up the callback this.engineCallback = parentEngineCallback; this.parentGlobalProperties = parentGlobalPropertyGroup; this.toolsetSearchLocations = toolSetSearchLocations; @@ -228,7 +228,7 @@ public void PostBuildRequestToNode(int nodeIndex, BuildRequest buildRequest) if (nodeData[nodeIndex].NodeState != NodeState.Launched) { // Note that we have to check the node status again inside the mutex. This - // ensures that that after flipping the status to launched inside the mutex + // ensures that that after flipping the status to launched inside the mutex // there will be no more writes to the queue of targets waiting to be sent lock (nodeStateLock) { @@ -319,7 +319,7 @@ public void ShutdownNodes(Node.NodeShutdownLevel nodeShutdownLevel) } } - // Reset the shutdown response received properties incase the nodes are going + // Reset the shutdown response received properties incase the nodes are going // to be used for another build on the same engine. foreach (LocalNodeInfo nodeInfo in nodeData) { @@ -459,7 +459,7 @@ private void TerminateChildNode(int processId) } catch (System.ComponentModel.Win32Exception) { - // The exception indicates that the child process is no longer running or + // The exception indicates that the child process is no longer running or // the parent cannot access the child process information due to insufficent security permissions } } @@ -697,7 +697,7 @@ private void InitializeNode(int nodeIndex) nodeInUseEvent.Close(); // If the node is still active and has not replied to the initialization message it must - // be in bad state - try to get that node to exit + // be in bad state - try to get that node to exit if (!nodeConnected && checkIfNodeActive(nodeData[nodeIndex].NodeNumber)) { EventWaitHandle nodeShutdownEvent = new EventWaitHandle(false, EventResetMode.ManualReset, LocalNodeProviderGlobalNames.NodeErrorShutdownEventName(nodeData[nodeIndex].NodeNumber)); @@ -738,7 +738,7 @@ private static bool checkIfNodeActive(int nodeNumber) } catch (WaitHandleCannotBeOpenedException) { - // Assume that the node is not running + // Assume that the node is not running } finally { diff --git a/src/Deprecated/Engine/Resources/xlf/Strings.en.xlf b/src/Deprecated/Engine/Resources/xlf/Strings.en.xlf deleted file mode 100644 index 5fdd9a1a8ef..00000000000 --- a/src/Deprecated/Engine/Resources/xlf/Strings.en.xlf +++ /dev/null @@ -1,1516 +0,0 @@ - - - - - - MSB4001: The "{0}" task has more than one parameter called "{1}". - MSB4001: The "{0}" task has more than one parameter called "{1}". - {StrBegin="MSB4001: "}UE: This message is shown when a task has more than one .NET property with the same name -- it's unclear which of - those properties the task wants to use as a parameter in project files. - - - MSB4081: The value "{0}" of the "ItemName" attribute in element <Output> contains an "@" character. If you intended to use an item name then remove the @( ) around the item name. - MSB4081: The value "{0}" of the "ItemName" attribute in element <Output> contains an "@" character. If you intended to use an item name then remove the @( ) around the item name. - {StrBegin="MSB4081: "}UE: This message is shown when an output tag has an itemname that contains an @. They probably typed @(foo) instead of foo in the ItemName.LOCALIZATION: Output and ItemName should not be localized. - - - MSB4002: There was a failure retrieving the attributes for parameters in the "{0}" task. {1} - MSB4002: There was a failure retrieving the attributes for parameters in the "{0}" task. {1} - {StrBegin="MSB4002: "}UE: This message is shown when the .NET attributes that a task's .NET properties are decorated with, cannot be - retrieved -- this is typically because the .NET classes that define the .NET attributes cannot be loaded because the assembly - they are defined in cannot be found, or the classes themselves cannot be found. - - - MSB4003: "{0}" is a reserved attribute of the <{1}> element, and must be spelled with the correct casing. This attribute cannot be used as a parameter to the "{2}" task. - MSB4003: "{0}" is a reserved attribute of the <{1}> element, and must be spelled with the correct casing. This attribute cannot be used as a parameter to the "{2}" task. - {StrBegin="MSB4003: "}UE: Tasks are not allowed to use incorrect case for reserved attributes on the task nodes e.g. "continueonerror" - instead of the "ContinueOnError". - - - The "REQUESTBATCHSIZE" must be a number greater than 1. "{0}" is an invalid value. The value 10 will be used instead. - The "REQUESTBATCHSIZE" must be a number greater than 1. "{0}" is an invalid value. The value 10 will be used instead. - The name "REQUESTBATCHSIZE" is an environment variable - - - Build completed in {0}. - Build completed in {0}. - - - - Build FAILED. - Build FAILED. - - - - Build succeeded. - Build succeeded. - - - - Build started. - Build started. - - - - Build started {0}. - Build started {0}. - - - - Building target "{0}" completely. - Building target "{0}" completely. - {0} is the name of the target. - - - No input files were specified. - No input files were specified. - - - - Input file "{0}" is newer than output file "{1}". - Input file "{0}" is newer than output file "{1}". - {0} and {1} are filenames on disk. - - - Output file "{0}" does not exist. - Output file "{0}" does not exist. - {0} is a filename on disk. - - - Input file "{0}" does not exist. - Input file "{0}" does not exist. - {0} is a filename on disk. - - - Building target "{0}" partially, because some output files are out of date with respect to their input files. - Building target "{0}" partially, because some output files are out of date with respect to their input files. - {0} is the name of the target. - - - [{0}: Input={1}, Output={2}] Input file is newer than output file. - [{0}: Input={1}, Output={2}] Input file is newer than output file. - {0} is the name of an MSBuild item. {1} and {2} are filenames on disk. - - - [{0}: Input={1}, Output={2}] Output file does not exist. - [{0}: Input={1}, Output={2}] Output file does not exist. - {0} is the name of an MSBuild item. {1} and {2} are filenames on disk. - - - [{0}: Input={1}, Output={2}] Input file does not exist. - [{0}: Input={1}, Output={2}] Input file does not exist. - {0} is the name of an MSBuild item. {1} and {2} are filenames on disk. - - - The attribute "{0}" is a known MSBuild attribute, and cannot be accessed using this method. - The attribute "{0}" is a known MSBuild attribute, and cannot be accessed using this method. - - - - Properties in persisted property groups cannot be accessed by name. - Properties in persisted property groups cannot be accessed by name. - - - - MSB4023: Cannot evaluate the item metadata "%({0})". {1} - MSB4023: Cannot evaluate the item metadata "%({0})". {1} - {StrBegin="MSB4023: "}UE: This message is shown when the value of an item metadata cannot be computed for some reason e.g. trying to apply - %(RootDir) to an item-spec that's not a valid path, would result in this error. - LOCALIZATION: "{1}" is a localized message explaining the problem. - - - Cannot execute a task not associated with a project object. - Cannot execute a task not associated with a project object. - - - - The cache entry has already been set to a different value and cannot be modified. - The cache entry has already been set to a different value and cannot be modified. - - - - The "{0}" property comes from an environment variable, and cannot be modified. - The "{0}" property comes from an environment variable, and cannot be modified. - - - - The "{0}" property is a global property, and cannot be modified through an evaluated property group. Use "{1}" instead. - The "{0}" property is a global property, and cannot be modified through an evaluated property group. Use "{1}" instead. - - - - Modifying the XML of an imported project file is not allowed. Open that project file directly. - Modifying the XML of an imported project file is not allowed. Open that project file directly. - - - - MSB4117: The "{0}" item name is reserved, and cannot be used. - MSB4117: The "{0}" item name is reserved, and cannot be used. - {StrBegin="MSB4117: "}UE: This message is shown when the user tries to redefine one of the reserved MSBuild items e.g. @(Choose) - - - MSB4118: The "{0}" item metadata name is reserved, and cannot be used. - MSB4118: The "{0}" item metadata name is reserved, and cannot be used. - {StrBegin="MSB4118: "}UE: This message is shown when the user tries to redefine one of the reserved MSBuild items e.g. @(Choose) - - - MSB4004: The "{0}" property is reserved, and cannot be modified. - MSB4004: The "{0}" property is reserved, and cannot be modified. - {StrBegin="MSB4004: "}UE: This message is shown when the user tries to redefine one of the reserved MSBuild properties e.g. $(MSBuildProjectFile) - - - MSB4094: "{0}" is an invalid value for the "{1}" parameter of the "{3}" task. Multiple items cannot be passed into a parameter of type "{2}". - MSB4094: "{0}" is an invalid value for the "{1}" parameter of the "{3}" task. Multiple items cannot be passed into a parameter of type "{2}". - {StrBegin="MSB4094: "} - UE: This error is shown when a project tries to pass multiple items into a task parameter of type ITaskItem (singular). - - - - MSB4115: The "{0}" function only accepts a scalar value, but its argument "{1}" evaluates to "{2}" which is not a scalar value. - MSB4115: The "{0}" function only accepts a scalar value, but its argument "{1}" evaluates to "{2}" which is not a scalar value. - {StrBegin="MSB4115: "} - UE: This error is shown when a project tries to pass multiple items into a function in a conditional expression, that can only accept a scalar value (such as the "exists()" function). - - - - The task is currently associated with a project object, and should not be added to a different one. - The task is currently associated with a project object, and should not be added to a different one. - - - - MSB4095: The item metadata %({0}) is being referenced without an item name. Specify the item name by using %(itemname.{0}). - MSB4095: The item metadata %({0}) is being referenced without an item name. Specify the item name by using %(itemname.{0}). - {StrBegin="MSB4095: "} - - - The task is not associated with the specified target element collection, and should not be removed from it. - The task is not associated with the specified target element collection, and should not be removed from it. - - - - MSB4005: The current working directory could not be restored to {0}. {1} - MSB4005: The current working directory could not be restored to {0}. {1} - {StrBegin="MSB4005: "}UE: This message is shown when the current working directory cannot be reset after a build. "{1}" contains a message explaining why. - LOCALIZATION: "{1}" is a message from an CLR/FX exception and is already localized. - - - Cannot set a condition on an object not represented by an XML element in the project file. - Cannot set a condition on an object not represented by an XML element in the project file. - - - - Cannot set ContinueOnError on an object not represented by an XML element in the project file. - Cannot set ContinueOnError on an object not represented by an XML element in the project file. - - - - MSB4134: DefaultToolsVersion cannot be set after a project has been loaded into the Engine. - MSB4134: DefaultToolsVersion cannot be set after a project has been loaded into the Engine. - {StrBegin="MSB4134: "} - - - Assigning the "{0}" attribute on an item that has been evaluated is not allowed. This operation is only allowed on the original persisted item that came directly from the project file. - Assigning the "{0}" attribute on an item that has been evaluated is not allowed. This operation is only allowed on the original persisted item that came directly from the project file. - - - - Assigning the "{0}" attribute of a virtual item is not allowed. - Assigning the "{0}" attribute of a virtual item is not allowed. - - - - A property cannot be set while building. - A property cannot be set while building. - - - - A property cannot be set to null. - A property cannot be set to null. - - - - Cannot get or set parameters on a task not associated with a project object. - Cannot get or set parameters on a task not associated with a project object. - - - - MSB4162: <{0}> is not valid. Child elements are not allowed below a item remove element. - MSB4162: <{0}> is not valid. Child elements are not allowed below a item remove element. - {StrBegin="MSB4162: "} - - - MSB4166: Child node "{0}" exited prematurely. Shutting down. - MSB4166: Child node "{0}" exited prematurely. Shutting down. - {StrBegin="MSB4166: "} - - - MSB4085: A <Choose> must contain at least one <When>. - MSB4085: A <Choose> must contain at least one <When>. - {StrBegin="MSB4085: "} - - - MSB4114: <Choose> elements cannot be nested more than {0} levels deep. - MSB4114: <Choose> elements cannot be nested more than {0} levels deep. - {StrBegin="MSB4114: "}UE: This message appears if the project file contains unreasonably nested Choose elements. - LOCALIZATION: Do not localize "Choose" as it is an XML element name. - - - MSB4006: There is a circular dependency in the target dependency graph involving target "{0}". - MSB4006: There is a circular dependency in the target dependency graph involving target "{0}". - {StrBegin="MSB4006: "}UE: This message is shown when the build engine detects a target referenced in a circular manner -- a project cannot - request a target to build itself (perhaps via a chain of other targets). - - - MSB4086: A numeric comparison was attempted on "{1}" that evaluates to "{2}" instead of a number, in condition "{0}". - MSB4086: A numeric comparison was attempted on "{1}" that evaluates to "{2}" instead of a number, in condition "{0}". - {StrBegin="MSB4086: "} - - - MSB4007: The clause "{0}", specified in the "{1}" attribute is invalid. - MSB4007: The clause "{0}", specified in the "{1}" attribute is invalid. - {StrBegin="MSB4007: "}UE: This message is shown when the Condition on an element is invalid in some way -- e.g. syntax error, unrecognized operator. - - - MSB4130: The condition "{0}" may have been evaluated incorrectly in an earlier version of MSBuild. Please verify that the order of the AND and OR clauses is written as intended. To avoid this warning, add parentheses to make the evaluation order explicit. - MSB4130: The condition "{0}" may have been evaluated incorrectly in an earlier version of MSBuild. Please verify that the order of the AND and OR clauses is written as intended. To avoid this warning, add parentheses to make the evaluation order explicit. - {StrBegin="MSB4130: "} - - - MSB4087: Specified condition "{0}" does not evaluate to a boolean. - MSB4087: Specified condition "{0}" does not evaluate to a boolean. - {StrBegin="MSB4087: "} - - - MSB4113: Specified condition "{0}" evaluates to "{1}" instead of a boolean. - MSB4113: Specified condition "{0}" evaluates to "{1}" instead of a boolean. - {StrBegin="MSB4113: "} - - - {0}, line {1} - {0}, line {1} - - - - MSB4136: Error reading the toolset information from the configuration file "{0}". {1} - MSB4136: Error reading the toolset information from the configuration file "{0}". {1} - {StrBegin="MSB4136: "} - - - MSB4008: A conflicting assembly for the task assembly "{0}" has been found at "{1}". - MSB4008: A conflicting assembly for the task assembly "{0}" has been found at "{1}". - {StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly. - - - MSB4142: MSBuildToolsPath is not the same as MSBuildBinPath for the ToolsVersion "{0}" defined at "{1}". If both are present they must have the same value. - MSB4142: MSBuildToolsPath is not the same as MSBuildBinPath for the ToolsVersion "{0}" defined at "{1}". If both are present they must have the same value. - {StrBegin="MSB4142: "} - - - MSB4097: The element <{0}> beneath element <{1}> may not have a custom XML namespace. - MSB4097: The element <{0}> beneath element <{1}> may not have a custom XML namespace. - {StrBegin="MSB4097: "} - - - MSB4009: The default tasks file could not be successfully loaded. {0} - MSB4009: The default tasks file could not be successfully loaded. {0} - {StrBegin="MSB4009: "}UE: This message is shown when one of the default tasks file (*.tasks) located alongside the MSBuild binaries cannot - be opened/parsed. "{0}" contains a message explaining why. The filename itself is not part of the message but is provided - separately to loggers. - LOCALIZATION: "{0}" is a message from some FX method and is already localized. - - - MSB4010: The "{0}" files could not be successfully loaded from their expected location "{1}". Default tasks will not be available. {2} - MSB4010: The "{0}" files could not be successfully loaded from their expected location "{1}". Default tasks will not be available. {2} - {StrBegin="MSB4010: "}UE: This message is shown when the default tasks files that are located alongside the MSBuild binaries cannot be - found, either because they don't exist, or because of lack of permissions. "{2}" contains a message explaining why. - LOCALIZATION: "{2}" is a message from some FX method and is already localized. - - - MSB4140: Default tools version is specified in neither the registry nor the configuration file. - MSB4140: Default tools version is specified in neither the registry nor the configuration file. - {StrBegin="MSB4140: "} - - - MSB4133: A default tools version "{0}" was specified, but its definition could not be found. - MSB4133: A default tools version "{0}" was specified, but its definition could not be found. - {StrBegin="MSB4133: "} - - - MSB4080: The value "{0}" of the "PropertyName" attribute in element <Output> contains a "$" character. If you intended to use a property name then remove the $( ) around the property name. - MSB4080: The value "{0}" of the "PropertyName" attribute in element <Output> contains a "$" character. If you intended to use a property name then remove the $( ) around the property name. - {StrBegin="MSB4080: "}UE: This message is shown when an output tag has an property name that contains an $. They probably typed $(foo) instead of foo in the PropertyName. LOCALIZATION: Output and PropertyName should not be localized. - - - MSB4011: There is a circular reference involving the import of file "{0}". This file may have been imported more than once, or you may have attempted to import the main project file. All except the first instance of this file will be ignored. - MSB4011: There is a circular reference involving the import of file "{0}". This file may have been imported more than once, or you may have attempted to import the main project file. All except the first instance of this file will be ignored. - {StrBegin="MSB4011: "} - - - MSB4079: The <ProjectExtensions> element occurs more than once. - MSB4079: The <ProjectExtensions> element occurs more than once. - {StrBegin="MSB4079: "} - - - MSB4012: The expression "{0}" cannot be used in this context. Item lists cannot be concatenated with other strings where an item list is expected. Use a semicolon to separate multiple item lists. - MSB4012: The expression "{0}" cannot be used in this context. Item lists cannot be concatenated with other strings where an item list is expected. Use a semicolon to separate multiple item lists. - {StrBegin="MSB4012: "}UE: This message is shown when the user does not properly specify an item list when an item list is expected - e.g. "badprefix@(foo)badsuffix" instead of "prefix; @(foo); suffix" - - - Need to specify the project file name. - Need to specify the project file name. - UE: This message is shown when the user calls into the engine to build a project without specifying a filename. - - - end of input - end of input - This is the name of the "EndOfInput" token. It is displayed in quotes as the - unexpected char or token when the end of a conditional was unexpectedly reached. - - - The previous error was converted to a warning because the task was called with ContinueOnError=true. - The previous error was converted to a warning because the task was called with ContinueOnError=true. - - - - {0} Error(s) - {0} Error(s) - - - - MSB4159: Error creating the toolset "{0}". {1} - MSB4159: Error creating the toolset "{0}". {1} - {StrBegin="MSB4159: "} - - - MSB4146: Cannot evaluate the property expression "{0}" found at "{1}". {2} - MSB4146: Cannot evaluate the property expression "{0}" found at "{1}". {2} - {StrBegin="MSB4146: "} - - - The <{0}> tag is no longer supported as a child of the <Project> element. Place this tag within a target, and add the name of the target to the "InitialTargets" attribute of the <Project> element. - The <{0}> tag is no longer supported as a child of the <Project> element. Place this tag within a target, and add the name of the target to the "InitialTargets" attribute of the <Project> element. - - - - MSB4100: Expected "{0}" to evaluate to a boolean instead of "{1}", in condition "{2}". - MSB4100: Expected "{0}" to evaluate to a boolean instead of "{1}", in condition "{2}". - {StrBegin="MSB4100: "} - - - MSB4028: The "{0}" task's outputs could not be retrieved from the "{1}" parameter. {2} - MSB4028: The "{0}" task's outputs could not be retrieved from the "{1}" parameter. {2} - {StrBegin="MSB4028: "} - - - MSB4014: The build was aborted because of an internal failure. - MSB4014: The build was aborted because of an internal failure. - {StrBegin="MSB4014: "}UE: This message is shown when an unhandled exception terminates the build. The cause is most likely a programming - error in the build engine. - - - MSB4015: The build was aborted because the "{0}" logger failed unexpectedly during shutdown. - MSB4015: The build was aborted because the "{0}" logger failed unexpectedly during shutdown. - {StrBegin="MSB4015: "}UE: This message is used for a special exception that is thrown when a logger fails while shutting down (most likely - because of a programming error in the logger). When a logger dies, we cannot proceed with the build, and we throw a special - exception to abort the build. - - - MSB4016: The build was aborted because the "{0}" logger failed unexpectedly during initialization. - MSB4016: The build was aborted because the "{0}" logger failed unexpectedly during initialization. - {StrBegin="MSB4016: "}UE: This message is used for a special exception that is thrown when a logger fails while initializing itself (most - likely because of a programming error in the logger). When a logger dies, we cannot proceed with the build, and we throw a - special exception to abort the build. - - - MSB4017: The build was aborted because of an unexpected logger failure. - MSB4017: The build was aborted because of an unexpected logger failure. - {StrBegin="MSB4017: "}UE: This message is used for a special exception that is thrown when a logger fails while logging an event (most - likely because of a programming error in the logger). When a logger dies, we cannot proceed with the build, and we throw a - special exception to abort the build. - - - MSB4018: The "{0}" task failed unexpectedly. - MSB4018: The "{0}" task failed unexpectedly. - {StrBegin="MSB4018: "}UE: This message is shown when a task terminates because of an unhandled exception. The cause is most likely a - programming error in the task; however, it is also possible that the unhandled exception originated in the engine, and was - surfaced through the task when the task called into the engine. - - - MSB4165: Failed to receive a response from the child node "{0}" in the timeout period "{1}" ms. Shutting down. - MSB4165: Failed to receive a response from the child node "{0}" in the timeout period "{1}" ms. Shutting down. - {StrBegin="MSB4165: "} - - - MSB4088: Condition "{0}" is improperly constructed. - MSB4088: Condition "{0}" is improperly constructed. - {StrBegin="MSB4088: "} - - - MSB4105: Found an unexpected character '{2}' at position {1} in condition "{0}". Did you intend to use "=="? - MSB4105: Found an unexpected character '{2}' at position {1} in condition "{0}". Did you intend to use "=="? - {StrBegin="MSB4105: "} - - - MSB4106: Expected an item list at position {1} in condition "{0}". Did you forget the closing parenthesis? - MSB4106: Expected an item list at position {1} in condition "{0}". Did you forget the closing parenthesis? - {StrBegin="MSB4106: "} - - - MSB4107: Expected an item list at position {1} in condition "{0}". Did you forget the opening parenthesis after the '@'? To use a literal '@', use '%40' instead. - MSB4107: Expected an item list at position {1} in condition "{0}". Did you forget the opening parenthesis after the '@'? To use a literal '@', use '%40' instead. - {StrBegin="MSB4107: "} - - - MSB4108: Expected an item list at position {1} in condition "{0}". Did you forget to close a quote inside the item list expression? - MSB4108: Expected an item list at position {1} in condition "{0}". Did you forget to close a quote inside the item list expression? - {StrBegin="MSB4108: "} - - - MSB4109: Expected a property at position {1} in condition "{0}". Did you forget the closing parenthesis? - MSB4109: Expected a property at position {1} in condition "{0}". Did you forget the closing parenthesis? - {StrBegin="MSB4109: "} - - - MSB4110: Expected a property at position {1} in condition "{0}". Did you forget the opening parenthesis after the '$'? To use a literal '$', use '%24' instead. - MSB4110: Expected a property at position {1} in condition "{0}". Did you forget the opening parenthesis after the '$'? To use a literal '$', use '%24' instead. - {StrBegin="MSB4110: "} - - - MSB4101: Expected a closing quote after position {1} in condition "{0}". - MSB4101: Expected a closing quote after position {1} in condition "{0}". - {StrBegin="MSB4101: "} - - - MSB4019: The imported project "{0}" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk. - MSB4019: The imported project "{0}" was not found. Confirm that the path in the <Import> declaration is correct, and that the file exists on disk. - {StrBegin="MSB4019: "}LOCALIZATION: <Import> should not be localized. - - - MSB4089: Incorrect number of arguments to function in condition "{0}". Found {1} argument(s) when expecting {2}. - MSB4089: Incorrect number of arguments to function in condition "{0}". Found {1} argument(s) when expecting {2}. - {StrBegin="MSB4089: "} - - - The "{0}" object specified does not belong to the correct "{1}" object. - The "{0}" object specified does not belong to the correct "{1}" object. - - - - MSB4020: The value "{0}" of the "{1}" attribute in element <{2}> is invalid. - MSB4020: The value "{0}" of the "{1}" attribute in element <{2}> is invalid. - {StrBegin="MSB4020: "}UE: This is a generic message that is displayed when we find a project element with an incorrect value for one of its - attributes e.g. <Import Project=""> -- the value of Project should not be an empty string. - - - MSB4102: The value "{0}" of the "{1}" attribute in element <{2}> is invalid. {3} - MSB4102: The value "{0}" of the "{1}" attribute in element <{2}> is invalid. {3} - {StrBegin="MSB4102: "}UE: This is a generic message that is displayed when we find a project element with an incorrect value for one of its - attributes. At the end of the message we show the exception text we got trying to use the value. - - - MSB4021: The "ContinueOnError" attribute of the "{0}" task is not valid. {1} - MSB4021: The "ContinueOnError" attribute of the "{0}" task is not valid. {1} - {StrBegin="MSB4021: "}LOCALIZATION: "ContinueOnError" should not be localized. "{1}" is a message from another exception explaining the problem. - - - MSB4139: Invalid value for default ToolsVersion is specified in registry key "{0}". - MSB4139: Invalid value for default ToolsVersion is specified in registry key "{0}". - {StrBegin="MSB4139: "} - - - MSB4022: The result "{0}" of evaluating the value "{1}" of the "{2}" attribute in element <{3}> is not valid. - MSB4022: The result "{0}" of evaluating the value "{1}" of the "{2}" attribute in element <{3}> is not valid. - {StrBegin="MSB4022: "}UE: This message is shown when the engine is checking the correctness of the value (after evaluating embedded - properties/items) assigned to an XML attribute of an XML element in the project file. - - - "{0}" is not a valid event category. To raise a custom event, use the "{1}" category. - "{0}" is not a valid event category. To raise a custom event, use the "{1}" category. - - - - "{0}" is not a valid importance level for events. - "{0}" is not a valid importance level for events. - - - - MSB4104: Failed to write to log file "{0}". {1} - MSB4104: Failed to write to log file "{0}". {1} - {StrBegin="MSB4104: "}UE: This is shown when the File Logger can't create or write to the file it was instructed to log to. - - - MSB4024: The imported project file could not be loaded. {0} - MSB4024: The imported project file could not be loaded. {0} - {StrBegin="MSB4024: "}UE: This message is shown when an imported project file cannot be loaded because of incorrect XML. The project - filename is not part of the message because it is provided separately to loggers. - LOCALIZATION: {0} is a localized message from the CLR/FX explaining why the project is invalid. - - - Operation invalid for a persisted item group. - Operation invalid for a persisted item group. - - - - Operation invalid for a virtual item group. - Operation invalid for a virtual item group. - - - - MSB4071: The value Importance="{0}" in the <Message> element is invalid. Valid values are: High, Normal and Low. - MSB4071: The value Importance="{0}" in the <Message> element is invalid. Valid values are: High, Normal and Low. - {StrBegin="MSB4071: "}UE: This message is shown when a user specifies a value for the importance attribute of Message which is not valid. - The importance enumeration is: High, Normal and Low. Specifying any other importance will result in this message being shown - LOCALIZATION: "Importance" should not be localized. - "Message" should not be localized. - High should not be localized. - Normal should not be localized. - Low should not be localized. - - - MSB4025: The project file could not be loaded. {0} - MSB4025: The project file could not be loaded. {0} - {StrBegin="MSB4025: "}UE: This message is shown when the project file given to the engine cannot be loaded because the filename/path is - invalid, or due to lack of permissions, or incorrect XML. The project filename is not part of the message because it is - provided separately to loggers. - LOCALIZATION: {0} is a localized message from the CLR/FX explaining why the project is invalid. - - - MSB4147: The property "{0}" at "{1}" is invalid. {2} - MSB4147: The property "{0}" at "{1}" is invalid. {2} - {StrBegin="MSB4147: "} - - - "{0}" is an invalid "ProjectFileEncoding" value. - "{0}" is an invalid "ProjectFileEncoding" value. - LOCALIZATION: "ProjectFileEncoding" should not be localized. - - - MSB4143: The expression "{0}" cannot be evaluated. {1} - MSB4143: The expression "{0}" cannot be evaluated. {1} - {StrBegin="MSB4143: "} - UE: This message is shown when the user attempts to provide an expression like "$(Registry:HKEY_LOCAL_MACHINE\Software\Vendor\Tools@TaskLocation)" - LOCALIZATION: "{0}" is the expression that was bad. "{1}" is a message from an FX exception that describes why the expression is bad. - - - - MSB4184: The expression "{0}" cannot be evaluated. {1} - MSB4184: The expression "{0}" cannot be evaluated. {1} - {StrBegin="MSB4184: "} - Single quotes as the expression will typically have double quotes in it. - UE: This message is shown when the user attempts to provide an expression like "$(SomeProperty.ToLower())" - LOCALIZATION: "{0}" is the expression that was bad. "{1}" is a message from an FX exception that describes why the expression is bad. - - - - MSB4185: The function "{0}" on type "{1}" has not been enabled for execution. - MSB4185: The function "{0}" on type "{1}" has not been enabled for execution. - {StrBegin="MSB4185: "} - UE: This message is shown when the user attempts to provide an expression like "$([System.DateTime]::Now)", but the expression has not been enabled - LOCALIZATION: "{0}" is the static function name, "{1}" is the .NET Framework type name - - - - MSB4186: Invalid static method invocation syntax: "{0}". Static method invocation should be of the form: $([FullTypeName]::Method()), e.g. $([System.IO.Path]::Combine(`a`, `b`)) - MSB4186: Invalid static method invocation syntax: "{0}". Static method invocation should be of the form: $([FullTypeName]::Method()), e.g. $([System.IO.Path]::Combine(`a`, `b`)) - {StrBegin="MSB4186: "} - UE: This message is shown when the user attempts to call a static method on a type, but has used the incorrect syntax - LOCALIZATION: "{0}" is the function expression which is in error - - - - MSB4070: The schema "{0}" is not valid. {1} - MSB4070: The schema "{0}" is not valid. {1} - {StrBegin="MSB4070: "}UE: This message is shown when the schema file provided for the validation of a project is itself not valid. - LOCALIZATION: "{0}" is the schema file path. "{1}" is a message from an FX exception that describes why the schema file is bad. - - - MSB4026: The "{0}={1}" parameter for the "{2}" task is invalid. - MSB4026: The "{0}={1}" parameter for the "{2}" task is invalid. - {StrBegin="MSB4026: "}UE: This message is displayed when a task has an invalid parameter that cannot be initialized. - - - MSB4027: The "{0}" task generated invalid items from the "{1}" output parameter. {2} - MSB4027: The "{0}" task generated invalid items from the "{1}" output parameter. {2} - {StrBegin="MSB4027: "} - - - MSB4029: The "{0}" task has an invalid output specification. The "TaskParameter" attribute is required, and either the "ItemName" or "PropertyName" attribute must be specified (but not both). - MSB4029: The "{0}" task has an invalid output specification. The "TaskParameter" attribute is required, and either the "ItemName" or "PropertyName" attribute must be specified (but not both). - {StrBegin="MSB4029: "}LOCALIZATION: "TaskParameter", "ItemName" and "PropertyName" should not be localized. - - - MSB4030: "{0}" is an invalid value for the "{1}" parameter of the "{3}" task. The "{1}" parameter is of type "{2}". - MSB4030: "{0}" is an invalid value for the "{1}" parameter of the "{3}" task. The "{1}" parameter is of type "{2}". - {StrBegin="MSB4030: "}UE: This error is shown when a type mis-match occurs between the value assigned to task parameter in the project file - and the type of the .NET property that corresponds to the task parameter. For example, if an int task parameter called "Count" - is assigned the value "x", this error would be displayed: <MyTask Count="x" /> - - - MSB4137: Invalid value specified in the configuration file at "{0}". Property name or tools version name is an empty string. - MSB4137: Invalid value specified in the configuration file at "{0}". Property name or tools version name is an empty string. - {StrBegin="MSB4137: "} - - - MSB4103: "{0}" is not a valid logger verbosity level. - MSB4103: "{0}" is not a valid logger verbosity level. - {StrBegin="MSB4103: "} - - - MSB4163: <ItemDefinitionGroup> is not allowed inside a target. - MSB4163: <ItemDefinitionGroup> is not allowed inside a target. - {StrBegin="MSB4163: "} - - - The specified item does not belong to the current item group. - The specified item does not belong to the current item group. - - - - MSB4096: The item "{0}" in item list "{1}" does not define a value for metadata "{2}". In order to use this metadata, either qualify it by specifying %({1}.{2}), or ensure that all items in this list define a value for this metadata. - MSB4096: The item "{0}" in item list "{1}" does not define a value for metadata "{2}". In order to use this metadata, either qualify it by specifying %({1}.{2}), or ensure that all items in this list define a value for this metadata. - {StrBegin="MSB4096: "} - - - MSB4099: A reference to an item list at position {1} is not allowed in this condition "{0}". - MSB4099: A reference to an item list at position {1} is not allowed in this condition "{0}". - {StrBegin="MSB4099: "} - - - MSB4033: "{0}" is a reserved item metadata, and cannot be redefined as a custom metadata on the item. - MSB4033: "{0}" is a reserved item metadata, and cannot be redefined as a custom metadata on the item. - {StrBegin="MSB4033: "} - - - An InternalLoggerException can only be thrown by the MSBuild engine. The public constructors of this class cannot be used to create an instance of the exception. - An InternalLoggerException can only be thrown by the MSBuild engine. The public constructors of this class cannot be used to create an instance of the exception. - UE: This message is shown when a user tries to instantiate a special exception called InternalLoggerException through the OM -- - only the engine is allowed to create and throw this exception. - LOCALIZATION: "InternalLoggerException" and "MSBuild" should not be localized. - - - Initial Items: - Initial Items: - - - - MSB4083: Expected a Condition attribute on element "{0}". - MSB4083: Expected a Condition attribute on element "{0}". - {StrBegin="MSB4083: "} - - - MSB4164: The value "{0}" of metadata "{1}" contains an item list expression. Item list expressions are not allowed on default metadata values. - MSB4164: The value "{0}" of metadata "{1}" contains an item list expression. Item list expressions are not allowed on default metadata values. - {StrBegin="MSB4164: "} - - - MSBuild is expecting a valid "{0}" object. - MSBuild is expecting a valid "{0}" object. - - - - MSB4035: The required attribute "{0}" is missing from element <{1}>. - MSB4035: The required attribute "{0}" is missing from element <{1}>. - {StrBegin="MSB4035: "}UE: This message is shown when a user leaves off a required attribute from a project element - e.g. <UsingTask AssemblyName="foo"> -- this is missing the "TaskName" attribute. - - - MSB4036: The "{0}" task was not found. Check the following: 1.) The name of the task in the project file is the same as the name of the task class. 2.) The task class is "public" and implements the Microsoft.Build.Framework.ITask interface. 3.) The task is correctly declared with <UsingTask> in the project file, or in the *.tasks files located in the "{1}" directory. - MSB4036: The "{0}" task was not found. Check the following: 1.) The name of the task in the project file is the same as the name of the task class. 2.) The task class is "public" and implements the Microsoft.Build.Framework.ITask interface. 3.) The task is correctly declared with <UsingTask> in the project file, or in the *.tasks files located in the "{1}" directory. - {StrBegin="MSB4036: "}LOCALIZATION: <UsingTask> and "*.tasks" should not be localized. - - - MSB4141: MSBuildToolsPath is not specified for the ToolsVersion "{0}" defined at "{1}", or the value specified evaluates to the empty string. - MSB4141: MSBuildToolsPath is not specified for the ToolsVersion "{0}" defined at "{1}", or the value specified evaluates to the empty string. - {StrBegin="MSB4141: "} - - - MSB4031: The "MSBuildVersion" attribute is deprecated. If the project is authored in the MSBuild 2003 format, please remove the attribute from the <Project> tag. If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. - MSB4031: The "MSBuildVersion" attribute is deprecated. If the project is authored in the MSBuild 2003 format, please remove the attribute from the <Project> tag. If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. - {StrBegin="MSB4031: "}UE: This message is shown for projects that still contain the "MSBuildVersion" attribute in their <Project> - tag. We no longer need this attribute because the project's XML namespace (i.e. the "xmlns" attribute) gives us all the - version information we need. - LOCALIZATION: "MSBuild", "MSBuildVersion" and <Project> should not be localized. - - - MSB4144: Multiple definitions were found for the toolset "{0}". - MSB4144: Multiple definitions were found for the toolset "{0}". - {StrBegin="MSB4144: "} - - - MSB4145: Multiple definitions were found for the property "{0}". - MSB4145: Multiple definitions were found for the property "{0}". - {StrBegin="MSB4145: "} - - - MSB4082: Choose has more than one <Otherwise> element. - MSB4082: Choose has more than one <Otherwise> element. - {StrBegin="MSB4082: "} - - - This method is only valid for persisted <{0}> elements. - This method is only valid for persisted <{0}> elements. - - - - This method is only valid for virtual property groups, not <{0}> elements. - This method is only valid for virtual property groups, not <{0}> elements. - - - - MSB4038: The element <{0}> must be last under element <{1}>. Found element <{2}> instead. - MSB4038: The element <{0}> must be last under element <{1}>. Found element <{2}> instead. - {StrBegin="MSB4038: "} - - - MSB4138: Non-string data was specified at the registry location "{0}". - MSB4138: Non-string data was specified at the registry location "{0}". - {StrBegin="MSB4138: "} - - - MSB4039: No "{0}" element was found in the project file. - MSB4039: No "{0}" element was found in the project file. - {StrBegin="MSB4039: "} - - - MSB4040: There is no target in the project. - MSB4040: There is no target in the project. - {StrBegin="MSB4040: "} - - - Removing the "{0}" attribute of an item is not allowed. - Removing the "{0}" attribute of an item is not allowed. - - - - The object passed in is not part of the project. - The object passed in is not part of the project. - - - - Overriding target "{0}" in project "{1}" with target "{2}" from project "{3}". - Overriding target "{0}" in project "{1}" with target "{2}" from project "{3}". - - - - MSB4167: The parent process unexpectedly exited. Shutting down child node "{0}". - MSB4167: The parent process unexpectedly exited. Shutting down child node "{0}". - {StrBegin="MSB4167: "} - - - {0} ms {1} {2} calls - {0} ms {1} {2} calls - - - - (* = timing was not recorded because of reentrancy) - (* = timing was not recorded because of reentrancy) - - - - This project object has been unloaded from the MSBuild engine and is no longer valid. - This project object has been unloaded from the MSBuild engine and is no longer valid. - - - - The project file "{0}" was not found. - The project file "{0}" was not found. - UE: This message is shown when the user calls into the OM to build a project that doesn't exist on disk. - - - Done building project "{0}" -- FAILED. - Done building project "{0}" -- FAILED. - - - - Done building project "{0}". - Done building project "{0}". - - - - MSB4041: The default XML namespace of the project must be the MSBuild XML namespace. If the project is authored in the MSBuild 2003 format, please add xmlns="{0}" to the <Project> element. If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. - MSB4041: The default XML namespace of the project must be the MSBuild XML namespace. If the project is authored in the MSBuild 2003 format, please add xmlns="{0}" to the <Project> element. If the project has been authored in the old 1.0 or 1.2 format, please convert it to MSBuild 2003 format. - {StrBegin="MSB4041: "}UE: This is a Beta 1 message only. - LOCALIZATION: <Project>, "MSBuild" and "xmlns" should not be localized. - - - Project Performance Summary: - Project Performance Summary: - - - - MSB4042: Stopping because of syntax errors in project file. - MSB4042: Stopping because of syntax errors in project file. - {StrBegin="MSB4042: "} - - - Project "{0}" is building "{1}" ({2} target(s)): - Project "{0}" is building "{1}" ({2} target(s)): - - - - Project "{0}" is building "{1}" (default targets): - Project "{0}" is building "{1}" (default targets): - - - - Project "{0}" ({1} target(s)): - Project "{0}" ({1} target(s)): - - - - Project "{0}" (default targets): - Project "{0}" (default targets): - - - - Done Building Project "{0}" ({1} target(s)). - Done Building Project "{0}" ({1} target(s)). - - - - Done Building Project "{0}" (default targets). - Done Building Project "{0}" (default targets). - - - - Done Building Project "{0}" ({1} target(s)) -- FAILED. - Done Building Project "{0}" ({1} target(s)) -- FAILED. - - - - Done Building Project "{0}" (default targets) -- FAILED. - Done Building Project "{0}" (default targets) -- FAILED. - - - - MSB4075: The project file must be opened in the Visual Studio IDE and converted to the latest version before it can be built by MSBuild. - MSB4075: The project file must be opened in the Visual Studio IDE and converted to the latest version before it can be built by MSBuild. - {StrBegin="MSB4075: "} - - - MSB4192: The project file "{0}" is in the ".vcproj" file format, which MSBuild no longer supports. Please convert the project by opening it in the Visual Studio IDE or running the conversion tool, or use MSBuild 3.5 or earlier to build it. - MSB4192: The project file "{0}" is in the ".vcproj" file format, which MSBuild no longer supports. Please convert the project by opening it in the Visual Studio IDE or running the conversion tool, or use MSBuild 3.5 or earlier to build it. - {StrBegin="MSB4192: "} LOC: ".vcproj" should not be localized - - - The specified property does not belong to the current property group. - The specified property does not belong to the current property group. - - - - Initial Properties: - Initial Properties: - - - - MSB4148: The name of a property stored under the registry key "{0}" has zero length. - MSB4148: The name of a property stored under the registry key "{0}" has zero length. - {StrBegin="MSB4148: "} - - - The property names in the indexer and the "{0}" object do not match. - The property names in the indexer and the "{0}" object do not match. - - - - MSB4043: The item metadata reference "{0}" is invalid because it is qualified with an item name. Item metadata referenced in transforms do not need to be qualified, because the item name is automatically deduced from the items being transformed. Change "{0}" to "%({1})". - MSB4043: The item metadata reference "{0}" is invalid because it is qualified with an item name. Item metadata referenced in transforms do not need to be qualified, because the item name is automatically deduced from the items being transformed. Change "{0}" to "%({1})". - {StrBegin="MSB4043: "}UE: This message is shown when the user does something like this: @(foo->'%(foo.metadata)'). There is no need to specify - "foo.metadata", because "foo" is automatically deduced. In corollary, "bar.metadata" is not allowed either, where "bar" is a different - item list type. - - - MSB4135: Error reading the toolset information from the registry location "{0}". {1} - MSB4135: Error reading the toolset information from the registry location "{0}". {1} - {StrBegin="MSB4135: "} - - - MSB4044: The "{0}" task was not given a value for the required parameter "{1}". - MSB4044: The "{0}" task was not given a value for the required parameter "{1}". - {StrBegin="MSB4044: "}UE: This message is shown when a task parameter designated as "required" is not set in the project file. - - - Validating project using schema file "{0}". - Validating project using schema file "{0}". - LOCALIZATION: "{0}" is the location of the schema file. - - - MSB4045: Project is not valid. {0} - MSB4045: Project is not valid. {0} - {StrBegin="MSB4045: "}UE: This error is shown when the user asks his project to be validated against a schema (/val switch for - MSBuild.exe), and the project has errors. "{0}" contains a message explaining the problem. - LOCALIZATION: "{0}" is a message from the System.XML schema validator and is already localized. - - - MSB4112: The targets in this project have been disabled by the host and therefore cannot be built at this time. This may have been done for security reasons. To enable the targets, the host must set Project.BuildEnabled to "true". - MSB4112: The targets in this project have been disabled by the host and therefore cannot be built at this time. This may have been done for security reasons. To enable the targets, the host must set Project.BuildEnabled to "true". - {StrBegin="MSB4112: "} - - - MSB4093: The "{0}" parameter of the "{1}" task cannot be written to because it does not have a "set" accessor. - MSB4093: The "{0}" parameter of the "{1}" task cannot be written to because it does not have a "set" accessor. - {StrBegin="MSB4093: "}UE: This error is shown when a project tries to assign a value to a task parameter that does not have a "set" - accessor on the corresponding .NET property on the task class. - - - A shallow clone of this object cannot be created. - A shallow clone of this object cannot be created. - - - - Skipping target "{0}" because it has no inputs. - Skipping target "{0}" because it has no inputs. - - - - Though the target has declared its inputs, the input specification only references empty properties and/or empty item lists. - Though the target has declared its inputs, the input specification only references empty properties and/or empty item lists. - - - - Skipping target "{0}" because it has no outputs. - Skipping target "{0}" because it has no outputs. - - - - Though the target has declared its outputs, the output specification only references empty properties and/or empty item lists. - Though the target has declared its outputs, the output specification only references empty properties and/or empty item lists. - - - - Skipping target "{0}" because all output files are up-to-date with respect to the input files. - Skipping target "{0}" because all output files are up-to-date with respect to the input files. - - - - Input files: {0} - Input files: {0} - {0} is a semicolon-separated list of filenames. - - - Output files: {0} - Output files: {0} - {0} is a semicolon-separated list of filenames. - - - Building solution configuration "{0}". - Building solution configuration "{0}". - UE: This is not an error, so doesn't need an error code. - - - Using solution cache file "{0}" for configuration "{1}" and tools version "{2}". - Using solution cache file "{0}" for configuration "{1}" and tools version "{2}". - UE: This is not an error, so doesn't need an error code. - - - Failed to read solution cache file "{0}". {1} Using solution file directly. - Failed to read solution cache file "{0}". {1} Using solution file directly. - UE: This is not a true error, so doesn't need an error code. - - - Failed to write solution cache file "{0}". {1} - Failed to write solution cache file "{0}". {1} - UE: This is not a true error, so doesn't need an error code. - - - Solution cache file was created for a "{0}" value of "{1}" but the current value is "{2}". Refreshing cache file. - Solution cache file was created for a "{0}" value of "{1}" but the current value is "{2}". Refreshing cache file. - UE: This is not an error, so doesn't need an error code. - - - Solution cache file has an internal version number "{0}" but the current value is "{1}". Refreshing cache file. - Solution cache file has an internal version number "{0}" but the current value is "{1}". Refreshing cache file. - UE: This is not an error, so doesn't need an error code. - - - Solution cache file is out of date. Refreshing cache file. {0} - Solution cache file is out of date. Refreshing cache file. {0} - UE: This is not an error, so doesn't need an error code. - - - MSB4160: A circular dependency involving project "{0}" has been detected. - MSB4160: A circular dependency involving project "{0}" has been detected. - {StrBegin="MSB4160: "} - - - MSB4126: The specified solution configuration "{0}" is invalid. Please specify a valid solution configuration using the Configuration and Platform properties (e.g. MSBuild.exe Solution.sln /p:Configuration=Debug /p:Platform="Any CPU") or leave those properties blank to use the default solution configuration. - MSB4126: The specified solution configuration "{0}" is invalid. Please specify a valid solution configuration using the Configuration and Platform properties (e.g. MSBuild.exe Solution.sln /p:Configuration=Debug /p:Platform="Any CPU") or leave those properties blank to use the default solution configuration. - {StrBegin="MSB4126: "}UE: The solution filename is provided separately to loggers. - - - MSB4046: Error reading project file "{0}": {1} - MSB4046: Error reading project file "{0}": {1} - {StrBegin="MSB4046: "} - - - MSB4125: The project file name "{0}" is invalid. {1} - MSB4125: The project file name "{0}" is invalid. {1} - {StrBegin="MSB4125: "}UE: The solution filename is provided separately to loggers. - - - MSB4051: Project {0} is referencing a project with GUID {1}, but a project with this GUID was not found in the .SLN file. - MSB4051: Project {0} is referencing a project with GUID {1}, but a project with this GUID was not found in the .SLN file. - {StrBegin="MSB4051: "}UE: The solution filename is provided separately to loggers. - - - MSB4078: The project file "{0}" is not supported by MSBuild and cannot be built. - MSB4078: The project file "{0}" is not supported by MSBuild and cannot be built. - {StrBegin="MSB4078: "} - - - MSB4054: The solution file must be opened in the Visual Studio IDE and converted to the latest version before it can be built by MSBuild. - MSB4054: The solution file must be opened in the Visual Studio IDE and converted to the latest version before it can be built by MSBuild. - {StrBegin="MSB4054: "}UE: The solution filename is provided separately to loggers. - - - MSB4121: The project configuration for project "{0}" was not specified in the solution file for the solution configuration "{1}". - MSB4121: The project configuration for project "{0}" was not specified in the solution file for the solution configuration "{1}". - {StrBegin="MSB4121: "} - - - The project "{0}" is not selected for building in solution configuration "{1}". - The project "{0}" is not selected for building in solution configuration "{1}". - - UE: This is not an error, so doesn't need an error code. - - - - MSB4122: Scanning project dependencies for project "{0}" failed. {1} - MSB4122: Scanning project dependencies for project "{0}" failed. {1} - {StrBegin="MSB4122: "} - - - MSB4149: The tools version "{0}" of the solution does not support building projects with a different tools version. - MSB4149: The tools version "{0}" of the solution does not support building projects with a different tools version. - {StrBegin="MSB4149: "} - - - Web projects do not support the "Clean" target. Continuing with remaining projects ... - Web projects do not support the "Clean" target. Continuing with remaining projects ... - UE: This is not an error, so doesn't need an error code. - LOCALIZATION: Do not localize "Clean". - - - Web projects do not support the "Publish" target. Continuing with remaining projects ... - Web projects do not support the "Publish" target. Continuing with remaining projects ... - UE: This is not an error, so doesn't need an error code. - LOCALIZATION: Do not localize "Publish". - - - Skipping because the "$(AspNetConfiguration)" configuration is not supported for this web project. You can use the AspNetConfiguration property to override the configuration used for building web projects, by adding /p:AspNetConfiguration=<value> to the command line. Currently web projects only support Debug and Release configurations. - Skipping because the "$(AspNetConfiguration)" configuration is not supported for this web project. You can use the AspNetConfiguration property to override the configuration used for building web projects, by adding /p:AspNetConfiguration=<value> to the command line. Currently web projects only support Debug and Release configurations. - - UE: This is not an error, so doesn't need an error code. - LOCALIZATION: Do NOT localize "AspNetConfiguration", "Debug", "Release". - - - - MSB4076: VC projects do not support the "Publish" target. - MSB4076: VC projects do not support the "Publish" target. - {StrBegin="MSB4076: "}LOCALIZATION: Do not localize "Publish". - - - MSB4098: MSBuild is invoking VCBuild to build this project. Project-to-project references between VC++ projects (.VCPROJ) and C#/VB/VJ# projects (.CSPROJ, .VBPROJ, .VJSPROJ) are not supported by the command-line build systems when building stand-alone VC++ projects. Projects that contain such project-to-project references will fail to build. Please build the solution file containing this project instead. - MSB4098: MSBuild is invoking VCBuild to build this project. Project-to-project references between VC++ projects (.VCPROJ) and C#/VB/VJ# projects (.CSPROJ, .VBPROJ, .VJSPROJ) are not supported by the command-line build systems when building stand-alone VC++ projects. Projects that contain such project-to-project references will fail to build. Please build the solution file containing this project instead. - {StrBegin="MSB4098: "} - - - MSB4056: The MSBuild engine must be called on a single-threaded-apartment. Current threading model is "{0}". Proceeding, but some tasks may not function correctly. - MSB4056: The MSBuild engine must be called on a single-threaded-apartment. Current threading model is "{0}". Proceeding, but some tasks may not function correctly. - {StrBegin="MSB4056: "} - - - Schema validation - Schema validation - UE: this fragment is used to describe errors that are caused by schema validation. For example, if a normal error is - displayed like this: "MSBUILD : error MSB0000: This is an error.", then an error from schema validation would look like this: - "MSBUILD : Schema validation error MSB0000: This is an error." - LOCALIZATION: This fragment needs to be localized. - - - Target "{0}" skipped. Previously built unsuccessfully. - Target "{0}" skipped. Previously built unsuccessfully. - - - - Target "{0}" skipped. Previously built successfully. - Target "{0}" skipped. Previously built successfully. - - - - MSB4116: The condition "{1}" on the "{0}" target has a reference to item metadata. References to item metadata are not allowed in target conditions unless they are part of an item transform. - MSB4116: The condition "{1}" on the "{0}" target has a reference to item metadata. References to item metadata are not allowed in target conditions unless they are part of an item transform. - {StrBegin="MSB4116: "} - - - MSB4057: The target "{0}" does not exist in the project. - MSB4057: The target "{0}" does not exist in the project. - {StrBegin="MSB4057: "} - - - Done building target "{0}" in project "{1}" -- FAILED. - Done building target "{0}" in project "{1}" -- FAILED. - - - - Done building target "{0}" in project "{1}". - Done building target "{0}" in project "{1}". - - - - {0}: (TargetId:{1}) - {0}: (TargetId:{1}) - - - - MSB4058: The "{0}" target is missing its output specification. If a target declares inputs, it must also declare outputs. - MSB4058: The "{0}" target is missing its output specification. If a target declares inputs, it must also declare outputs. - {StrBegin="MSB4058: "} - - - MSB4168: The item "{0}" of type "{1}" does not define a value for the metadata in the expression "{2}". This expression is used in the target output for target "{3}". If a target declares outputs that are transforms, all items in the transform must have a value for the metadata in the transform. - MSB4168: The item "{0}" of type "{1}" does not define a value for the metadata in the expression "{2}". This expression is used in the target output for target "{3}". If a target declares outputs that are transforms, all items in the transform must have a value for the metadata in the transform. - {StrBegin="MSB4168: "} - - - Target Performance Summary: - Target Performance Summary: - - - - Target "{0}" skipped, due to false condition; ({1}) was evaluated as ({2}). - Target "{0}" skipped, due to false condition; ({1}) was evaluated as ({2}). - - - - Target "{0}" in project "{1}" - Target "{0}" in project "{1}" - - - - Target {0}: - Target {0}: - - - - Target "{0}" in file "{1}": - Target "{0}" in file "{1}": - - - - Build continuing because "{0}" on the task "{1}" is set to "{2}". - Build continuing because "{0}" on the task "{1}" is set to "{2}". - - - - Target {0} from project "{1}": - Target {0} from project "{1}": - - - - Target "{0}" in file "{1}" from project "{2}": - Target "{0}" in file "{1}" from project "{2}": - - - - MSB4060: The "{0}" task has been declared or used incorrectly, or failed during construction. Check the spelling of the task name and the assembly name. - MSB4060: The "{0}" task has been declared or used incorrectly, or failed during construction. Check the spelling of the task name and the assembly name. - {StrBegin="MSB4060: "} - - - MSB4077: The "{0}" task has been marked with the attribute LoadInSeparateAppDomain, but does not derive from MarshalByRefObject. Check that the task derives from MarshalByRefObject or AppDomainIsolatedTask. - MSB4077: The "{0}" task has been marked with the attribute LoadInSeparateAppDomain, but does not derive from MarshalByRefObject. Check that the task derives from MarshalByRefObject or AppDomainIsolatedTask. - {StrBegin="MSB4077: "}LOCALIZATION: <LoadInSeparateAppDomain>, <MarshalByRefObject>, <AppDomainIsolatedTask> should not be localized. - - - Done executing task "{0}" -- FAILED. - Done executing task "{0}" -- FAILED. - - - - Done executing task "{0}". - Done executing task "{0}". - - - - {0} (TaskId:{1}) - {0} (TaskId:{1}) - - - - Using "{0}" task from assembly "{1}". - Using "{0}" task from assembly "{1}". - UE: This informational message helps users determine which assemblies their tasks were loaded from. - - - MSB4061: The "{0}" task could not be instantiated from the assembly "{1}". {2} - MSB4061: The "{0}" task could not be instantiated from the assembly "{1}". {2} - {StrBegin="MSB4061: "}LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. - - - MSB4127: The "{0}" task could not be instantiated from the assembly "{1}". Please verify the task assembly has been built using the same version of the Microsoft.Build.Framework assembly as the one installed on your computer and that your host application is not missing a binding redirect for Microsoft.Build.Framework. {2} - MSB4127: The "{0}" task could not be instantiated from the assembly "{1}". Please verify the task assembly has been built using the same version of the Microsoft.Build.Framework assembly as the one installed on your computer and that your host application is not missing a binding redirect for Microsoft.Build.Framework. {2} - {StrBegin="MSB4127: "}UE: This message is a specialized version of the TaskInstantiationFailureError message and can probably reuse some of its docs. - LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. Also, Microsoft.Build.Framework should not be localized - - - MSB4062: The "{0}" task could not be loaded from the assembly {1}. {2} Confirm that the <UsingTask> declaration is correct, and that the assembly and all its dependencies are available. - MSB4062: The "{0}" task could not be loaded from the assembly {1}. {2} Confirm that the <UsingTask> declaration is correct, and that the assembly and all its dependencies are available. - {StrBegin="MSB4062: "}UE: This message is shown when a task cannot be loaded from its assembly for various reasons e.g. corrupt assembly, - invalid task declaration, disk error, etc. "{2}" contains a message explaining what happened. - LOCALIZATION: "{2}" is a message from the CLR loader and is already localized. Also, <UsingTask> should not be localized. - - - MSB4063: The "{0}" task could not be initialized with its input parameters. {1} - MSB4063: The "{0}" task could not be initialized with its input parameters. {1} - {StrBegin="MSB4063: "} - - - Task Performance Summary: - Task Performance Summary: - - - - Task "{0}" skipped, due to false condition; ({1}) was evaluated as ({2}). - Task "{0}" skipped, due to false condition; ({1}) was evaluated as ({2}). - - - - Task "{0}" - Task "{0}" - - - - Time Elapsed {0} - Time Elapsed {0} - - - - Building with tools version "{0}". - Building with tools version "{0}". - - - - Project file contains ToolsVersion="{0}". This toolset is unknown or missing. You may be able to resolve this by installing the appropriate .NET Framework for this toolset. Treating the project as if it had ToolsVersion="4.0". - Project file contains ToolsVersion="{0}". This toolset is unknown or missing. You may be able to resolve this by installing the appropriate .NET Framework for this toolset. Treating the project as if it had ToolsVersion="4.0". - - - - Deferred Messages - Deferred Messages - - - - Some messages did not display because they were not associated with any ProjectStarted events. Use diagnostic verbosity to view these messages. - Some messages did not display because they were not associated with any ProjectStarted events. Use diagnostic verbosity to view these messages. - - - - MSB4090: Found an unexpected character '{2}' at position {1} in condition "{0}". - MSB4090: Found an unexpected character '{2}' at position {1} in condition "{0}". - {StrBegin="MSB4090: "} - - - MSB4091: Found a call to an undefined function "{1}" in condition "{0}". - MSB4091: Found a call to an undefined function "{1}" in condition "{0}". - {StrBegin="MSB4091: "} - - - MSB4064: The "{0}" parameter is not supported by the "{1}" task. Verify the parameter exists on the task, and it is a settable public instance property. - MSB4064: The "{0}" parameter is not supported by the "{1}" task. Verify the parameter exists on the task, and it is a settable public instance property. - {StrBegin="MSB4064: "} - - - MSB4131: The "{0}" parameter is not supported by the "{1}" task. Verify the parameter exists on the task, and it is a gettable public instance property. - MSB4131: The "{0}" parameter is not supported by the "{1}" task. Verify the parameter exists on the task, and it is a gettable public instance property. - {StrBegin="MSB4131: "} - - - MSB4092: An unexpected token "{1}" was found at character position {2} in condition "{0}". - MSB4092: An unexpected token "{1}" was found at character position {2} in condition "{0}". - {StrBegin="MSB4092: "} - - - MSB4065: The "{0}" parameter is not marked for output by the "{1}" task. - MSB4065: The "{0}" parameter is not marked for output by the "{1}" task. - {StrBegin="MSB4065: "} - - - MSB4066: The attribute "{0}" in element <{1}> is unrecognized. - MSB4066: The attribute "{0}" in element <{1}> is unrecognized. - {StrBegin="MSB4066: "} - - - MSB4067: The element <{0}> beneath element <{1}> is unrecognized. - MSB4067: The element <{0}> beneath element <{1}> is unrecognized. - {StrBegin="MSB4067: "} - - - MSB4068: The element <{0}> is unrecognized, or not supported in this context. - MSB4068: The element <{0}> is unrecognized, or not supported in this context. - {StrBegin="MSB4068: "} - - - MSB4132: The tools version "{0}" is unrecognized. - MSB4132: The tools version "{0}" is unrecognized. - {StrBegin="MSB4132: "} - - - MSB4069: The "{0}" type of the "{1}" parameter of the "{2}" task is not supported by MSBuild. - MSB4069: The "{0}" type of the "{1}" parameter of the "{2}" task is not supported by MSBuild. - {StrBegin="MSB4069: "}LOCALIZATION: "MSBuild" should not be localized. - - - MSB4072: A <{0}> element must contain either the "{1}" attribute or the "{2}" attribute (but not both). - MSB4072: A <{0}> element must contain either the "{1}" attribute or the "{2}" attribute (but not both). - {StrBegin="MSB4072: "} - - - {0} Warning(s) - {0} Warning(s) - - - - MSB4084: A <When> element may not follow an <Otherwise> element in a <Choose>. - MSB4084: A <When> element may not follow an <Otherwise> element in a <Choose>. - {StrBegin="MSB4084: "} - - - Target Name: "{0}" Project Name: "{1}" - Target Name: "{0}" Project Name: "{1}" - - - - Cycle trace: - Cycle trace: - - - - MSB4150: Must initialize the console logger using the initialize method before using ApplyParameter - MSB4150: Must initialize the console logger using the initialize method before using ApplyParameter - {StrBegin="MSB4150: "} - - - MSB4151: An error occurred while building project "{0}", target "{1}". Please see innerException for detailed information. - MSB4151: An error occurred while building project "{0}", target "{1}". Please see innerException for detailed information. - {StrBegin="MSB4151: "} - - - MSB4152: An error occurred on the child node "{0}" and could not be passed to the parent node. {1} - MSB4152: An error occurred on the child node "{0}" and could not be passed to the parent node. {1} - {StrBegin="MSB4152: "} - - - MSB4153: A call is made on an inactive IBuildEngine interface corresponding to a task that already finished execution. - MSB4153: A call is made on an inactive IBuildEngine interface corresponding to a task that already finished execution. - {StrBegin="MSB4153: "} - - - MSB4154: A forwarding logger is attempting to forward BuildFinished event - MSB4154: A forwarding logger is attempting to forward BuildFinished event - {StrBegin="MSB4154: "} - - - MSB4155: There was an error while communicating with a node. - MSB4155: There was an error while communicating with a node. - {StrBegin="MSB4155: "} - - - MSB1021: Cannot create an instance of the logger - {0}. - MSB1021: Cannot create an instance of the logger - {0}. - {StrBegin="MSB1021: "}The error code for this message is duplicated from MSBuild.exe. - - - MSB1020: The logger {0} was not found. Check the following: 1.) The logger name specified is the same as the name of the logger class. 2.) The logger class is "public" and implements the Microsoft.Build.Framework.ILogger interface. 3.) The path to the logger assembly is correct, or the logger can be loaded using only the assembly name provided. - MSB1020: The logger {0} was not found. Check the following: 1.) The logger name specified is the same as the name of the logger class. 2.) The logger class is "public" and implements the Microsoft.Build.Framework.ILogger interface. 3.) The path to the logger assembly is correct, or the logger can be loaded using only the assembly name provided. - {StrBegin="MSB1020: "}The error code for this message is duplicated from MSBuild.exe. - - - MSB4156: A forwarding logger is attempting to forward BuildStarted event - MSB4156: A forwarding logger is attempting to forward BuildStarted event - {StrBegin="MSB4156: "} - - - "{0}" ({1} target) ({2}) -> - "{0}" ({1} target) ({2}) -> - - - - "{0}" (default target) ({1}) -> - "{0}" (default target) ({1}) -> - - - - {0} {1,5} - {0} {1,5} - - - - Project "{0}" on node {1} ({2} target(s)). - Project "{0}" on node {1} ({2} target(s)). - - - - Project "{0}" on node {1} (default targets). - Project "{0}" on node {1} (default targets). - - - - Project "{0}" ({1}) is building "{2}" ({3}) on node {4} ({5} target(s)). - Project "{0}" ({1}) is building "{2}" ({3}) on node {4} ({5} target(s)). - - - - Project "{0}" ({1}) is building "{2}" ({3}) on node {4} (default targets). - Project "{0}" ({1}) is building "{2}" ({3}) on node {4} (default targets). - - - - ({0} target) -> - ({0} target) -> - - - - MSB4157: The array of project files needs to contain at least one value. - MSB4157: The array of project files needs to contain at least one value. - {StrBegin="MSB4157: "} - - - MSB4158: The project file name at element {0} is empty. - MSB4158: The project file name at element {0} is empty. - {StrBegin="MSB4158: "} - - - The property "{0}" with value "{1}" is being overridden by another batch. The property is now: "{2}" - The property "{0}" with value "{1}" is being overridden by another batch. The property is now: "{2}" - - - - Please see inner exception for error information. - Please see inner exception for error information. - - - - Event type "{0}" was expected to be serializable. The event was not serializable and has been ignored. - Event type "{0}" was expected to be serializable. The event was not serializable and has been ignored. - - - - The log file path cannot be null or empty. - The log file path cannot be null or empty. - - - - MSB4161: Project "{0}" was loaded and unloaded during the current build. Reloading a project during the build can result in errors or an inconsistent build state. Either avoid unloading project "{0}" or cache the evaluation results of target(s) "{1}" before unloading the project. - MSB4161: Project "{0}" was loaded and unloaded during the current build. Reloading a project during the build can result in errors or an inconsistent build state. Either avoid unloading project "{0}" or cache the evaluation results of target(s) "{1}" before unloading the project. - {StrBegin="MSB4161: "} - - - [default] - [default] - - - - - \ No newline at end of file diff --git a/src/Deprecated/Engine/Resources/xlf/Strings.zh-Hans.xlf b/src/Deprecated/Engine/Resources/xlf/Strings.zh-Hans.xlf index 031b8bc6e53..95cc52355de 100644 --- a/src/Deprecated/Engine/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/Deprecated/Engine/Resources/xlf/Strings.zh-Hans.xlf @@ -1,4 +1,4 @@ - +
diff --git a/src/Deprecated/Engine/Resources/xlf/Strings.zh-Hant.xlf b/src/Deprecated/Engine/Resources/xlf/Strings.zh-Hant.xlf index 52c6f2222ec..9d065a332ba 100644 --- a/src/Deprecated/Engine/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/Deprecated/Engine/Resources/xlf/Strings.zh-Hant.xlf @@ -1,4 +1,4 @@ - +
diff --git a/src/Deprecated/Engine/Shared/ErrorUtilities.cs b/src/Deprecated/Engine/Shared/ErrorUtilities.cs index 213310ce25e..1af0f4ceb05 100644 --- a/src/Deprecated/Engine/Shared/ErrorUtilities.cs +++ b/src/Deprecated/Engine/Shared/ErrorUtilities.cs @@ -32,7 +32,7 @@ internal static void LaunchMsBuildDebuggerOnFatalError() /// Puts up an assertion dialog in debug builds, and throws an exception in /// both debug and release builds. Since this is not a no-op in release builds, /// it should not be called repeatedly in performance-critical scenarios. - /// + /// /// PERF WARNING: calling a method that takes a variable number of arguments /// is expensive, because memory is allocated for the array of arguments -- do /// not call this method repeatedly in performance-critical scenarios @@ -48,7 +48,7 @@ private static void ThrowInternalError params object[] args ) { - // We ignore showAssert: we don't want to show the assert dialog no matter what. + // We ignore showAssert: we don't want to show the assert dialog no matter what. throw new InternalErrorException(ResourceUtilities.FormatString(unformattedMessage, args)); } @@ -243,7 +243,7 @@ object arg3 /// /// Throws an InvalidOperationException. - /// + /// /// PERF WARNING: calling a method that takes a variable number of arguments /// is expensive, because memory is allocated for the array of arguments -- do /// not call this method repeatedly in performance-critical scenarios @@ -597,7 +597,7 @@ internal static void VerifyThrowArgumentNull(object parameter, string parameterN { if (parameter == null) { - // Most ArgumentNullException overloads append its own rather clunky multi-line message. + // Most ArgumentNullException overloads append its own rather clunky multi-line message. // So use the one overload that doesn't. throw new ArgumentNullException( ResourceUtilities.FormatResourceString(resourceName, parameterName), diff --git a/src/Deprecated/Engine/Shared/FrameworkLocationHelper.cs b/src/Deprecated/Engine/Shared/FrameworkLocationHelper.cs index fd30818c312..bf0b4845cec 100644 --- a/src/Deprecated/Engine/Shared/FrameworkLocationHelper.cs +++ b/src/Deprecated/Engine/Shared/FrameworkLocationHelper.cs @@ -12,7 +12,7 @@ namespace Microsoft.Build.BuildEngine.Shared internal static class FrameworkLocationHelper { #region Private and internal members - + /// /// By default when a root path is not specified we would like to use the program files directory \ reference assemblies\framework as the root location /// to generate the reference assembly paths from. @@ -48,9 +48,9 @@ internal static class FrameworkLocationHelper internal const string fullDotNetFrameworkSdkRegistryKeyV35 = "HKEY_LOCAL_MACHINE\\" + dotNetFrameworkSdkRegistryPathV35; private const string dotNetFrameworkRegistryKeyV35 = dotNetFrameworkSetupRegistryPath + "\\" + dotNetFrameworkVersionFolderPrefixV35; internal const string dotNetFrameworkSdkInstallKeyValueV35 = "InstallationFolder"; - + internal const string dotNetFrameworkVersionFolderPrefixV35 = "v3.5"; // v3.5 is for Orcas. - + private const string dotNetFrameworkAssemblyFoldersRegistryKeyV35 = dotNetFrameworkAssemblyFoldersRegistryPath + "\\" + dotNetFrameworkVersionFolderPrefixV35; private const string secondaryDotNetFrameworkSdkRegistryPathV35 = "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows"; internal const string secondaryDotNetFrameworkSdkInstallKeyValueV35 = "CurrentInstallFolder"; @@ -66,8 +66,7 @@ internal static class FrameworkLocationHelper internal const string secondaryDotNetFrameworkSdkInstallKeyValueV40 = "CurrentInstallFolder"; private const string dotNetFrameworkRegistryKeyV40 = dotNetFrameworkSetupRegistryPath + "\\v4\\Full"; private static readonly GetDirectories getDirectories = new GetDirectories(Directory.GetDirectories); - - + private static string pathToDotNetFrameworkV11; internal static string PathToDotNetFrameworkV11 { @@ -249,13 +248,13 @@ internal static string PathToDotNetFrameworkSdkV35 dotNetFrameworkSdkInstallKeyValueV35); // Because there is no longer a strong 1:1 mapping between FX versions and SDK - // versions, if we're unable to locate the desired SDK version, we will try to + // versions, if we're unable to locate the desired SDK version, we will try to // use whichever SDK version is installed by looking at the key pointing to the // "latest" version. // - // This isn't ideal, but it will allow our tasks to function on any of several + // This isn't ideal, but it will allow our tasks to function on any of several // related SDKs even if they don't have exactly the same versions. - + if (String.IsNullOrEmpty(FrameworkLocationHelper.pathToDotNetFrameworkSdkV35)) { FrameworkLocationHelper.pathToDotNetFrameworkSdkV35 = FindRegistryValueUnderKey( @@ -281,11 +280,11 @@ internal static string PathToDotNetFrameworkSdkV40 dotNetFrameworkSdkInstallKeyValueV40); // Because there is no longer a strong 1:1 mapping between FX versions and SDK - // versions, if we're unable to locate the desired SDK version, we will try to + // versions, if we're unable to locate the desired SDK version, we will try to // use whichever SDK version is installed by looking at the key pointing to the // "latest" version. For example, instead of 6.0A, we might fall back to 6.0B. // - // This isn't ideal, but it will allow our tasks to function on any of several + // This isn't ideal, but it will allow our tasks to function on any of several // related SDKs even if they don't have exactly the same versions. if (String.IsNullOrEmpty(FrameworkLocationHelper.pathToDotNetFrameworkSdkV40)) @@ -506,7 +505,7 @@ GetDirectories getDirectories // The intention here is to choose the alphabetical maximum. string max = directories[0]; - // the max.EndsWith condition: pre beta 2 versions of v3.5 have build number like v3.5.20111. + // the max.EndsWith condition: pre beta 2 versions of v3.5 have build number like v3.5.20111. // This was removed in beta2 // We should favor \v3.5 over \v3.5.xxxxx // versions previous to 2.0 have .xxxx version numbers. 3.0 and 3.5 do not. @@ -530,7 +529,7 @@ GetDirectories getDirectories } #endregion - + /// /// Determine the 32 bit program files directory, this is used for finding where the reference assemblies live. /// @@ -549,7 +548,7 @@ internal static string GenerateProgramFiles32() } /// - /// Generate the path to the program files reference assembly location by taking in the program files special folder and then + /// Generate the path to the program files reference assembly location by taking in the program files special folder and then /// using that path to generate the path to the reference assemblies location. /// internal static string GenerateProgramFilesReferenceAssemblyRoot() diff --git a/src/Directory.BeforeCommon.targets b/src/Directory.BeforeCommon.targets index 9bafce8e113..913c97b1281 100644 --- a/src/Directory.BeforeCommon.targets +++ b/src/Directory.BeforeCommon.targets @@ -99,7 +99,7 @@ $(DefineConstants);FEATURE_DEBUGGER $(DefineConstants);FEATURE_WIN32_REGISTRY $(DefineConstants);FEATURE_WORKINGSET - $(DefineConstants);FEATURE_VISUALSTUDIOSETUP + $(DefineConstants);FEATURE_VISUALSTUDIOSETUP $(DefineConstants);FEATURE_MSCOREE @@ -110,7 +110,7 @@ $(DefineConstants);TEST_ISWINDOWS - + true $(DefineConstants);RUNTIME_TYPE_NETCORE @@ -124,7 +124,7 @@ $(DefineConstants);WORKAROUND_COREFX_19110 - + $(DefineConstants);FEATURE_PIPEOPTIONS_CURRENTUSERONLY $(DefineConstants);FEATURE_NODE_REUSE diff --git a/src/Directory.Build.props b/src/Directory.Build.props index d91dd8521f6..b0e23319446 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -32,16 +32,13 @@ AnyCPU - net5.0 + net6.0 $(FullFrameworkTFM);$(RuntimeOutputTargetFrameworks) $(FullFrameworkTFM) false - - en;cs;de;es;fr;it;ja;ko;pl;pt-BR;ru;tr;zh-Hans;zh-Hant - true @@ -67,6 +69,9 @@ PrivateAssets to keep this reference (auto-added when targeting netcoreapp*) from making it into our NuGet packages. --> + + + @@ -78,10 +83,9 @@ $(AssemblyName) $(MSBuildProjectName) - net - netstandard - netstandard - netstandard + net + netstandard + netstandard $(RepoRoot)ref\$(GenAPIAssemblyName)\$(GenAPIShortFrameworkIdentifier)\$(GenAPIAssemblyName).cs @@ -100,6 +104,16 @@ Visible="false" Pack="false"/> + + + + true + MSBuild:_GenerateResxSource + Microsoft.CodeAnalysis.Collections.SR + + + + diff --git a/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj b/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj index b73c360d59d..2043a496e46 100644 --- a/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj +++ b/src/Framework.UnitTests/Microsoft.Build.Framework.UnitTests.csproj @@ -1,4 +1,4 @@ - + @@ -13,6 +13,7 @@ + diff --git a/src/Framework/BuildEventContext.cs b/src/Framework/BuildEventContext.cs index edde77d5fbb..0cda93d76c6 100644 --- a/src/Framework/BuildEventContext.cs +++ b/src/Framework/BuildEventContext.cs @@ -306,5 +306,9 @@ private bool InternalEquals(BuildEventContext buildEventContext) } #endregion + public override string ToString() + { + return $"Node={NodeId} Submission={SubmissionId} ProjectContext={ProjectContextId} ProjectInstance={ProjectInstanceId} Eval={EvaluationId} Target={TargetId} Task={TaskId}"; + } } } diff --git a/src/Framework/BuildMessageEventArgs.cs b/src/Framework/BuildMessageEventArgs.cs index 23282a1db87..e84c9ed6c02 100644 --- a/src/Framework/BuildMessageEventArgs.cs +++ b/src/Framework/BuildMessageEventArgs.cs @@ -302,7 +302,11 @@ internal override void CreateFromStream(BinaryReader reader, int version) /// /// Importance of the message /// - public MessageImportance Importance => importance; + public MessageImportance Importance + { + get => importance; + internal set => importance = value; + } /// /// The custom sub-type of the event. @@ -322,12 +326,20 @@ internal override void CreateFromStream(BinaryReader reader, int version) /// /// Line number of interest in associated file. /// - public int LineNumber => lineNumber; + public int LineNumber + { + get => lineNumber; + internal set => lineNumber = value; + } /// /// Column number of interest in associated file. /// - public int ColumnNumber => columnNumber; + public int ColumnNumber + { + get => columnNumber; + internal set => columnNumber = value; + } /// /// Ending line number of interest in associated file. diff --git a/src/Framework/ChangeWaves.cs b/src/Framework/ChangeWaves.cs index 7761c468d44..d97becfa1d0 100644 --- a/src/Framework/ChangeWaves.cs +++ b/src/Framework/ChangeWaves.cs @@ -22,10 +22,9 @@ internal enum ChangeWaveConversionState /// For dev docs: https://github.com/dotnet/msbuild/blob/master/documentation/wiki/ChangeWaves-Dev.md internal class ChangeWaves { - internal static readonly Version Wave16_8 = new Version(16, 8); internal static readonly Version Wave16_10 = new Version(16, 10); internal static readonly Version Wave17_0 = new Version(17, 0); - internal static readonly Version[] AllWaves = { Wave16_8, Wave16_10, Wave17_0 }; + internal static readonly Version[] AllWaves = { Wave16_10, Wave17_0 }; /// /// Special value indicating that all features behind all Change Waves should be enabled. diff --git a/src/Framework/EngineServices.cs b/src/Framework/EngineServices.cs new file mode 100644 index 00000000000..43b8d10f7c6 --- /dev/null +++ b/src/Framework/EngineServices.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace Microsoft.Build.Framework +{ + /// + /// Exposes build engine functionality that was made available in newer versions of MSBuild. + /// + /// + /// Make all members virtual but not abstract, ensuring that implementations can override them and external implementations + /// won't break when the class is extended with new members. This base implementation should be throwing . + /// + [Serializable] + public abstract class EngineServices + { + /// + /// Initial version with LogsMessagesOfImportance() and IsTaskInputLoggingEnabled as the only exposed members. + /// + public const int Version1 = 1; + + /// + /// An explicit version of this class. Must be incremented whenever new members are added. Derived classes should override + /// the property to return the version actually being implemented. + /// + public virtual int Version => Version1; // Not updated since we have not shipped 17.0 yet + + /// + /// Returns if the given message importance is not guaranteed to be ignored by registered loggers. + /// + /// The importance to check. + /// True if messages of the given importance should be logged, false if it's guaranteed that such messages would be ignored. + /// + /// Example: If we know that no logger is interested in , this method returns + /// for and , and returns + /// for . + /// + public virtual bool LogsMessagesOfImportance(MessageImportance importance) => throw new NotImplementedException(); + + /// + /// Returns if the build is configured to log all task inputs. + /// + /// + /// This is a performance optimization allowing tasks to skip expensive double-logging. + /// + public virtual bool IsTaskInputLoggingEnabled => throw new NotImplementedException(); + } +} diff --git a/src/Framework/Event args classes.cd b/src/Framework/Event args classes.cd deleted file mode 100644 index 705d0aa3b6f..00000000000 --- a/src/Framework/Event args classes.cd +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - BuildErrorEventArgs.cs - AAAAAAAAgAAAAAAAAAAoAAAArAAEAAgAFAAAABAABAA= - - - - - - BuildEventArgs.cs - AAAAAAAAAAAgAAAAAAAGAAAABgAAAAAAACAAICAgACA= - - - - - - BuildFinishedEventArgs.cs - AAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIA= - - - - - - BuildMessageEventArgs.cs - AAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAAAAAAAAAA= - - - - - - BuildStartedEventArgs.cs - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - - - - - - BuildStatusEventArgs.cs - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - - - - - - BuildWarningEventArgs.cs - AAAAAAAAgAAAAAAAAAAoAAAArAAEAAgAFAAAABAABAA= - - - - - - CustomBuildEventArgs.cs - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= - - - - - - ProjectFinishedEventArgs.cs - AAAAgAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAIA= - - - - - - ProjectStartedEventArgs.cs - AAAAABAAABAAAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAA= - - - - - - TargetFinishedEventArgs.cs - AAAAoAAAADAAAAAQAAAAAAAAAAAAAAAgAAAAIAAAAIA= - - - - - - TargetStartedEventArgs.cs - AAAAIAAAADAAAAAQAAAAAAAAAAAAAAAgAAAAIAAAAAA= - - - - - - TaskFinishedEventArgs.cs - AAAAggAAABIEAAAQBAAAAAAAAAAAAAAAAAAAAAAAAIA= - - - - - - TaskStartedEventArgs.cs - AAAAAgAAABIEAAAQBAAAAAAAAAAAAAAAAAAAAAAAAAA= - - - \ No newline at end of file diff --git a/src/Framework/IBuildEngine10.cs b/src/Framework/IBuildEngine10.cs new file mode 100644 index 00000000000..7a7805d9791 --- /dev/null +++ b/src/Framework/IBuildEngine10.cs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Build.Framework +{ + /// + /// This interface extends to provide a reference to the class. + /// Future engine API should be added to the class as opposed to introducing yet another version of the IBuildEngine interface. + /// + public interface IBuildEngine10 : IBuildEngine9 + { + /// + /// Returns the new build engine interface. + /// + EngineServices EngineServices { get; } + } +} diff --git a/src/Framework/MSBuildEventSource.cs b/src/Framework/MSBuildEventSource.cs index 5ba9fa07883..3581225789d 100644 --- a/src/Framework/MSBuildEventSource.cs +++ b/src/Framework/MSBuildEventSource.cs @@ -103,106 +103,100 @@ public void EvaluateConditionStop(string condition, bool result) /// /// Call this method to notify listeners of how the project data was evaluated. /// - /// Relevant information about where in the run of the progam it is. - [Event(11, Keywords = Keywords.All)] + /// Filename of the project being evaluated. + [Event(11, Keywords = Keywords.All | Keywords.PerformanceLog)] public void EvaluateStart(string projectFile) { WriteEvent(11, projectFile); } - /// Relevant information about where in the run of the progam it is. - [Event(12, Keywords = Keywords.All)] - public void EvaluatePass0Start(string projectFile) + /// Filename of the project being evaluated. + [Event(12, Keywords = Keywords.All | Keywords.PerformanceLog)] + public void EvaluateStop(string projectFile) { WriteEvent(12, projectFile); } - /// Relevant information about where in the run of the progam it is. + /// Filename of the project being evaluated. [Event(13, Keywords = Keywords.All)] - public void EvaluatePass0Stop(string projectFile) + public void EvaluatePass0Start(string projectFile) { WriteEvent(13, projectFile); } - /// Relevant information about where in the run of the progam it is. + /// Filename of the project being evaluated. [Event(14, Keywords = Keywords.All)] - public void EvaluatePass1Start(string projectFile) + public void EvaluatePass0Stop(string projectFile) { WriteEvent(14, projectFile); } - /// Relevant information about where in the run of the progam it is. - /// Number of Properties getting evaluated. - /// Number of Imports getting evaluated. + /// Filename of the project being evaluated. [Event(15, Keywords = Keywords.All)] - public void EvaluatePass1Stop(string projectFile, int numberOfProperties, int numberOfImports) + public void EvaluatePass1Start(string projectFile) { - WriteEvent(15, projectFile, numberOfProperties, numberOfImports); + WriteEvent(15, projectFile); } - /// Relevant information about where in the run of the progam it is. + /// Filename of the project being evaluated. [Event(16, Keywords = Keywords.All)] - public void EvaluatePass2Start(string projectFile) + public void EvaluatePass1Stop(string projectFile) { WriteEvent(16, projectFile); } - /// Relevant information about where in the run of the progam it is. - /// Number of ItemDefinitionGroupElements getting evaluated. + /// Filename of the project being evaluated. [Event(17, Keywords = Keywords.All)] - public void EvaluatePass2Stop(string projectFile, int numberOfItemDefinitionGroupElements) + public void EvaluatePass2Start(string projectFile) { - WriteEvent(17, projectFile, numberOfItemDefinitionGroupElements); + WriteEvent(17, projectFile); } - /// Relevant information about where in the run of the progam it is. + /// Filename of the project being evaluated. [Event(18, Keywords = Keywords.All)] - public void EvaluatePass3Start(string projectFile) + public void EvaluatePass2Stop(string projectFile) { WriteEvent(18, projectFile); } - /// Relevant information about where in the run of the progam it is. - /// Number of project items evaluated. + /// Filename of the project being evaluated. [Event(19, Keywords = Keywords.All)] - public void EvaluatePass3Stop(string projectFile, int numberOfItemGroupElements) + public void EvaluatePass3Start(string projectFile) { - WriteEvent(19, projectFile, numberOfItemGroupElements); + WriteEvent(19, projectFile); } - /// Relevant information about where in the run of the progam it is. + /// Filename of the project being evaluated. [Event(20, Keywords = Keywords.All)] - public void EvaluatePass4Start(string projectFile) + public void EvaluatePass3Stop(string projectFile) { WriteEvent(20, projectFile); } - /// Relevant information about where in the run of the progam it is. - /// Number of using tasks elements evaluated. + /// Filename of the project being evaluated. [Event(21, Keywords = Keywords.All)] - public void EvaluatePass4Stop(string projectFile, int numberOfUsingTaskElements) + public void EvaluatePass4Start(string projectFile) { - WriteEvent(21, projectFile, numberOfUsingTaskElements); + WriteEvent(21, projectFile); } - /// Relevant information about where in the run of the progam it is. + /// Filename of the project being evaluated. [Event(22, Keywords = Keywords.All)] - public void EvaluatePass5Start(string projectFile) + public void EvaluatePass4Stop(string projectFile) { WriteEvent(22, projectFile); } - /// Relevant information about where in the run of the progam it is. - /// Number of targets read. + /// Filename of the project being evaluated. [Event(23, Keywords = Keywords.All)] - public void EvaluatePass5Stop(string projectFile, int targetElementsCount) + public void EvaluatePass5Start(string projectFile) { - WriteEvent(23, projectFile, targetElementsCount); + WriteEvent(23, projectFile); } - /// Relevant information about where in the run of the progam it is. + /// Filename of the project being evaluated. [Event(24, Keywords = Keywords.All)] - public void EvaluateStop(string projectFile) + public void EvaluatePass5Stop(string projectFile) { WriteEvent(24, projectFile); } @@ -226,9 +220,9 @@ public void RarOverallStart() } [Event(28, Keywords = Keywords.All | Keywords.PerformanceLog)] - public void RarOverallStop() + public void RarOverallStop(int assembliesCount, int assemblyFilesCount, int resolvedFilesCount, int resolvedDependencyFilesCount, int copyLocalFilesCount, bool findDependencies) { - WriteEvent(28); + WriteEvent(28, assembliesCount, assemblyFilesCount, resolvedFilesCount, resolvedDependencyFilesCount, copyLocalFilesCount, findDependencies); } /// @@ -284,15 +278,15 @@ public void RarLogResultsStop() /// /// Call this method to notify listeners of profiling for the function that parses an XML document into a ProjectRootElement. /// - /// Relevant information about where in the run of the progam it is. - [Event(33, Keywords = Keywords.All | Keywords.PerformanceLog)] + /// Filename of the project being evaluated. + [Event(33, Keywords = Keywords.All)] public void ParseStart(string projectFileName) { WriteEvent(33, projectFileName); } - /// Relevant information about where in the run of the progam it is. - [Event(34, Keywords = Keywords.All | Keywords.PerformanceLog)] + /// Filename of the project being evaluated. + [Event(34, Keywords = Keywords.All)] public void ParseStop(string projectFileName) { WriteEvent(34, projectFileName); @@ -423,6 +417,43 @@ public void PacketReadSize(int size) { WriteEvent(55, size); } + + [Event(56, Keywords = Keywords.All)] + public void TargetUpToDateStart() + { + WriteEvent(56); + } + + [Event(57, Keywords = Keywords.All)] + public void TargetUpToDateStop(int result) + { + WriteEvent(57, result); + } + + [Event(58, Keywords = Keywords.All)] + public void CopyUpToDateStart(string path) + { + WriteEvent(58, path); + } + + [Event(59, Keywords = Keywords.All)] + public void CopyUpToDateStop(string path, bool wasUpToDate) + { + WriteEvent(59, path, wasUpToDate); + } + + [Event(60, Keywords = Keywords.All)] + public void WriteLinesToFileUpToDateStart() + { + WriteEvent(60); + } + + [Event(61, Keywords = Keywords.All)] + public void WriteLinesToFileUpToDateStop(string fileItemSpec, bool wasUpToDate) + { + WriteEvent(61, fileItemSpec, wasUpToDate); + } + #endregion } } diff --git a/src/Framework/Microsoft.Build.Framework.csproj b/src/Framework/Microsoft.Build.Framework.csproj index 939d7db72f7..d2b221eb376 100644 --- a/src/Framework/Microsoft.Build.Framework.csproj +++ b/src/Framework/Microsoft.Build.Framework.csproj @@ -1,14 +1,27 @@  $(LibraryTargetFrameworks) + true true true true This package contains the $(MSBuildProjectName) assembly which is a common assembly used by other MSBuild assemblies. false - partial + full + + + $(NoWarn),CS8632 + + + + diff --git a/src/Framework/NullableAttributes.cs b/src/Framework/NullableAttributes.cs new file mode 100644 index 00000000000..826ecacee10 --- /dev/null +++ b/src/Framework/NullableAttributes.cs @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +// This was copied from https://github.com/dotnet/runtime/blob/39b9607807f29e48cae4652cd74735182b31182e/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs +// and updated to have the scope of the attributes be internal. + +namespace System.Diagnostics.CodeAnalysis +{ +#if !NETCOREAPP + + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class DisallowNullAttribute : Attribute { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute { } + + /// Specifies that an output will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class NotNullAttribute : Attribute { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } + +#endif + +#if !NETCOREAPP || NETCOREAPP3_1 + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullAttribute : Attribute + { + /// Initializes the attribute with a field or property member. + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullAttribute(string member) => Members = new[] { member }; + + /// Initializes the attribute with the list of field and property members. + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullAttribute(params string[] members) => Members = members; + + /// Gets field or property member names. + public string[] Members { get; } + } + + /// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. + [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] + internal sealed class MemberNotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition and a field or property member. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The field or property member that is promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, string member) + { + ReturnValue = returnValue; + Members = new[] { member }; + } + + /// Initializes the attribute with the specified return value condition and list of field and property members. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + /// + /// The list of field and property members that are promised to be not-null. + /// + public MemberNotNullWhenAttribute(bool returnValue, params string[] members) + { + ReturnValue = returnValue; + Members = members; + } + + /// Gets the return value condition. + public bool ReturnValue { get; } + + /// Gets field or property member names. + public string[] Members { get; } + } + +#endif +} diff --git a/src/Framework/Properties/AssemblyInfo.cs b/src/Framework/Properties/AssemblyInfo.cs index cb201c360de..5ab3c12ac9a 100644 --- a/src/Framework/Properties/AssemblyInfo.cs +++ b/src/Framework/Properties/AssemblyInfo.cs @@ -61,14 +61,7 @@ // so that we don't run into known security issues with loading libraries from unsafe locations [assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] -// Needed for the "hub-and-spoke model to locate and retrieve localized resources": https://msdn.microsoft.com/en-us/library/21a15yht(v=vs.110).aspx -// We want "en" to require a satellite assembly for debug builds in order to flush out localization -// issues, but we want release builds to work without it. Also, .net core does not have resource fallbacks -#if (DEBUG && !RUNTIME_TYPE_NETCORE) -[assembly: NeutralResourcesLanguage("en", UltimateResourceFallbackLocation.Satellite)] -#else [assembly: NeutralResourcesLanguage("en")] -#endif [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] diff --git a/src/Framework/Sdk/SdkResolver.cs b/src/Framework/Sdk/SdkResolver.cs index 9aafb5a84ff..9280c30af24 100644 --- a/src/Framework/Sdk/SdkResolver.cs +++ b/src/Framework/Sdk/SdkResolver.cs @@ -29,10 +29,11 @@ public abstract class SdkResolver /// An containing the resolved SDKs or associated error / reason /// the SDK could not be resolved. Return null if the resolver is not /// applicable for a particular . - /// - /// Note: You must use the to return a result. - /// - /// + /// + /// + /// Note: You must use to return a result. + /// + /// public abstract SdkResult Resolve(SdkReference sdkReference, SdkResolverContext resolverContext, SdkResultFactory factory); } diff --git a/src/Framework/Sdk/SdkResolverContext.cs b/src/Framework/Sdk/SdkResolverContext.cs index df8c5f3f148..63e2f48482d 100644 --- a/src/Framework/Sdk/SdkResolverContext.cs +++ b/src/Framework/Sdk/SdkResolverContext.cs @@ -37,11 +37,11 @@ public abstract class SdkResolverContext /// /// Version of MSBuild currently running. + /// /// /// File version based on commit height from our public git repository. This is informational /// and not equal to the assembly version. /// - /// public virtual Version MSBuildVersion { get; protected set; } /// diff --git a/src/Framework/Sdk/SdkResult.cs b/src/Framework/Sdk/SdkResult.cs index 12cf5db0eac..29c201f24b8 100644 --- a/src/Framework/Sdk/SdkResult.cs +++ b/src/Framework/Sdk/SdkResult.cs @@ -7,11 +7,10 @@ namespace Microsoft.Build.Framework { /// /// An abstract interface class to indicate SDK resolver success or failure. - /// - /// Note: Use to create instances of this class. Do not - /// inherit from this class. - /// /// + /// + /// Note: Use to create instances of this class. Do not inherit from this class. + /// public abstract class SdkResult { // Explicit backing fields so that implementation in Microsoft.Build.dll can use them for translation diff --git a/src/Framework/TargetSkippedEventArgs.cs b/src/Framework/TargetSkippedEventArgs.cs index 5bad19a7251..d34e468ff32 100644 --- a/src/Framework/TargetSkippedEventArgs.cs +++ b/src/Framework/TargetSkippedEventArgs.cs @@ -7,6 +7,37 @@ namespace Microsoft.Build.Framework { + /// + /// A reason why a target was skipped. + /// + public enum TargetSkipReason + { + /// + /// The target was not skipped or the skip reason was unknown. + /// + None, + + /// + /// The target previously built successfully. + /// + PreviouslyBuiltSuccessfully, + + /// + /// The target previously built unsuccessfully. + /// + PreviouslyBuiltUnsuccessfully, + + /// + /// All the target outputs were up-to-date with respect to their inputs. + /// + OutputsUpToDate, + + /// + /// The condition on the target was evaluated as false. + /// + ConditionWasFalse + } + /// /// Arguments for the target skipped event. /// @@ -45,6 +76,11 @@ params object[] messageArgs { } + /// + /// The reason why the target was skipped. + /// + public TargetSkipReason SkipReason { get; set; } + /// /// Gets or sets the name of the target being skipped. /// @@ -65,10 +101,24 @@ params object[] messageArgs /// public TargetBuiltReason BuildReason { get; set; } + /// + /// Whether the target succeeded originally. + /// public bool OriginallySucceeded { get; set; } + /// + /// describing the original build of the target, or null if not available. + /// + public BuildEventContext OriginalBuildEventContext { get; set; } + + /// + /// The condition expression on the target declaration. + /// public string Condition { get; set; } + /// + /// The value of the condition expression as it was evaluated. + /// public string EvaluatedCondition { get; set; } internal override void WriteToStream(BinaryWriter writer) @@ -81,7 +131,9 @@ internal override void WriteToStream(BinaryWriter writer) writer.WriteOptionalString(Condition); writer.WriteOptionalString(EvaluatedCondition); writer.Write7BitEncodedInt((int)BuildReason); + writer.Write7BitEncodedInt((int)SkipReason); writer.Write(OriginallySucceeded); + writer.WriteOptionalBuildEventContext(OriginalBuildEventContext); } internal override void CreateFromStream(BinaryReader reader, int version) @@ -94,7 +146,9 @@ internal override void CreateFromStream(BinaryReader reader, int version) Condition = reader.ReadOptionalString(); EvaluatedCondition = reader.ReadOptionalString(); BuildReason = (TargetBuiltReason)reader.Read7BitEncodedInt(); + SkipReason = (TargetSkipReason)reader.Read7BitEncodedInt(); OriginallySucceeded = reader.ReadBoolean(); + OriginalBuildEventContext = reader.ReadOptionalBuildEventContext(); } public override string Message @@ -107,22 +161,29 @@ public override string Message { if (RawMessage == null) { - if (Condition != null) - { - RawMessage = FormatResourceStringIgnoreCodeAndKeyword( - "TargetSkippedFalseCondition", - TargetName, - Condition, - EvaluatedCondition); - } - else + RawMessage = SkipReason switch { - RawMessage = FormatResourceStringIgnoreCodeAndKeyword( - OriginallySucceeded - ? "TargetAlreadyCompleteSuccess" - : "TargetAlreadyCompleteFailure", - TargetName); - } + TargetSkipReason.PreviouslyBuiltSuccessfully or TargetSkipReason.PreviouslyBuiltUnsuccessfully => + FormatResourceStringIgnoreCodeAndKeyword( + OriginallySucceeded + ? "TargetAlreadyCompleteSuccess" + : "TargetAlreadyCompleteFailure", + TargetName), + + TargetSkipReason.ConditionWasFalse => + FormatResourceStringIgnoreCodeAndKeyword( + "TargetSkippedFalseCondition", + TargetName, + Condition, + EvaluatedCondition), + + TargetSkipReason.OutputsUpToDate => + FormatResourceStringIgnoreCodeAndKeyword( + "SkipTargetBecauseOutputsUpToDate", + TargetName), + + _ => SkipReason.ToString() + }; } } } diff --git a/src/Framework/TaskParameterEventArgs.cs b/src/Framework/TaskParameterEventArgs.cs index b4efc953e84..1ca1a55f7ad 100644 --- a/src/Framework/TaskParameterEventArgs.cs +++ b/src/Framework/TaskParameterEventArgs.cs @@ -87,6 +87,8 @@ internal override void CreateFromStream(BinaryReader reader, int version) BuildEventContext = reader.ReadOptionalBuildEventContext(); Kind = (TaskParameterMessageKind)reader.Read7BitEncodedInt(); ItemType = reader.ReadOptionalString(); + LineNumber = reader.Read7BitEncodedInt(); + ColumnNumber = reader.Read7BitEncodedInt(); Items = ReadItems(reader); } @@ -134,6 +136,8 @@ internal override void WriteToStream(BinaryWriter writer) writer.WriteOptionalBuildEventContext(BuildEventContext); writer.Write7BitEncodedInt((int)Kind); writer.WriteOptionalString(ItemType); + writer.Write7BitEncodedInt(LineNumber); + writer.Write7BitEncodedInt(ColumnNumber); WriteItems(writer, Items); } diff --git a/src/Framework/TaskStartedEventArgs.cs b/src/Framework/TaskStartedEventArgs.cs index ce50a3b7723..207d20472d2 100644 --- a/src/Framework/TaskStartedEventArgs.cs +++ b/src/Framework/TaskStartedEventArgs.cs @@ -91,6 +91,8 @@ internal override void WriteToStream(BinaryWriter writer) writer.WriteOptionalString(taskName); writer.WriteOptionalString(projectFile); writer.WriteOptionalString(taskFile); + writer.Write7BitEncodedInt(LineNumber); + writer.Write7BitEncodedInt(ColumnNumber); } /// @@ -105,6 +107,8 @@ internal override void CreateFromStream(BinaryReader reader, int version) taskName = reader.ReadByte() == 0 ? null : reader.ReadString(); projectFile = reader.ReadByte() == 0 ? null : reader.ReadString(); taskFile = reader.ReadByte() == 0 ? null : reader.ReadString(); + LineNumber = reader.Read7BitEncodedInt(); + ColumnNumber = reader.Read7BitEncodedInt(); } #endregion @@ -123,6 +127,16 @@ internal override void CreateFromStream(BinaryReader reader, int version) /// public string TaskFile => taskFile; + /// + /// Line number of the task invocation in the project file + /// + public int LineNumber { get; internal set; } + + /// + /// Column number of the task invocation in the project file + /// + public int ColumnNumber { get; internal set; } + public override string Message { get diff --git a/src/MSBuild.Bootstrap/MSBuild.Bootstrap.csproj b/src/MSBuild.Bootstrap/MSBuild.Bootstrap.csproj index 87992e4b1f9..bcd44da6e60 100644 --- a/src/MSBuild.Bootstrap/MSBuild.Bootstrap.csproj +++ b/src/MSBuild.Bootstrap/MSBuild.Bootstrap.csproj @@ -11,7 +11,7 @@ - + @@ -65,7 +65,7 @@ - + diff --git a/src/MSBuild.UnitTests/Microsoft.Build.CommandLine.UnitTests.csproj b/src/MSBuild.UnitTests/Microsoft.Build.CommandLine.UnitTests.csproj index be9203b581a..701d0c1e3fc 100644 --- a/src/MSBuild.UnitTests/Microsoft.Build.CommandLine.UnitTests.csproj +++ b/src/MSBuild.UnitTests/Microsoft.Build.CommandLine.UnitTests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/MSBuild.UnitTests/ValidateAssemblyLoadContext.cs b/src/MSBuild.UnitTests/ValidateAssemblyLoadContext.cs index 4cdcab25b0d..8670b4d358e 100644 --- a/src/MSBuild.UnitTests/ValidateAssemblyLoadContext.cs +++ b/src/MSBuild.UnitTests/ValidateAssemblyLoadContext.cs @@ -23,7 +23,6 @@ public override bool Execute() // This probably doesn't need to be how it is forever: https://github.com/microsoft/msbuild/issues/5041 if (thisLoadContext.GetType().FullName == typeof(MSBuildLoadContext).FullName) { -#if NETCOREAPP && !NETCOREAPP2_1 // TODO: enable this functionality when targeting .NET Core 3.0+ if (!thisLoadContext.Name.EndsWith(typeof(ValidateAssemblyLoadContext).Assembly.GetName().Name + ".dll")) { Log.LogError($"Unexpected AssemblyLoadContext name: \"{thisLoadContext.Name}\", but the current executing assembly was {typeof(ValidateAssemblyLoadContext).Assembly.GetName().Name}"); @@ -32,7 +31,6 @@ public override bool Execute() { Log.LogMessage(MessageImportance.High, $"Task {nameof(ValidateAssemblyLoadContext)} loaded in AssemblyLoadContext named {thisLoadContext.Name}"); } -#endif } else { diff --git a/src/MSBuild.UnitTests/XMake_Tests.cs b/src/MSBuild.UnitTests/XMake_Tests.cs index c5809755219..c9dbd905e72 100644 --- a/src/MSBuild.UnitTests/XMake_Tests.cs +++ b/src/MSBuild.UnitTests/XMake_Tests.cs @@ -19,10 +19,11 @@ using Shouldly; using System.IO.Compression; using System.Reflection; +using Microsoft.Build.Utilities; namespace Microsoft.Build.UnitTests { - public class XMakeAppTests + public class XMakeAppTests : IDisposable { #if USE_MSBUILD_DLL_EXTN private const string MSBuildExeName = "MSBuild.dll"; @@ -31,10 +32,12 @@ public class XMakeAppTests #endif private readonly ITestOutputHelper _output; + private readonly TestEnvironment _env; public XMakeAppTests(ITestOutputHelper output) { _output = output; + _env = UnitTests.TestEnvironment.Create(_output); } private const string AutoResponseFileName = "MSBuild.rsp"; @@ -45,7 +48,7 @@ public void GatherCommandLineSwitchesTwoProperties() CommandLineSwitches switches = new CommandLineSwitches(); var arguments = new List(); - arguments.AddRange(new string[] { "/p:a=b", "/p:c=d" }); + arguments.AddRange(new[] { "/p:a=b", "/p:c=d" }); MSBuildApp.GatherCommandLineSwitches(arguments, switches); @@ -60,7 +63,7 @@ public void GatherCommandLineSwitchesMaxCpuCountWithArgument() CommandLineSwitches switches = new CommandLineSwitches(); var arguments = new List(); - arguments.AddRange(new string[] { "/m:2" }); + arguments.AddRange(new[] { "/m:2" }); MSBuildApp.GatherCommandLineSwitches(arguments, switches); @@ -77,7 +80,7 @@ public void GatherCommandLineSwitchesMaxCpuCountWithoutArgument() CommandLineSwitches switches = new CommandLineSwitches(); var arguments = new List(); - arguments.AddRange(new string[] { "/m:3", "/m" }); + arguments.AddRange(new[] { "/m:3", "/m" }); MSBuildApp.GatherCommandLineSwitches(arguments, switches); @@ -97,7 +100,7 @@ public void GatherCommandLineSwitchesMaxCpuCountWithoutArgumentButWithColon() CommandLineSwitches switches = new CommandLineSwitches(); var arguments = new List(); - arguments.AddRange(new string[] { "/m:" }); + arguments.AddRange(new[] { "/m:" }); MSBuildApp.GatherCommandLineSwitches(arguments, switches); @@ -137,11 +140,8 @@ public void GatherCommandLineSwitchesMaxCpuCountWithoutArgumentButWithColon() [Fact] public void SplitUnquotedTest() { - List sa; - int emptySplits; - // nothing quoted - sa = QuotingUtilities.SplitUnquoted("abcdxyz"); + var sa = QuotingUtilities.SplitUnquoted("abcdxyz"); sa.Count.ShouldBe(1); sa[0].ShouldBe("abcdxyz"); @@ -165,7 +165,7 @@ public void SplitUnquotedTest() sa[2].ShouldBe("dxyz"); // nothing quoted - sa = QuotingUtilities.SplitUnquoted("abc,c;dxyz", 2, false, false, out emptySplits, ';', ','); + sa = QuotingUtilities.SplitUnquoted("abc,c;dxyz", 2, false, false, out var emptySplits, ';', ','); emptySplits.ShouldBe(0); sa.Count.ShouldBe(2); sa[0].ShouldBe("abc"); @@ -183,8 +183,8 @@ public void SplitUnquotedTest() emptySplits.ShouldBe(0); sa.Count.ShouldBe(4); sa[0].ShouldBe("abc"); - sa[1].ShouldBe(String.Empty); - sa[2].ShouldBe(String.Empty); + sa[1].ShouldBe(string.Empty); + sa[2].ShouldBe(string.Empty); sa[3].ShouldBe("dxyz"); // "c d" is quoted @@ -330,10 +330,8 @@ public void SplitUnquotedTest() [Fact] public void UnquoteTest() { - int doubleQuotesRemoved; - // "cde" is quoted - QuotingUtilities.Unquote("abc\"cde\"xyz", out doubleQuotesRemoved).ShouldBe("abccdexyz"); + QuotingUtilities.Unquote("abc\"cde\"xyz", out var doubleQuotesRemoved).ShouldBe("abccdexyz"); doubleQuotesRemoved.ShouldBe(2); // "xyz" is quoted (the terminal double-quote is assumed) @@ -393,8 +391,7 @@ public void UnquoteTest() public void ExtractSwitchParametersTest() { string commandLineArg = "\"/p:foo=\"bar"; - int doubleQuotesRemovedFromArg; - string unquotedCommandLineArg = QuotingUtilities.Unquote(commandLineArg, out doubleQuotesRemovedFromArg); + string unquotedCommandLineArg = QuotingUtilities.Unquote(commandLineArg, out var doubleQuotesRemovedFromArg); MSBuildApp.ExtractSwitchParameters(commandLineArg, unquotedCommandLineArg, doubleQuotesRemovedFromArg, "p", unquotedCommandLineArg.IndexOf(':')).ShouldBe(":\"foo=\"bar"); doubleQuotesRemovedFromArg.ShouldBe(2); @@ -513,15 +510,15 @@ public void InvalidVerbosity() [Fact] public void ValidMaxCPUCountSwitch() { - MSBuildApp.ProcessMaxCPUCountSwitch(new string[] { "1" }).ShouldBe(1); - MSBuildApp.ProcessMaxCPUCountSwitch(new string[] { "2" }).ShouldBe(2); - MSBuildApp.ProcessMaxCPUCountSwitch(new string[] { "3" }).ShouldBe(3); - MSBuildApp.ProcessMaxCPUCountSwitch(new string[] { "4" }).ShouldBe(4); - MSBuildApp.ProcessMaxCPUCountSwitch(new string[] { "8" }).ShouldBe(8); - MSBuildApp.ProcessMaxCPUCountSwitch(new string[] { "63" }).ShouldBe(63); + MSBuildApp.ProcessMaxCPUCountSwitch(new[] { "1" }).ShouldBe(1); + MSBuildApp.ProcessMaxCPUCountSwitch(new[] { "2" }).ShouldBe(2); + MSBuildApp.ProcessMaxCPUCountSwitch(new[] { "3" }).ShouldBe(3); + MSBuildApp.ProcessMaxCPUCountSwitch(new[] { "4" }).ShouldBe(4); + MSBuildApp.ProcessMaxCPUCountSwitch(new[] { "8" }).ShouldBe(8); + MSBuildApp.ProcessMaxCPUCountSwitch(new[] { "63" }).ShouldBe(63); // Should pick last value - MSBuildApp.ProcessMaxCPUCountSwitch(new string[] { "8", "4" }).ShouldBe(4); + MSBuildApp.ProcessMaxCPUCountSwitch(new[] { "8", "4" }).ShouldBe(4); } [Fact] @@ -529,7 +526,7 @@ public void InvalidMaxCPUCountSwitch1() { Should.Throw(() => { - MSBuildApp.ProcessMaxCPUCountSwitch(new string[] { "-1" }); + MSBuildApp.ProcessMaxCPUCountSwitch(new[] { "-1" }); } ); } @@ -539,7 +536,7 @@ public void InvalidMaxCPUCountSwitch2() { Should.Throw(() => { - MSBuildApp.ProcessMaxCPUCountSwitch(new string[] { "0" }); + MSBuildApp.ProcessMaxCPUCountSwitch(new[] { "0" }); } ); } @@ -550,7 +547,7 @@ public void InvalidMaxCPUCountSwitch3() Should.Throw(() => { // Too big - MSBuildApp.ProcessMaxCPUCountSwitch(new string[] { "foo" }); + MSBuildApp.ProcessMaxCPUCountSwitch(new[] { "foo" }); } ); } @@ -560,7 +557,7 @@ public void InvalidMaxCPUCountSwitch4() { Should.Throw(() => { - MSBuildApp.ProcessMaxCPUCountSwitch(new string[] { "1025" }); + MSBuildApp.ProcessMaxCPUCountSwitch(new[] { "1025" }); } ); } @@ -586,7 +583,7 @@ public void SetConsoleUICulture() MSBuildApp.SetConsoleUI(); // Make sure this doesn't throw an exception. - string bar = String.Format(CultureInfo.CurrentUICulture, "{0}", 1); + string bar = string.Format(CultureInfo.CurrentUICulture, "{0}", 1); // Restore the current UI culture back to the way it was at the beginning of this unit test. thisThread.CurrentUICulture = originalUICulture; @@ -647,20 +644,19 @@ public void ConfigurationInvalid() var msbuildParameters = "\"" + pathToProjectFile + "\""; - bool successfulExit; - output = RunnerUtilities.ExecMSBuild(newPathToMSBuildExe, msbuildParameters, out successfulExit); + output = RunnerUtilities.ExecMSBuild(newPathToMSBuildExe, msbuildParameters, out var successfulExit); successfulExit.ShouldBeFalse(); } catch (Exception ex) { - Console.WriteLine(ex.ToString()); + _output.WriteLine(ex.ToString()); throw; } finally { if (output != null) { - Console.WriteLine(output); + _output.WriteLine(output); } try @@ -800,7 +796,7 @@ public void MSBuildEngineLogger() } } - private string _pathToArbitraryBogusFile = NativeMethodsShared.IsWindows // OK on 64 bit as well + private readonly string _pathToArbitraryBogusFile = NativeMethodsShared.IsWindows // OK on 64 bit as well ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "notepad.exe") : "/bin/cat"; @@ -813,8 +809,7 @@ public void GetCommandLine() var msbuildParameters = "\"" + _pathToArbitraryBogusFile + "\"" + (NativeMethodsShared.IsWindows ? " /v:diag" : " -v:diag"); File.Exists(_pathToArbitraryBogusFile).ShouldBeTrue(); - bool successfulExit; - string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out successfulExit); + string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out var successfulExit); successfulExit.ShouldBeFalse(); output.ShouldContain(RunnerUtilities.PathToCurrentlyRunningMsBuildExe + (NativeMethodsShared.IsWindows ? " /v:diag " : " -v:diag ") + _pathToArbitraryBogusFile, Case.Insensitive); @@ -829,7 +824,6 @@ public void GetCommandLineQuotedExe() var msbuildParameters = "\"" + _pathToArbitraryBogusFile + "\"" + (NativeMethodsShared.IsWindows ? " /v:diag" : " -v:diag"); File.Exists(_pathToArbitraryBogusFile).ShouldBeTrue(); - bool successfulExit; string pathToMSBuildExe = RunnerUtilities.PathToCurrentlyRunningMsBuildExe; // This @pathToMSBuildExe is used directly with Process, so don't quote it on // Unix @@ -838,7 +832,7 @@ public void GetCommandLineQuotedExe() pathToMSBuildExe = "\"" + pathToMSBuildExe + "\""; } - string output = RunnerUtilities.ExecMSBuild(pathToMSBuildExe, msbuildParameters, out successfulExit); + string output = RunnerUtilities.ExecMSBuild(pathToMSBuildExe, msbuildParameters, out var successfulExit); successfulExit.ShouldBeFalse(); output.ShouldContain(RunnerUtilities.PathToCurrentlyRunningMsBuildExe + (NativeMethodsShared.IsWindows ? " /v:diag " : " -v:diag ") + _pathToArbitraryBogusFile, Case.Insensitive); @@ -850,7 +844,7 @@ public void GetCommandLineQuotedExe() [Fact] public void GetCommandLineQuotedExeOnPath() { - string output = null; + string output; string current = Directory.GetCurrentDirectory(); try @@ -859,8 +853,7 @@ public void GetCommandLineQuotedExeOnPath() var msbuildParameters = "\"" + _pathToArbitraryBogusFile + "\"" + (NativeMethodsShared.IsWindows ? " /v:diag" : " -v:diag"); - bool successfulExit; - output = RunnerUtilities.ExecMSBuild(msbuildParameters, out successfulExit); + output = RunnerUtilities.ExecMSBuild(msbuildParameters, out var successfulExit); successfulExit.ShouldBeFalse(); } finally @@ -878,38 +871,23 @@ public void GetCommandLineQuotedExeOnPath() [Fact] public void ResponseFileInProjectDirectoryFoundImplicitly() { - string directory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N")); + string directory = _env.DefaultTestDirectory.Path; string projectPath = Path.Combine(directory, "my.proj"); string rspPath = Path.Combine(directory, AutoResponseFileName); - string currentDirectory = Directory.GetCurrentDirectory(); + string content = ObjectModelHelpers.CleanupFileContents(""); + File.WriteAllText(projectPath, content); - try - { - Directory.CreateDirectory(directory); - - string content = ObjectModelHelpers.CleanupFileContents(""); - File.WriteAllText(projectPath, content); - - string rspContent = "/p:A=1"; - File.WriteAllText(rspPath, rspContent); + string rspContent = "/p:A=1"; + File.WriteAllText(rspPath, rspContent); - // Find the project in the current directory - Directory.SetCurrentDirectory(directory); + // Find the project in the current directory + _env.SetCurrentDirectory(directory); - bool successfulExit; - string output = RunnerUtilities.ExecMSBuild(String.Empty, out successfulExit); - successfulExit.ShouldBeTrue(); + string output = RunnerUtilities.ExecMSBuild(string.Empty, out var successfulExit); + successfulExit.ShouldBeTrue(); - output.ShouldContain("[A=1]"); - } - finally - { - Directory.SetCurrentDirectory(currentDirectory); - File.Delete(projectPath); - File.Delete(rspPath); - FileUtilities.DeleteWithoutTrailingBackslash(directory); - } + output.ShouldContain("[A=1]"); } /// @@ -935,8 +913,7 @@ public void ResponseFileInProjectDirectoryExplicit() var msbuildParameters = "\"" + projectPath + "\""; - bool successfulExit; - string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out successfulExit); + string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out var successfulExit); successfulExit.ShouldBeTrue(); output.ShouldContain("[A=1]"); @@ -971,8 +948,7 @@ public void ResponseFileInProjectDirectoryRandomName() var msbuildParameters = "\"" + projectPath + "\""; - bool successfulExit; - string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out successfulExit); + string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out var successfulExit); successfulExit.ShouldBeTrue(); output.ShouldContain("[A=]"); @@ -1008,8 +984,7 @@ public void ResponseFileInProjectDirectoryCommandLineWins() var msbuildParameters = "\"" + projectPath + "\"" + " /p:A=2"; - bool successfulExit; - string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out successfulExit); + string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out var successfulExit); successfulExit.ShouldBeTrue(); output.ShouldContain("[A=2]"); @@ -1054,8 +1029,7 @@ public void ResponseFileInProjectDirectoryWinsOverMainMSBuildRsp() var msbuildParameters = "\"" + projectPath + "\""; - bool successfulExit; - string output = RunnerUtilities.ExecMSBuild(exePath, msbuildParameters, out successfulExit); + string output = RunnerUtilities.ExecMSBuild(exePath, msbuildParameters, out var successfulExit); successfulExit.ShouldBeTrue(); output.ShouldContain("[A=1]"); @@ -1090,8 +1064,7 @@ public void ProjectDirectoryIsMSBuildExeDirectory() var msbuildParameters = "\"" + projectPath + "\""; - bool successfulExit; - string output = RunnerUtilities.ExecMSBuild(exePath, msbuildParameters, out successfulExit); + string output = RunnerUtilities.ExecMSBuild(exePath, msbuildParameters, out var successfulExit); successfulExit.ShouldBeTrue(); output.ShouldContain("[A=1]"); @@ -1124,8 +1097,7 @@ public void ResponseFileInProjectDirectoryItselfWithNoAutoResponseSwitch() var msbuildParameters = "\"" + projectPath + "\""; - bool successfulExit; - string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out successfulExit); + string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out var successfulExit); successfulExit.ShouldBeFalse(); output.ShouldContain("MSB1027"); // msbuild.rsp cannot have /noautoresponse in it @@ -1160,8 +1132,7 @@ public void ResponseFileInProjectDirectoryButCommandLineNoAutoResponseSwitch() var msbuildParameters = "\"" + projectPath + "\" /noautoresponse"; - bool successfulExit; - string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out successfulExit); + string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out var successfulExit); successfulExit.ShouldBeTrue(); output.ShouldContain("[A=]"); @@ -1193,8 +1164,7 @@ public void ResponseFileInProjectDirectoryNullCase() var msbuildParameters = "\"" + projectPath + "\""; - bool successfulExit; - string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out successfulExit); + string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out var successfulExit); successfulExit.ShouldBeTrue(); output.ShouldContain("[A=]"); @@ -1213,22 +1183,19 @@ public void ResponseFileInProjectDirectoryNullCase() [Fact] public void ResponseFileSupportsThisFileDirectory() { - using (var env = UnitTests.TestEnvironment.Create()) - { - var content = ObjectModelHelpers.CleanupFileContents( - ""); + var content = ObjectModelHelpers.CleanupFileContents( + ""); - var directory = env.CreateFolder(); - directory.CreateFile("Directory.Build.rsp", "/p:A=%MSBuildThisFileDirectory%"); - var projectPath = directory.CreateFile("my.proj", content).Path; + var directory = _env.CreateFolder(); + directory.CreateFile("Directory.Build.rsp", "/p:A=%MSBuildThisFileDirectory%"); + var projectPath = directory.CreateFile("my.proj", content).Path; - var msbuildParameters = "\"" + projectPath + "\""; + var msbuildParameters = "\"" + projectPath + "\""; - string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out var successfulExit); - successfulExit.ShouldBeTrue(); + string output = RunnerUtilities.ExecMSBuild(msbuildParameters, out var successfulExit); + successfulExit.ShouldBeTrue(); - output.ShouldContain($"[A={directory.Path}{Path.DirectorySeparatorChar}]"); - } + output.ShouldContain($"[A={directory.Path}{Path.DirectorySeparatorChar}]"); } /// @@ -1254,7 +1221,7 @@ public void NormalPriorityBuild() private void RunPriorityBuildTest(ProcessPriorityClass expectedPrority, params string[] arguments) { - string[] aggregateArguments = arguments.Union(new string[] { " /nr:false /v:diag "}).ToArray(); + string[] aggregateArguments = arguments.Union(new[] { " /nr:false /v:diag "}).ToArray(); string contents = ObjectModelHelpers.CleanupFileContents(@" @@ -1275,7 +1242,7 @@ private void RunPriorityBuildTest(ProcessPriorityClass expectedPrority, params s string logContents = ExecuteMSBuildExeExpectSuccess(contents, envsToCreate: environmentVars, arguments: aggregateArguments); - string expected = string.Format(@"Task priority is '{0}'", expectedPrority); + string expected = $@"Task priority is '{expectedPrority}'"; logContents.ShouldContain(expected, () => logContents); } @@ -1283,11 +1250,11 @@ private void RunPriorityBuildTest(ProcessPriorityClass expectedPrority, params s /// Test the default file to build in cases involving at least one solution filter file. /// [Theory] - [InlineData(new string[] { "my.proj", "my.sln", "my.slnf" }, "my.sln")] - [InlineData(new string[] { "abc.proj", "bcd.csproj", "slnf.slnf", "other.slnf" }, "abc.proj")] - [InlineData(new string[] { "abc.sln", "slnf.slnf", "abc.slnf" }, "abc.sln")] - [InlineData(new string[] { "abc.csproj", "abc.slnf", "not.slnf" }, "abc.csproj")] - [InlineData(new string[] { "abc.slnf" }, "abc.slnf")] + [InlineData(new[] { "my.proj", "my.sln", "my.slnf" }, "my.sln")] + [InlineData(new[] { "abc.proj", "bcd.csproj", "slnf.slnf", "other.slnf" }, "abc.proj")] + [InlineData(new[] { "abc.sln", "slnf.slnf", "abc.slnf" }, "abc.sln")] + [InlineData(new[] { "abc.csproj", "abc.slnf", "not.slnf" }, "abc.csproj")] + [InlineData(new[] { "abc.slnf" }, "abc.slnf")] public void TestDefaultBuildWithSolutionFilter(string[] projects, string answer) { string[] extensionsToIgnore = Array.Empty(); @@ -1304,10 +1271,10 @@ public void TestDefaultBuildWithSolutionFilter(string[] projects, string answer) [Fact] public void TestProcessProjectSwitchOneProjNotFoundExtension() { - string[] projects = new string[] { "my.proj" }; - string[] extensionsToIgnore = new string[] { ".phantomextension" }; + string[] projects = { "my.proj" }; + string[] extensionsToIgnore = { ".phantomextension" }; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" } /// @@ -1316,10 +1283,10 @@ public void TestProcessProjectSwitchOneProjNotFoundExtension() [Fact] public void TestTwoIdenticalExtensionsToIgnore() { - string[] projects = new string[] { "my.proj" }; - string[] extensionsToIgnore = new string[] { ".phantomextension", ".phantomextension" }; + string[] projects = { "my.proj" }; + string[] extensionsToIgnore = { ".phantomextension", ".phantomextension" }; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" } /// @@ -1328,13 +1295,13 @@ public void TestTwoIdenticalExtensionsToIgnore() [Fact] public void TestProcessProjectSwitchNullandEmptyProjectsToIgnore() { - string[] projects = new string[] { "my.proj" }; + string[] projects = { "my.proj" }; string[] extensionsToIgnore = null; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" extensionsToIgnore = new string[] { }; - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" } /// @@ -1345,10 +1312,10 @@ public void TestProcessProjectSwitchNullInList() { Should.Throw(() => { - string[] projects = new string[] { "my.proj" }; - string[] extensionsToIgnore = new string[] { ".phantomextension", null }; + string[] projects = { "my.proj" }; + string[] extensionsToIgnore = { ".phantomextension", null }; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" } ); } @@ -1361,10 +1328,10 @@ public void TestProcessProjectSwitchEmptyInList() { Should.Throw(() => { - string[] projects = new string[] { "my.proj" }; - string[] extensionsToIgnore = new string[] { ".phantomextension", string.Empty }; + string[] projects = { "my.proj" }; + string[] extensionsToIgnore = { ".phantomextension", string.Empty }; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" } ); } @@ -1376,10 +1343,10 @@ public void TestProcessProjectSwitchExtensionWithoutDot() { Should.Throw(() => { - string[] projects = new string[] { "my.proj" }; - string[] extensionsToIgnore = new string[] { "phantomextension" }; + string[] projects = { "my.proj" }; + string[] extensionsToIgnore = { "phantomextension" }; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); } ); } @@ -1391,10 +1358,10 @@ public void TestProcessProjectSwitchMalformed() { Should.Throw(() => { - string[] projects = new string[] { "my.proj" }; - string[] extensionsToIgnore = new string[] { ".C:\\boocatmoo.a" }; + string[] projects = { "my.proj" }; + string[] extensionsToIgnore = { ".C:\\boocatmoo.a" }; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("my.proj", StringCompareShould.IgnoreCase); // "Expected my.proj to be only project found" } ); } @@ -1406,65 +1373,65 @@ public void TestProcessProjectSwitchWildcards() { Should.Throw(() => { - string[] projects = new string[] { "my.proj" }; - string[] extensionsToIgnore = new string[] { ".proj*", ".nativeproj?" }; + string[] projects = { "my.proj" }; + string[] extensionsToIgnore = { ".proj*", ".nativeproj?" }; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles); + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles); } ); } [Fact] public void TestProcessProjectSwitch() { - string[] projects = new string[] { "test.nativeproj", "test.vcproj" }; - string[] extensionsToIgnore = new string[] { ".phantomextension", ".vcproj" }; + string[] projects = { "test.nativeproj", "test.vcproj" }; + string[] extensionsToIgnore = { ".phantomextension", ".vcproj" }; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.nativeproj", StringCompareShould.IgnoreCase); // "Expected test.nativeproj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.nativeproj", StringCompareShould.IgnoreCase); // "Expected test.nativeproj to be only project found" - projects = new string[] { "test.nativeproj", "test.vcproj", "test.proj" }; - extensionsToIgnore = new string[] { ".phantomextension", ".vcproj" }; + projects = new[] { "test.nativeproj", "test.vcproj", "test.proj" }; + extensionsToIgnore = new[] { ".phantomextension", ".vcproj" }; projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.proj", StringCompareShould.IgnoreCase); // "Expected test.proj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.proj", StringCompareShould.IgnoreCase); // "Expected test.proj to be only project found" - projects = new string[] { "test.nativeproj", "test.vcproj" }; - extensionsToIgnore = new string[] { ".vcproj" }; + projects = new[] { "test.nativeproj", "test.vcproj" }; + extensionsToIgnore = new[] { ".vcproj" }; projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.nativeproj", StringCompareShould.IgnoreCase); // "Expected test.nativeproj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.nativeproj", StringCompareShould.IgnoreCase); // "Expected test.nativeproj to be only project found" - projects = new string[] { "test.proj", "test.sln" }; - extensionsToIgnore = new string[] { ".vcproj" }; + projects = new[] { "test.proj", "test.sln" }; + extensionsToIgnore = new[] { ".vcproj" }; projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.sln", StringCompareShould.IgnoreCase); // "Expected test.sln to be only solution found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.sln", StringCompareShould.IgnoreCase); // "Expected test.sln to be only solution found" - projects = new string[] { "test.proj", "test.sln", "test.proj~", "test.sln~" }; + projects = new[] { "test.proj", "test.sln", "test.proj~", "test.sln~" }; extensionsToIgnore = new string[] { }; projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.sln", StringCompareShould.IgnoreCase); // "Expected test.sln to be only solution found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.sln", StringCompareShould.IgnoreCase); // "Expected test.sln to be only solution found" - projects = new string[] { "test.proj" }; + projects = new[] { "test.proj" }; extensionsToIgnore = new string[] { }; projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.proj", StringCompareShould.IgnoreCase); // "Expected test.proj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.proj", StringCompareShould.IgnoreCase); // "Expected test.proj to be only project found" - projects = new string[] { "test.proj", "test.proj~" }; + projects = new[] { "test.proj", "test.proj~" }; extensionsToIgnore = new string[] { }; projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.proj", StringCompareShould.IgnoreCase); // "Expected test.proj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.proj", StringCompareShould.IgnoreCase); // "Expected test.proj to be only project found" - projects = new string[] { "test.sln" }; + projects = new[] { "test.sln" }; extensionsToIgnore = new string[] { }; projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.sln", StringCompareShould.IgnoreCase); // "Expected test.sln to be only solution found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.sln", StringCompareShould.IgnoreCase); // "Expected test.sln to be only solution found" - projects = new string[] { "test.sln", "test.sln~" }; + projects = new[] { "test.sln", "test.sln~" }; extensionsToIgnore = new string[] { }; projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.sln", StringCompareShould.IgnoreCase); // "Expected test.sln to be only solution found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.sln", StringCompareShould.IgnoreCase); // "Expected test.sln to be only solution found" - projects = new string[] { "test.sln~", "test.sln" }; + projects = new[] { "test.sln~", "test.sln" }; extensionsToIgnore = new string[] { }; projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.sln", StringCompareShould.IgnoreCase); // "Expected test.sln to be only solution found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.sln", StringCompareShould.IgnoreCase); // "Expected test.sln to be only solution found" } /// @@ -1473,10 +1440,10 @@ public void TestProcessProjectSwitch() [Fact] public void TestProcessProjectSwitchReplicateBuildingDFLKG() { - string[] projects = new string[] { "test.proj", "test.sln", "Foo.vcproj" }; + string[] projects = { "test.proj", "test.sln", "Foo.vcproj" }; string[] extensionsToIgnore = { ".sln", ".vcproj" }; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.proj"); // "Expected test.proj to be only project found" + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles).ShouldBe("test.proj"); // "Expected test.proj to be only project found" } /// @@ -1487,12 +1454,10 @@ public void TestProcessProjectSwitchRemovedAllprojects() { Should.Throw(() => { - string[] projects; - string[] extensionsToIgnore = null; - projects = new string[] { "test.nativeproj", "test.vcproj" }; - extensionsToIgnore = new string[] { ".nativeproj", ".vcproj" }; + var projects = new[] { "test.nativeproj", "test.vcproj" }; + var extensionsToIgnore = new[] { ".nativeproj", ".vcproj" }; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles); + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles); } ); } @@ -1504,10 +1469,10 @@ public void TestProcessProjectSwitchSlnProjDifferentNames() { Should.Throw(() => { - string[] projects = new string[] { "test.proj", "Different.sln" }; + string[] projects = { "test.proj", "Different.sln" }; string[] extensionsToIgnore = null; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles); + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles); } ); } @@ -1519,10 +1484,10 @@ public void TestProcessProjectSwitchTwoProj() { Should.Throw(() => { - string[] projects = new string[] { "test.proj", "Different.proj" }; + string[] projects = { "test.proj", "Different.proj" }; string[] extensionsToIgnore = null; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles); + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles); } ); } @@ -1534,10 +1499,10 @@ public void TestProcessProjectSwitchTwoNative() { Should.Throw(() => { - string[] projects = new string[] { "test.nativeproj", "Different.nativeproj" }; + string[] projects = { "test.nativeproj", "Different.nativeproj" }; string[] extensionsToIgnore = null; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles); + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles); } ); } @@ -1549,10 +1514,10 @@ public void TestProcessProjectSwitchTwoSolutions() { Should.Throw(() => { - string[] projects = new string[] { "test.sln", "Different.sln" }; + string[] projects = { "test.sln", "Different.sln" }; string[] extensionsToIgnore = null; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles); + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles); } ); } @@ -1564,10 +1529,10 @@ public void TestProcessProjectSwitchMoreThenTwoProj() { Should.Throw(() => { - string[] projects = new string[] { "test.nativeproj", "Different.csproj", "Another.proj" }; + string[] projects = { "test.nativeproj", "Different.csproj", "Another.proj" }; string[] extensionsToIgnore = null; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles); + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles); } ); } @@ -1579,10 +1544,10 @@ public void TestProcessProjectSwitchNoProjectOrSolution() { Should.Throw(() => { - string[] projects = new string[] { }; + string[] projects = { }; string[] extensionsToIgnore = null; IgnoreProjectExtensionsHelper projectHelper = new IgnoreProjectExtensionsHelper(projects); - MSBuildApp.ProcessProjectSwitch(new string[0] { }, extensionsToIgnore, projectHelper.GetFiles); + MSBuildApp.ProcessProjectSwitch(new string[] { }, extensionsToIgnore, projectHelper.GetFiles); } ); } @@ -1591,7 +1556,7 @@ public void TestProcessProjectSwitchNoProjectOrSolution() /// internal class IgnoreProjectExtensionsHelper { - private List _directoryFileNameList; + private readonly List _directoryFileNameList; /// /// Takes in a list of file names to simulate as being in a directory @@ -1618,14 +1583,14 @@ internal string[] GetFiles(string path, string searchPattern) List fileNamesToReturn = new List(); foreach (string file in _directoryFileNameList) { - if (String.Equals(searchPattern, "*.sln", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(searchPattern, "*.sln", StringComparison.OrdinalIgnoreCase)) { if (FileUtilities.IsSolutionFilename(file)) { fileNamesToReturn.Add(file); } } - else if (String.Equals(searchPattern, "*.*proj", StringComparison.OrdinalIgnoreCase)) + else if (string.Equals(searchPattern, "*.*proj", StringComparison.OrdinalIgnoreCase)) { if (Path.GetExtension(file).Contains("proj")) { @@ -1759,7 +1724,7 @@ public void TestProcessFileLoggerSwitch3() distributedLoggerRecords = new List(); loggers = new List(); - fileLoggerParameters = new string[1] { "Parameter" }; + fileLoggerParameters = new[] { "Parameter" }; MSBuildApp.ProcessDistributedFileLogger ( distributedFileLogger, @@ -1774,7 +1739,7 @@ public void TestProcessFileLoggerSwitch3() distributedLoggerRecords = new List(); loggers = new List(); - fileLoggerParameters = new string[2] { "Parameter1", "Parameter" }; + fileLoggerParameters = new[] { "Parameter1", "Parameter" }; MSBuildApp.ProcessDistributedFileLogger ( distributedFileLogger, @@ -1814,7 +1779,7 @@ public void TestProcessFileLoggerSwitch4() distributedLoggerRecords = new List(); loggers = new List(); - fileLoggerParameters = new string[1] { "verbosity=Normal;" }; + fileLoggerParameters = new[] { "verbosity=Normal;" }; MSBuildApp.ProcessDistributedFileLogger ( distributedFileLogger, @@ -1831,7 +1796,7 @@ public void TestProcessFileLoggerSwitch4() distributedLoggerRecords = new List(); loggers = new List(); - fileLoggerParameters = new string[2] { "verbosity=Normal", "" }; + fileLoggerParameters = new[] { "verbosity=Normal", "" }; MSBuildApp.ProcessDistributedFileLogger ( distributedFileLogger, @@ -1848,7 +1813,7 @@ public void TestProcessFileLoggerSwitch4() distributedLoggerRecords = new List(); loggers = new List(); - fileLoggerParameters = new string[2] { "", "Parameter1" }; + fileLoggerParameters = new[] { "", "Parameter1" }; MSBuildApp.ProcessDistributedFileLogger ( distributedFileLogger, @@ -1865,7 +1830,7 @@ public void TestProcessFileLoggerSwitch4() distributedLoggerRecords = new List(); loggers = new List(); - fileLoggerParameters = new string[2] { "Parameter1", "verbosity=Normal;logfile=" + (NativeMethodsShared.IsWindows ? "c:\\temp\\cat.log" : "/tmp/cat.log") }; + fileLoggerParameters = new[] { "Parameter1", "verbosity=Normal;logfile=" + (NativeMethodsShared.IsWindows ? "c:\\temp\\cat.log" : "/tmp/cat.log") }; MSBuildApp.ProcessDistributedFileLogger ( distributedFileLogger, @@ -1880,7 +1845,7 @@ public void TestProcessFileLoggerSwitch4() distributedLoggerRecords = new List(); loggers = new List(); - fileLoggerParameters = new string[2] { "Parameter1", "verbosity=Normal;logfile=" + Path.Combine("..", "cat.log") + ";Parameter1" }; + fileLoggerParameters = new[] { "Parameter1", "verbosity=Normal;logfile=" + Path.Combine("..", "cat.log") + ";Parameter1" }; MSBuildApp.ProcessDistributedFileLogger ( distributedFileLogger, @@ -1895,7 +1860,7 @@ public void TestProcessFileLoggerSwitch4() loggers = new List(); distributedLoggerRecords = new List(); - fileLoggerParameters = new string[6] { "Parameter1", ";Parameter;", "", ";", ";Parameter", "Parameter;" }; + fileLoggerParameters = new[] { "Parameter1", ";Parameter;", "", ";", ";Parameter", "Parameter;" }; MSBuildApp.ProcessDistributedFileLogger ( distributedFileLogger, @@ -1937,8 +1902,8 @@ public void ProcessConsoleLoggerSwitches() { var loggers = new List(); LoggerVerbosity verbosity = LoggerVerbosity.Normal; - List distributedLoggerRecords = new List(); - string[] consoleLoggerParameters = new string[6] { "Parameter1", ";Parameter;", "", ";", ";Parameter", "Parameter;" }; + List distributedLoggerRecords = new List(); + string[] consoleLoggerParameters = { "Parameter1", ";Parameter;", "", ";", ";Parameter", "Parameter;" }; MSBuildApp.ProcessConsoleLoggerSwitch ( @@ -1991,7 +1956,7 @@ public void RestoreFirstReevaluatesImportGraph() {Guid.NewGuid():N}.props - + @@ -2003,10 +1968,10 @@ public void RestoreFirstReevaluatesImportGraph() - + - + "); string logContents = ExecuteMSBuildExeExpectSuccess(projectContents, arguments: "/restore"); @@ -2026,7 +1991,7 @@ public void RestoreFirstClearsProjectRootElementCache() {restoreFirstProps} - + @@ -2039,10 +2004,10 @@ public void RestoreFirstClearsProjectRootElementCache() - + - + "); IDictionary preExistingProps = new Dictionary @@ -2073,7 +2038,7 @@ public void RestoreIgnoresMissingImports() {restoreFirstProps} - + @@ -2086,10 +2051,10 @@ public void RestoreIgnoresMissingImports() - + - + "); IDictionary preExistingProps = new Dictionary @@ -2186,18 +2151,15 @@ public void MissingOptionalLoggersAreIgnored(string logger) "" + "" + ""; - using (var env = UnitTests.TestEnvironment.Create()) - { - var tempDir = env.CreateFolder(); - var projectFile = tempDir.CreateFile("missingloggertest.proj", projectString); + var tempDir = _env.CreateFolder(); + var projectFile = tempDir.CreateFile("missingloggertest.proj", projectString); - var parametersLoggerOptional = $"{logger} -verbosity:diagnostic \"{projectFile.Path}\""; + var parametersLoggerOptional = $"{logger} -verbosity:diagnostic \"{projectFile.Path}\""; - var output = RunnerUtilities.ExecMSBuild(parametersLoggerOptional, out bool successfulExit, _output); - successfulExit.ShouldBe(true); - output.ShouldContain("Hello", output); - output.ShouldContain("The specified logger could not be created and will not be used.", output); - } + var output = RunnerUtilities.ExecMSBuild(parametersLoggerOptional, out bool successfulExit, _output); + successfulExit.ShouldBe(true); + output.ShouldContain("Hello", output); + output.ShouldContain("The specified logger could not be created and will not be used.", output); } [Theory] @@ -2211,7 +2173,7 @@ public void InteractiveSetsBuiltInProperty(string arguments) - + "); string logContents = ExecuteMSBuildExeExpectSuccess(projectContents, arguments: arguments); @@ -2225,60 +2187,155 @@ public void InteractiveSetsBuiltInProperty(string arguments) [Fact] public void BinaryLogContainsImportedFiles() { - using (TestEnvironment testEnvironment = UnitTests.TestEnvironment.Create()) - { - var testProject = testEnvironment.CreateFile("Importer.proj", ObjectModelHelpers.CleanupFileContents(@" - - + var testProject = _env.CreateFile("Importer.proj", ObjectModelHelpers.CleanupFileContents(@" + + - - - - ")); + + - testEnvironment.CreateFile("TestProject.proj", @" - - - - - - "); + ")); - string binLogLocation = testEnvironment.DefaultTestDirectory.Path; + _env.CreateFile("TestProject.proj", @" + + + + + + "); - string output = RunnerUtilities.ExecMSBuild($"\"{testProject.Path}\" \"/bl:{binLogLocation}/output.binlog\"", out var success, _output); + string binLogLocation = _env.DefaultTestDirectory.Path; - success.ShouldBeTrue(output); + string output = RunnerUtilities.ExecMSBuild($"\"{testProject.Path}\" \"/bl:{binLogLocation}/output.binlog\"", out var success, _output); - RunnerUtilities.ExecMSBuild($"\"{binLogLocation}/output.binlog\" \"/bl:{binLogLocation}/replay.binlog;ProjectImports=ZipFile\"", out success, _output); + success.ShouldBeTrue(output); - using (ZipArchive archive = ZipFile.OpenRead($"{binLogLocation}/replay.ProjectImports.zip")) - { - archive.Entries.ShouldContain(e => e.FullName.EndsWith(".proj", StringComparison.OrdinalIgnoreCase), 2); - } - } + RunnerUtilities.ExecMSBuild($"\"{binLogLocation}/output.binlog\" \"/bl:{binLogLocation}/replay.binlog;ProjectImports=ZipFile\"", out success, _output); + + using ZipArchive archive = ZipFile.OpenRead($"{binLogLocation}/replay.ProjectImports.zip"); + archive.Entries.ShouldContain(e => e.FullName.EndsWith(".proj", StringComparison.OrdinalIgnoreCase), 2); } [Fact] public void EndToEndWarnAsErrors() { - using TestEnvironment testEnvironment = UnitTests.TestEnvironment.Create(); - string projectContents = ObjectModelHelpers.CleanupFileContents(@" - + "); - TransientTestProjectWithFiles testProject = testEnvironment.CreateTestProjectWithFiles(projectContents); + TransientTestProjectWithFiles testProject = _env.CreateTestProjectWithFiles(projectContents); RunnerUtilities.ExecMSBuild($"\"{testProject.ProjectFile}\" -warnaserror", out bool success, _output); success.ShouldBeFalse(); } + [Trait("Category", "netcore-osx-failing")] + [Trait("Category", "netcore-linux-failing")] + [Fact] + public void BuildSlnOutOfProc() + { + string solutionFileContents = +@"Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +Project('{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}') = 'TestProject', 'TestProject.proj', '{6185CC21-BE89-448A-B3C0-D1C27112E595}' +EndProject +Global +GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Mixed Platforms = Debug|Mixed Platforms + Release|Any CPU = Release|Any CPU +EndGlobalSection +GlobalSection(ProjectConfigurationPlatforms) = postSolution + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Mixed Platforms.ActiveCfg = CSConfig1|Any CPU + {6185CC21-BE89-448A-B3C0-D1C27112E595}.Debug|Mixed Platforms.Build.0 = CSConfig1|Any CPU +EndGlobalSection +EndGlobal + ".Replace("'", "\""); + + var testSolution = _env.CreateFile("TestSolution.sln", ObjectModelHelpers.CleanupFileContents(solutionFileContents)); + + string testMessage = "Hello from TestProject!"; + _env.CreateFile("TestProject.proj", @$" + + + + + + "); + + _env.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1"); + + string output = RunnerUtilities.ExecMSBuild($"\"{testSolution.Path}\" /p:Configuration=Debug", out var success, _output); + + success.ShouldBeTrue(output); + output.ShouldContain(testMessage); + } + + /// + /// Helper task used by to verify . + /// + public class MessageImportanceCheckingTask : Task + { + public int ExpectedMinimumMessageImportance { get; set; } + + public override bool Execute() + { + bool shouldLogHigh = Log.LogsMessagesOfImportance(MessageImportance.High); + bool shouldLogNormal = Log.LogsMessagesOfImportance(MessageImportance.Normal); + bool shouldLogLow = Log.LogsMessagesOfImportance(MessageImportance.Low); + return (MessageImportance)ExpectedMinimumMessageImportance switch + { + MessageImportance.High - 1 => !shouldLogHigh && !shouldLogNormal && !shouldLogLow, + MessageImportance.High => shouldLogHigh && !shouldLogNormal && !shouldLogLow, + MessageImportance.Normal => shouldLogHigh && shouldLogNormal && !shouldLogLow, + MessageImportance.Low => shouldLogHigh && shouldLogNormal && shouldLogLow, + _ => false + }; + } + } + + [Theory] + [InlineData("/v:diagnostic", MessageImportance.Low)] + [InlineData("/v:detailed", MessageImportance.Low)] + [InlineData("/v:normal", MessageImportance.Normal)] + [InlineData("/v:minimal", MessageImportance.High)] + [InlineData("/v:quiet", MessageImportance.High - 1)] + [InlineData("/v:diagnostic /bl", MessageImportance.Low)] + [InlineData("/v:detailed /bl", MessageImportance.Low)] + [InlineData("/v:normal /bl", MessageImportance.Low)] // v:normal but with binary logger so everything must be logged + [InlineData("/v:minimal /bl", MessageImportance.Low)] // v:minimal but with binary logger so everything must be logged + [InlineData("/v:quiet /bl", MessageImportance.Low)] // v:quiet but with binary logger so everything must be logged + public void EndToEndMinimumMessageImportance(string arguments, MessageImportance expectedMinimumMessageImportance) + { + using TestEnvironment testEnvironment = UnitTests.TestEnvironment.Create(); + + string projectContents = ObjectModelHelpers.CleanupFileContents(@" + + + + + + + +"); + + TransientTestProjectWithFiles testProject = testEnvironment.CreateTestProjectWithFiles(projectContents); + + // Build in-proc. + RunnerUtilities.ExecMSBuild($"{arguments} \"{testProject.ProjectFile}\"", out bool success, _output); + success.ShouldBeTrue(); + + // Build out-of-proc to exercise both logging code paths. + testEnvironment.SetEnvironmentVariable("MSBUILDNOINPROCNODE", "1"); + testEnvironment.SetEnvironmentVariable("MSBUILDDISABLENODEREUSE", "1"); + RunnerUtilities.ExecMSBuild($"{arguments} \"{testProject.ProjectFile}\"", out success, _output); + success.ShouldBeTrue(); + } + #if FEATURE_ASSEMBLYLOADCONTEXT /// /// Ensure that tasks get loaded into their own . @@ -2372,32 +2429,32 @@ private string ExecuteMSBuildExeExpectFailure(string projectContents, IDictionar private (bool result, string output) ExecuteMSBuildExe(string projectContents, IDictionary filesToCreate = null, IDictionary envsToCreate = null, params string[] arguments) { - using (TestEnvironment testEnvironment = UnitTests.TestEnvironment.Create()) - { - TransientTestProjectWithFiles testProject = testEnvironment.CreateTestProjectWithFiles(projectContents, new string[0]); + TransientTestProjectWithFiles testProject = _env.CreateTestProjectWithFiles(projectContents, new string[0]); - if (filesToCreate != null) + if (filesToCreate != null) + { + foreach (var item in filesToCreate) { - foreach (var item in filesToCreate) - { - File.WriteAllText(Path.Combine(testProject.TestRoot, item.Key), item.Value); - } + File.WriteAllText(Path.Combine(testProject.TestRoot, item.Key), item.Value); } + } - if (envsToCreate != null) + if (envsToCreate != null) + { + foreach (var env in envsToCreate) { - foreach (var env in envsToCreate) - { - testEnvironment.SetEnvironmentVariable(env.Key, env.Value); - } + _env.SetEnvironmentVariable(env.Key, env.Value); } + } - bool success; + string output = RunnerUtilities.ExecMSBuild($"\"{testProject.ProjectFile}\" {string.Join(" ", arguments)}", out var success, _output); - string output = RunnerUtilities.ExecMSBuild($"\"{testProject.ProjectFile}\" {String.Join(" ", arguments)}", out success, _output); + return (success, output); + } - return (success, output); - } + public void Dispose() + { + _env.Dispose(); } } } diff --git a/src/MSBuild/AssemblyInfo.cs b/src/MSBuild/AssemblyInfo.cs index 63428b4c74e..67d13ed587e 100644 --- a/src/MSBuild/AssemblyInfo.cs +++ b/src/MSBuild/AssemblyInfo.cs @@ -13,14 +13,7 @@ // so that we don't run into known security issues with loading libraries from unsafe locations [assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] -// Needed for the "hub-and-spoke model to locate and retrieve localized resources": https://msdn.microsoft.com/en-us/library/21a15yht(v=vs.110).aspx -// We want "en" to require a satellite assembly for debug builds in order to flush out localization -// issues, but we want release builds to work without it. Also, .net core does not have resource fallbacks -#if (DEBUG && !RUNTIME_TYPE_NETCORE) -[assembly: NeutralResourcesLanguage("en", UltimateResourceFallbackLocation.Satellite)] -#else [assembly: NeutralResourcesLanguage("en")] -#endif [assembly: ComVisible(false)] [assembly: CLSCompliant(true)] diff --git a/src/MSBuild/GitBuildInfo.cs b/src/MSBuild/GitBuildInfo.cs index 0edd6d856ac..6de36d537da 100644 --- a/src/MSBuild/GitBuildInfo.cs +++ b/src/MSBuild/GitBuildInfo.cs @@ -1 +1 @@ -class GitBuildInfoForMono { public static string BuildInfo = "xplat-master/9acaaeee4 Sat Mar 27 12:09:15 CET 2021"; } +class GitBuildInfoForMono { public static string BuildInfo = "xplat-master/54834ff9d Sun May 1 07:31:58 AM EEST 2022"; } diff --git a/src/MSBuild/MSBuild.csproj b/src/MSBuild/MSBuild.csproj index 63b38a6ba1a..7c15eba0ecb 100644 --- a/src/MSBuild/MSBuild.csproj +++ b/src/MSBuild/MSBuild.csproj @@ -14,7 +14,7 @@ - win7-x86;win7-x64 + win7-x86;win7-x64 false true @@ -25,11 +25,17 @@ false MSBuild.exe.manifest + app.config app.amd64.config + + false + true contentFiles contentFiles\any\ diff --git a/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd b/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd index 4810afd35e9..c032f6978c2 100644 --- a/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd +++ b/src/MSBuild/MSBuild/Microsoft.Build.CommonTypes.xsd @@ -1171,6 +1171,25 @@ elementFormDefault="qualified"> + + + Customizes the application default font. The format equivalent to the output of FontConverter.ConvertToInvariantString(). Applies only to Windows Forms projects. + + + + + Customizes the application DPI awareness mode. Applies only to Windows Forms projects. + + + + + + + + + + + @@ -1182,6 +1201,16 @@ elementFormDefault="qualified"> Matches the expression "\d\.\d\.\d\.(\d|\*)" + + + Indicates whether to set UseCompatibleTextRendering property defined on certain controls (boolean). Applies only to Windows Forms projects. + + + + + Indicates whether to enable or disable visual styles (boolean). Applies only to Windows Forms projects. + + Name of folder for Application Designer @@ -1252,10 +1281,19 @@ elementFormDefault="qualified"> Whether to emit symbols (boolean) - + - none, pdbonly, or full + none, pdbonly, embedded, portable, or full. From C# 6 onwards, pdbonly is the same as full. + + + + + + + + + @@ -1552,6 +1590,18 @@ elementFormDefault="qualified"> + + + Enable implicit global usings for the C# project. Possible values are enable, true, and disable. + + + + + + + + + @@ -1680,11 +1730,21 @@ elementFormDefault="qualified"> Type of output to generate (WinExe, Exe, or Library) + + + Path to the output folder for the package generated when calling Pack. + + Allows packages using alternative monikers to be referenced in this project, which include older (e.g. dnxcore50, dotnet5.x) and Portable Class Library names. + + + Indicate whether the NuGet package should be configured as a .NET tool suitable for use with "dotnet tool install". + + Can be set to one or more target framework monikers. When determining package compatibility, if the package does not have compatible assets for the project's real target framework, compatibility will be rechecked using each target framework from the AssetTargetFramework project of the referencing project. @@ -1724,6 +1784,7 @@ elementFormDefault="qualified"> Value indicating whether reference assemblies can be used in dynamic compilation + @@ -1788,6 +1849,11 @@ elementFormDefault="qualified"> + + + Semi-colon separated list of culture names to preserve satellite resource assemblies during build and publish. Names must be a valid culture name (like en-US;it; or fr). If left empty, all satellite resource assemblies will be preserved. Defaults to empty. + + @@ -1828,6 +1894,11 @@ elementFormDefault="qualified"> + + + Specifies the command that will invoke the tool after it's installed. + + @@ -1868,6 +1939,8 @@ elementFormDefault="qualified"> + + @@ -5743,4 +5816,67 @@ elementFormDefault="qualified"> + + + + A C# global using to add to the project. + + + + + + + + + The namespace or type identifier to add, e.g. Microsoft.AspNetCore + + + + + + + Optional alias for the namespace or type. + + + + + + + Determines whether the identifier should be registered as a static import. + + + + + + + + + + + + Specifies that internal types and members are visible to the specified friend assemblies. + + + + + + + + + The name of the friend assembly to make internal types and members visible to. + + + + + + + Optional public key associated with the strong name signature of the friend assembly. + + + + + + + + diff --git a/src/MSBuild/OutOfProcTaskHostNode.cs b/src/MSBuild/OutOfProcTaskHostNode.cs index 34adcd7cb25..6dc795d6762 100644 --- a/src/MSBuild/OutOfProcTaskHostNode.cs +++ b/src/MSBuild/OutOfProcTaskHostNode.cs @@ -33,7 +33,7 @@ internal class OutOfProcTaskHostNode : #if CLR2COMPATIBILITY IBuildEngine3 #else - IBuildEngine9 + IBuildEngine10 #endif { /// @@ -172,7 +172,7 @@ public OutOfProcTaskHostNode() // We don't know what the current build thinks this variable should be until RunTask(), but as a fallback in case there are // communications before we get the configuration set up, just go with what was already in the environment from when this node // was initially launched. - _debugCommunications = (Environment.GetEnvironmentVariable("MSBUILDDEBUGCOMM") == "1"); + _debugCommunications = Traits.Instance.DebugNodeCommunication; _receivedPackets = new Queue(); @@ -189,6 +189,10 @@ public OutOfProcTaskHostNode() thisINodePacketFactory.RegisterPacketHandler(NodePacketType.TaskHostConfiguration, TaskHostConfiguration.FactoryForDeserialization, this); thisINodePacketFactory.RegisterPacketHandler(NodePacketType.TaskHostTaskCancelled, TaskHostTaskCancelled.FactoryForDeserialization, this); thisINodePacketFactory.RegisterPacketHandler(NodePacketType.NodeBuildComplete, NodeBuildComplete.FactoryForDeserialization, this); + +#if !CLR2COMPATIBILITY + EngineServices = new EngineServicesImpl(this); +#endif } #region IBuildEngine Implementation (Properties) @@ -492,6 +496,39 @@ public void ReleaseCores(int coresToRelease) } #endregion + + #region IBuildEngine10 Members + + [Serializable] + private sealed class EngineServicesImpl : EngineServices + { + private readonly OutOfProcTaskHostNode _taskHost; + + internal EngineServicesImpl(OutOfProcTaskHostNode taskHost) + { + _taskHost = taskHost; + } + + /// + /// No logging verbosity optimization in OOP nodes. + /// + public override bool LogsMessagesOfImportance(MessageImportance importance) => true; + + /// + public override bool IsTaskInputLoggingEnabled + { + get + { + ErrorUtilities.VerifyThrow(_taskHost._currentConfiguration != null, "We should never have a null configuration during a BuildEngine callback!"); + return _taskHost._currentConfiguration.IsTaskInputLoggingEnabled; + } + } + } + + public EngineServices EngineServices { get; } + + #endregion + #endif #region INodePacketFactory Members diff --git a/src/MSBuild/Resources/xlf/Strings.cs.xlf b/src/MSBuild/Resources/xlf/Strings.cs.xlf index d1ce644a52e..5769d82fa59 100644 --- a/src/MSBuild/Resources/xlf/Strings.cs.xlf +++ b/src/MSBuild/Resources/xlf/Strings.cs.xlf @@ -1,4 +1,4 @@ - + @@ -494,10 +494,10 @@ Copyright (C) Microsoft Corporation. Všechna práva vyhrazena. ErrorsOnly – zobrazí jenom chyby. WarningsOnly – zobrazí jenom upozornění. NoItemAndPropertyList – nezobrazí na začátku sestavení každého - projektu seznamy položek a vlastností. - ShowCommandLine – zobrazí zprávy TaskCommandLineEvent. + projektu seznamy položek a vlastností. + ShowCommandLine – zobrazí zprávy TaskCommandLineEvent. ShowTimestamp – před každou zprávou zobrazí - časové razítko. + časové razítko. ShowEventId – zobrazí ID události pro spuštěné a dokončené události a zprávy. ForceNoAlign – nenastavuje text podle velikosti vyrovnávací @@ -634,7 +634,7 @@ Copyright (C) Microsoft Corporation. Všechna práva vyhrazena. Setting this also turns on isolated builds (-isolate). (short form: -orc) - -outputResultsCache:<souborMezipaměti>... + -outputResultsCache:[souborMezipaměti]... Výstupní soubor mezipaměti, do něhož bude MSBuild zapisovat obsah svých mezipamětí výsledků sestavení. Nastavením této možnosti zapnete také izolované buildy (-isolate). @@ -794,7 +794,7 @@ Copyright (C) Microsoft Corporation. Všechna práva vyhrazena. template and append the node id to this fileName to create a log file for each node. - -distributedFileLogger + -distributedFileLogger Uloží výstup sestavení do více souborů protokolu, po jednom pro každý uzel nástroje MSBuild. Tyto soubory jsou na počátku umístěny v aktuálním adresáři. Standardně mají tyto soubory @@ -1566,48 +1566,48 @@ Copyright (C) Microsoft Corporation. Všechna práva vyhrazena. -binaryLogger -binaryLogger[:[LogFile=]output.binlog[;ProjectImports={None,Embed,ZipFile}]] - Serializuje všechny události sestavení do komprimovaného - binárního souboru. Tento soubor se standardně nachází - v aktuálním adresáři a má název msbuild.binlog. Binární - protokol je podrobný popis procesu sestavení, který se + Serializuje všechny události sestavení do komprimovaného binárního souboru. + Tento soubor se standardně nachází v aktuálním adresáři a má název msbuild.binlog. + Binární protokol je podrobný popis procesu sestavení, který se dá později použít k rekonstrukci textových protokolů a který můžou používat jiné nástroje pro analýzu. Binární protokol má obvykle 10–20krát menší velikost než - nejpodrobnější textový protokol na úrovni diagnostiky, - ale obsahuje více informací. + nejpodrobnější textový protokol na úrovni diagnostiky, ale obsahuje více informací. (Krátký tvar: -bl) Binární protokolovací nástroj standardně shromažďuje zdrojový text projektových souborů včetně všech importovaných projektů a cílových souborů zjištěných - při sestavování. Toto chování řídí volitelný parametr - ProjectImports: - ProjectImports=None - Neshromažďovat importované - projekty + při sestavování. Toto chování řídí volitelný parametr ProjectImports: + + ProjectImports=None - Neshromažďovat importované + projekty. ProjectImports=Embed - Vložit importované projekty - do souboru protokolu + do souboru protokolu. ProjectImports=ZipFile - Uložit projektové soubory do output.projectimports.zip, kde output je stejný název - jako název souboru binárního - protokolu + jako název souboru binárního protokolu. + Výchozí nastavení pro ProjectImports je Embed. Poznámka: Protokolovací nástroj neshromažďuje zdrojové soubory nepatřící pod MSBuild, jako .cs, .cpp atd. + Soubor .binlog se dá znovu přehrát“ tak, že se předá souboru msbuild.exe jako argument místo projektu/řešení. Jiné protokolovací nástroje obdrží informace obsažené v souboru protokolu, jako by šlo o původní sestavení. - Více o binárním protokolu a jeho použití si můžete - přečíst na stránce: + Více o binárním protokolu a jeho použití si můžete přečíst na stránce: https://aka.ms/msbuild/binlog + Příklady: -bl -bl:output.binlog -bl:output.binlog;ProjectImports=None -bl:output.binlog;ProjectImports=ZipFile - -bl:..\\..\\custom.binlog - -binaryLogger + -bl:..\..\custom.binlog + -binaryLogger + LOCALIZATION: The following should not be localized: 1) "msbuild" diff --git a/src/MSBuild/Resources/xlf/Strings.de.xlf b/src/MSBuild/Resources/xlf/Strings.de.xlf index 8284308c137..9ccad6d1801 100644 --- a/src/MSBuild/Resources/xlf/Strings.de.xlf +++ b/src/MSBuild/Resources/xlf/Strings.de.xlf @@ -1,4 +1,4 @@ - + @@ -505,7 +505,7 @@ Copyright (C) Microsoft Corporation. Alle Rechte vorbehalten. bei der Mehrprozessorprotokollierung im Modus mit nur einem Prozessor. EnableMPLogging: Aktiviert das Format der Mehrprozessorprotokollierung auch bei der Ausführung - im Modus mit nur einem Prozessor. Dieses Protokollierungsformat ist standardmäßig aktiviert. + im Modus mit nur einem Prozessor. Dieses Protokollierungsformat ist standardmäßig aktiviert. ForceConsoleColor: Verwendet selbst dann ANSI-Konsolenfarben, wenn die Konsole dies nicht unterstützt. diff --git a/src/MSBuild/Resources/xlf/Strings.en.xlf b/src/MSBuild/Resources/xlf/Strings.en.xlf deleted file mode 100644 index c376c7e4b9d..00000000000 --- a/src/MSBuild/Resources/xlf/Strings.en.xlf +++ /dev/null @@ -1,1712 +0,0 @@ - - - - - - MSBUILD : error MSB1011: Specify which project or solution file to use because this folder contains more than one project or solution file. - MSBUILD : error MSB1011: Specify which project or solution file to use because this folder contains more than one project or solution file. - {StrBegin="MSBUILD : error MSB1011: "}UE: If no project or solution file is explicitly specified on the MSBuild.exe command-line, then the engine searches for a - project or solution file in the current directory by looking for *.*PROJ and *.SLN. If more than one file is found that matches this wildcard, we - fire this error. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - MSBUILD : error MSB1050: Specify which project or solution file to use because the folder "{0}" contains more than one project or solution file. - MSBUILD : error MSB1050: Specify which project or solution file to use because the folder "{0}" contains more than one project or solution file. - - {StrBegin="MSBUILD : error MSB1050: "}UE: If no project or solution file is explicitly specified on the MSBuild.exe command-line, then the engine searches for a - project or solution file in the current directory by looking for *.*PROJ and *.SLN. If more than one file is found that matches this wildcard, we - fire this error. - LOCALIZATION: The prefix "MSB1050 : error MSBxxxx:" should not be localized. - - - - Command line arguments = "{0}" - Command line arguments = "{0}" - - - - MSBUILD : Configuration error {0}: {1} - MSBUILD : Configuration error {0}: {1} - {SubString="Configuration"}UE: This prefixes any error from reading the toolset definitions in msbuild.exe.config or the registry. - There's no error code because one was included in the error message. - LOCALIZATION: The word "Configuration" should be localized, the words "MSBuild" and "error" should NOT be localized. - - - - MSBUILD : error MSB1027: The -noAutoResponse switch cannot be specified in the MSBuild.rsp auto-response file, nor in any response file that is referenced by the auto-response file. - MSBUILD : error MSB1027: The -noAutoResponse switch cannot be specified in the MSBuild.rsp auto-response file, nor in any response file that is referenced by the auto-response file. - {StrBegin="MSBUILD : error MSB1027: "}LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:", "-noAutoResponse" and "MSBuild.rsp" should not be localized. - - - Microsoft (R) Build Engine version {0} for {1} -Copyright (C) Microsoft Corporation. All rights reserved. - - Microsoft (R) Build Engine version {0} for {1} -Copyright (C) Microsoft Corporation. All rights reserved. - - LOCALIZATION: {0} contains the DLL version number. {1} contains the name of a runtime, like ".NET Framework", ".NET Core", or "Mono" - - - Current directory = "{0}" - Current directory = "{0}" - - - - MSBUILD : error MSB1058: Only one output results cache can be specified. - MSBUILD : error MSB1058: Only one output results cache can be specified. - {StrBegin="MSBUILD : error MSB1058: "} - - - MSBUILD : error MSB1008: Only one project can be specified. - MSBUILD : error MSB1008: Only one project can be specified. - {StrBegin="MSBUILD : error MSB1008: "}UE: This happens if the user does something like "msbuild.exe myapp.proj myapp2.proj". This is not allowed. - MSBuild.exe will only build a single project. The help topic may link to an article about how to author an MSBuild project - that itself launches MSBuild on a number of other projects. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - MSBUILD : error MSB1025: An internal failure occurred while running MSBuild. - MSBUILD : error MSB1025: An internal failure occurred while running MSBuild. - {StrBegin="MSBUILD : error MSB1025: "}UE: This message is shown when the application has to terminate either because of a bug in the code, or because some - FX/CLR method threw an unexpected exception. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" and "MSBuild" should not be localized. - - - Syntax: MSBuild.exe [options] [project file | directory] - - Syntax: MSBuild.exe [options] [project file | directory] - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - Description: Builds the specified targets in the project file. If - a project file is not specified, MSBuild searches the - current working directory for a file that has a file - extension that ends in "proj" and uses that file. If - a directory is specified, MSBuild searches that - directory for a project file. - - Description: Builds the specified targets in the project file. If - a project file is not specified, MSBuild searches the - current working directory for a file that has a file - extension that ends in "proj" and uses that file. If - a directory is specified, MSBuild searches that - directory for a project file. - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -interactive[:True|False] - Indicates that actions in the build are allowed to - interact with the user. Do not use this argument - in an automated scenario where interactivity is - not expected. - Specifying -interactive is the same as specifying - -interactive:true. Use the parameter to override a - value that comes from a response file. - - -interactive[:True|False] - Indicates that actions in the build are allowed to - interact with the user. Do not use this argument - in an automated scenario where interactivity is - not expected. - Specifying -interactive is the same as specifying - -interactive:true. Use the parameter to override a - value that comes from a response file. - - - LOCALIZATION: "-interactive" should not be localized. - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -isolateProjects[:True|False] - Causes MSBuild to build each project in isolation. - - This is a more restrictive mode of MSBuild as it requires - that the project graph be statically discoverable at - evaluation time, but can improve scheduling and reduce - memory overhead when building a large set of projects. - (Short form: -isolate) - - This flag is experimental and may not work as intended. - - -isolateProjects[:True|False] - Causes MSBuild to build each project in isolation. - - This is a more restrictive mode of MSBuild as it requires - that the project graph be statically discoverable at - evaluation time, but can improve scheduling and reduce - memory overhead when building a large set of projects. - (Short form: -isolate) - - This flag is experimental and may not work as intended. - - - LOCALIZATION: "MSBuild" should not be localized. - LOCALIZATION: "-isolateProjects" should not be localized. - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - -graphBuild[:True|False] - Causes MSBuild to construct and build a project graph. - - Constructing a graph involves identifying project - references to form dependencies. Building that graph - involves attempting to build project references prior - to the projects that reference them, differing from - traditional MSBuild scheduling. - (Short form: -graph) - - This flag is experimental and may not work as intended. - - -graphBuild[:True|False] - Causes MSBuild to construct and build a project graph. - - Constructing a graph involves identifying project - references to form dependencies. Building that graph - involves attempting to build project references prior - to the projects that reference them, differing from - traditional MSBuild scheduling. - (Short form: -graph) - - This flag is experimental and may not work as intended. - - - LOCALIZATION: "MSBuild" should not be localized. - LOCALIZATION: "-graphBuild" and "-graph" should not be localized. - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - For more detailed information, see https://aka.ms/msbuild/docs - For more detailed information, see https://aka.ms/msbuild/docs - - - - -targets[:file] - Prints a list of available targets without executing the - actual build process. By default the output is written to - the console window. If the path to an output file - is provided that will be used instead. - (Short form: -ts) - Example: - -ts:out.txt - - -targets[:file] - Prints a list of available targets without executing the - actual build process. By default the output is written to - the console window. If the path to an output file - is provided that will be used instead. - (Short form: -ts) - Example: - -ts:out.txt - - - LOCALIZATION: "MSBuild" should not be localized. - LOCALIZATION: "-targets" and "-ts" should not be localized. - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -lowPriority[:True|False] - Causes MSBuild to run at low process priority. - - Specifying -lowPriority is the same as specifying - -lowPriority:True. - (Short form: -low) - - -lowPriority[:True|False] - Causes MSBuild to run at low process priority. - - Specifying -lowPriority is the same as specifying - -lowPriority:True. - (Short form: -low) - - - LOCALIZATION: "MSBuild" should not be localized. - LOCALIZATION: "-lowPriority" and "-low" should not be localized. - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - Switches: Note that you can specify switches using - "-switch", "/switch" and "--switch". - - Switches: Note that you can specify switches using - "-switch", "/switch" and "--switch". - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -help Display this usage message. (Short form: -? or -h) - - -help Display this usage message. (Short form: -? or -h) - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -noLogo Do not display the startup banner and copyright message. - - -noLogo Do not display the startup banner and copyright message. - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -version Display version information only. (Short form: -ver) - - -version Display version information only. (Short form: -ver) - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - @<file> Insert command-line settings from a text file. To specify - multiple response files, specify each response file - separately. - - Any response files named "msbuild.rsp" are automatically - consumed from the following locations: - (1) the directory of msbuild.exe - (2) the directory of the first project or solution built - - @<file> Insert command-line settings from a text file. To specify - multiple response files, specify each response file - separately. - - Any response files named "msbuild.rsp" are automatically - consumed from the following locations: - (1) the directory of msbuild.exe - (2) the directory of the first project or solution built - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -noAutoResponse Do not auto-include any MSBuild.rsp files. (Short form: - -noAutoRsp) - - -noAutoResponse Do not auto-include any MSBuild.rsp files. (Short form: - -noAutoRsp) - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -target:<targets> Build these targets in this project. Use a semicolon or a - comma to separate multiple targets, or specify each - target separately. (Short form: -t) - Example: - -target:Resources;Compile - - -target:<targets> Build these targets in this project. Use a semicolon or a - comma to separate multiple targets, or specify each - target separately. (Short form: -t) - Example: - -target:Resources;Compile - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -property:<n>=<v> Set or override these project-level properties. <n> is - the property name, and <v> is the property value. Use a - semicolon or a comma to separate multiple properties, or - specify each property separately. (Short form: -p) - Example: - -property:WarningLevel=2;OutDir=bin\Debug\ - - -property:<n>=<v> Set or override these project-level properties. <n> is - the property name, and <v> is the property value. Use a - semicolon or a comma to separate multiple properties, or - specify each property separately. (Short form: -p) - Example: - -property:WarningLevel=2;OutDir=bin\Debug\ - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -logger:<logger> Use this logger to log events from MSBuild. To specify - multiple loggers, specify each logger separately. - The <logger> syntax is: - [<class>,]<assembly>[,<options>][;<parameters>] - The <logger class> syntax is: - [<partial or full namespace>.]<logger class name> - The <logger assembly> syntax is: - {<assembly name>[,<strong name>] | <assembly file>} - Logger options specify how MSBuild creates the logger. - The <logger parameters> are optional, and are passed - to the logger exactly as you typed them. (Short form: -l) - Examples: - -logger:XMLLogger,MyLogger,Version=1.0.2,Culture=neutral - -logger:XMLLogger,C:\Loggers\MyLogger.dll;OutputAsHTML - - -logger:<logger> Use this logger to log events from MSBuild. To specify - multiple loggers, specify each logger separately. - The <logger> syntax is: - [<class>,]<assembly>[,<options>][;<parameters>] - The <logger class> syntax is: - [<partial or full namespace>.]<logger class name> - The <logger assembly> syntax is: - {<assembly name>[,<strong name>] | <assembly file>} - Logger options specify how MSBuild creates the logger. - The <logger parameters> are optional, and are passed - to the logger exactly as you typed them. (Short form: -l) - Examples: - -logger:XMLLogger,MyLogger,Version=1.0.2,Culture=neutral - -logger:XMLLogger,C:\Loggers\MyLogger.dll;OutputAsHTML - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -verbosity:<level> Display this amount of information in the event log. - The available verbosity levels are: q[uiet], m[inimal], - n[ormal], d[etailed], and diag[nostic]. (Short form: -v) - Example: - -verbosity:quiet - - -verbosity:<level> Display this amount of information in the event log. - The available verbosity levels are: q[uiet], m[inimal], - n[ormal], d[etailed], and diag[nostic]. (Short form: -v) - Example: - -verbosity:quiet - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -consoleLoggerParameters:<parameters> - Parameters to console logger. (Short form: -clp) - The available parameters are: - PerformanceSummary--Show time spent in tasks, targets - and projects. - Summary--Show error and warning summary at the end. - NoSummary--Don't show error and warning summary at the - end. - ErrorsOnly--Show only errors. - WarningsOnly--Show only warnings. - NoItemAndPropertyList--Don't show list of items and - properties at the start of each project build. - ShowCommandLine--Show TaskCommandLineEvent messages - ShowTimestamp--Display the Timestamp as a prefix to any - message. - ShowEventId--Show eventId for started events, finished - events, and messages - ForceNoAlign--Does not align the text to the size of - the console buffer - DisableConsoleColor--Use the default console colors - for all logging messages. - DisableMPLogging-- Disable the multiprocessor - logging style of output when running in - non-multiprocessor mode. - EnableMPLogging--Enable the multiprocessor logging - style even when running in non-multiprocessor - mode. This logging style is on by default. - ForceConsoleColor--Use ANSI console colors even if - console does not support it - Verbosity--overrides the -verbosity setting for this - logger. - Example: - -consoleLoggerParameters:PerformanceSummary;NoSummary; - Verbosity=minimal - - -consoleLoggerParameters:<parameters> - Parameters to console logger. (Short form: -clp) - The available parameters are: - PerformanceSummary--Show time spent in tasks, targets - and projects. - Summary--Show error and warning summary at the end. - NoSummary--Don't show error and warning summary at the - end. - ErrorsOnly--Show only errors. - WarningsOnly--Show only warnings. - NoItemAndPropertyList--Don't show list of items and - properties at the start of each project build. - ShowCommandLine--Show TaskCommandLineEvent messages - ShowTimestamp--Display the Timestamp as a prefix to any - message. - ShowEventId--Show eventId for started events, finished - events, and messages - ForceNoAlign--Does not align the text to the size of - the console buffer - DisableConsoleColor--Use the default console colors - for all logging messages. - DisableMPLogging-- Disable the multiprocessor - logging style of output when running in - non-multiprocessor mode. - EnableMPLogging--Enable the multiprocessor logging - style even when running in non-multiprocessor - mode. This logging style is on by default. - ForceConsoleColor--Use ANSI console colors even if - console does not support it - Verbosity--overrides the -verbosity setting for this - logger. - Example: - -consoleLoggerParameters:PerformanceSummary;NoSummary; - Verbosity=minimal - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -noConsoleLogger Disable the default console logger and do not log events - to the console. (Short form: -noConLog) - - -noConsoleLogger Disable the default console logger and do not log events - to the console. (Short form: -noConLog) - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -validate Validate the project against the default schema. (Short - form: -val) - - -validate:<schema> Validate the project against the specified schema. (Short - form: -val) - Example: - -validate:MyExtendedBuildSchema.xsd - - -validate Validate the project against the default schema. (Short - form: -val) - - -validate:<schema> Validate the project against the specified schema. (Short - form: -val) - Example: - -validate:MyExtendedBuildSchema.xsd - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -maxCpuCount[:n] Specifies the maximum number of concurrent processes to - build with. If the switch is not used, the default - value used is 1. If the switch is used without a value - MSBuild will use up to the number of processors on the - computer. (Short form: -m[:n]) - - -maxCpuCount[:n] Specifies the maximum number of concurrent processes to - build with. If the switch is not used, the default - value used is 1. If the switch is used without a value - MSBuild will use up to the number of processors on the - computer. (Short form: -m[:n]) - - - LOCALIZATION: "maxCpuCount" should not be localized. - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - Examples: - - MSBuild MyApp.sln -t:Rebuild -p:Configuration=Release - MSBuild MyApp.csproj -t:Clean - -p:Configuration=Debug;TargetFrameworkVersion=v3.5 - - Examples: - - MSBuild MyApp.sln -t:Rebuild -p:Configuration=Release - MSBuild MyApp.csproj -t:Clean - -p:Configuration=Debug;TargetFrameworkVersion=v3.5 - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -inputResultsCaches:<cacheFile>... - Semicolon separated list of input cache files that MSBuild - will read build results from. - Setting this also turns on isolated builds (-isolate). - (short form: -irc) - - -inputResultsCaches:<cacheFile>... - Semicolon separated list of input cache files that MSBuild - will read build results from. - Setting this also turns on isolated builds (-isolate). - (short form: -irc) - - - LOCALIZATION: The following should not be localized: MSBuild, -isolate - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -outputResultsCache:[cacheFile] - Output cache file where MSBuild will write the contents of - its build result caches at the end of the build. - Setting this also turns on isolated builds (-isolate). - (short form: -orc) - - -outputResultsCache:[cacheFile] - Output cache file where MSBuild will write the contents of - its build result caches at the end of the build. - Setting this also turns on isolated builds (-isolate). - (short form: -orc) - - - LOCALIZATION: The following should not be localized: MSBuild, -isolate - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - For switch syntax, type "MSBuild -help" - For switch syntax, type "MSBuild -help" - UE: this message is shown when the user makes a syntax error on the command-line for a switch. - LOCALIZATION: "MSBuild -help" should not be localized. - - - -distributedLogger:<central logger>*<forwarding logger> - Use this logger to log events from MSBuild, attaching a - different logger instance to each node. To specify - multiple loggers, specify each logger separately. - (Short form -dl) - The <logger> syntax is: - [<class>,]<assembly>[,<options>][;<parameters>] - The <logger class> syntax is: - [<partial or full namespace>.]<logger class name> - The <logger assembly> syntax is: - {<assembly name>[,<strong name>] | <assembly file>} - Logger options specify how MSBuild creates the logger. - The <logger parameters> are optional, and are passed - to the logger exactly as you typed them. (Short form: -l) - Examples: - -dl:XMLLogger,MyLogger,Version=1.0.2,Culture=neutral - -dl:MyLogger,C:\My.dll*ForwardingLogger,C:\Logger.dll - - -distributedLogger:<central logger>*<forwarding logger> - Use this logger to log events from MSBuild, attaching a - different logger instance to each node. To specify - multiple loggers, specify each logger separately. - (Short form -dl) - The <logger> syntax is: - [<class>,]<assembly>[,<options>][;<parameters>] - The <logger class> syntax is: - [<partial or full namespace>.]<logger class name> - The <logger assembly> syntax is: - {<assembly name>[,<strong name>] | <assembly file>} - Logger options specify how MSBuild creates the logger. - The <logger parameters> are optional, and are passed - to the logger exactly as you typed them. (Short form: -l) - Examples: - -dl:XMLLogger,MyLogger,Version=1.0.2,Culture=neutral - -dl:MyLogger,C:\My.dll*ForwardingLogger,C:\Logger.dll - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg chars. - - - - -ignoreProjectExtensions:<extensions> - List of extensions to ignore when determining which - project file to build. Use a semicolon or a comma - to separate multiple extensions. - (Short form: -ignore) - Example: - -ignoreProjectExtensions:.sln - - -ignoreProjectExtensions:<extensions> - List of extensions to ignore when determining which - project file to build. Use a semicolon or a comma - to separate multiple extensions. - (Short form: -ignore) - Example: - -ignoreProjectExtensions:.sln - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -toolsVersion:<version> - The version of the MSBuild Toolset (tasks, targets, etc.) - to use during build. This version will override the - versions specified by individual projects. (Short form: - -tv) - Example: - -toolsVersion:3.5 - - -toolsVersion:<version> - The version of the MSBuild Toolset (tasks, targets, etc.) - to use during build. This version will override the - versions specified by individual projects. (Short form: - -tv) - Example: - -toolsVersion:3.5 - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -fileLogger[n] Logs the build output to a file. By default - the file is in the current directory and named - "msbuild[n].log". Events from all nodes are combined into - a single log. The location of the file and other - parameters for the fileLogger can be specified through - the addition of the "-fileLoggerParameters[n]" switch. - "n" if present can be a digit from 1-9, allowing up to - 10 file loggers to be attached. (Short form: -fl[n]) - - -fileLogger[n] Logs the build output to a file. By default - the file is in the current directory and named - "msbuild[n].log". Events from all nodes are combined into - a single log. The location of the file and other - parameters for the fileLogger can be specified through - the addition of the "-fileLoggerParameters[n]" switch. - "n" if present can be a digit from 1-9, allowing up to - 10 file loggers to be attached. (Short form: -fl[n]) - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -distributedFileLogger - Logs the build output to multiple log files, one log file - per MSBuild node. The initial location for these files is - the current directory. By default the files are called - "MSBuild<nodeid>.log". The location of the files and - other parameters for the fileLogger can be specified - with the addition of the "-fileLoggerParameters" switch. - - If a log file name is set through the fileLoggerParameters - switch the distributed logger will use the fileName as a - template and append the node id to this fileName to - create a log file for each node. - - -distributedFileLogger - Logs the build output to multiple log files, one log file - per MSBuild node. The initial location for these files is - the current directory. By default the files are called - "MSBuild<nodeid>.log". The location of the files and - other parameters for the fileLogger can be specified - with the addition of the "-fileLoggerParameters" switch. - - If a log file name is set through the fileLoggerParameters - switch the distributed logger will use the fileName as a - template and append the node id to this fileName to - create a log file for each node. - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -fileLoggerParameters[n]:<parameters> - Provides any extra parameters for file loggers. - The presence of this switch implies the - corresponding -fileLogger[n] switch. - "n" if present can be a digit from 1-9. - -fileLoggerParameters is also used by any distributed - file logger, see description of -distributedFileLogger. - (Short form: -flp[n]) - The same parameters listed for the console logger are - available. Some additional available parameters are: - LogFile--path to the log file into which the - build log will be written. - Append--determines if the build log will be appended - to or overwrite the log file. Setting the - switch appends the build log to the log file; - Not setting the switch overwrites the - contents of an existing log file. - The default is not to append to the log file. - Encoding--specifies the encoding for the file, - for example, UTF-8, Unicode, or ASCII - Default verbosity is Detailed. - Examples: - -fileLoggerParameters:LogFile=MyLog.log;Append; - Verbosity=diagnostic;Encoding=UTF-8 - - -flp:Summary;Verbosity=minimal;LogFile=msbuild.sum - -flp1:warningsonly;logfile=msbuild.wrn - -flp2:errorsonly;logfile=msbuild.err - - -fileLoggerParameters[n]:<parameters> - Provides any extra parameters for file loggers. - The presence of this switch implies the - corresponding -fileLogger[n] switch. - "n" if present can be a digit from 1-9. - -fileLoggerParameters is also used by any distributed - file logger, see description of -distributedFileLogger. - (Short form: -flp[n]) - The same parameters listed for the console logger are - available. Some additional available parameters are: - LogFile--path to the log file into which the - build log will be written. - Append--determines if the build log will be appended - to or overwrite the log file. Setting the - switch appends the build log to the log file; - Not setting the switch overwrites the - contents of an existing log file. - The default is not to append to the log file. - Encoding--specifies the encoding for the file, - for example, UTF-8, Unicode, or ASCII - Default verbosity is Detailed. - Examples: - -fileLoggerParameters:LogFile=MyLog.log;Append; - Verbosity=diagnostic;Encoding=UTF-8 - - -flp:Summary;Verbosity=minimal;LogFile=msbuild.sum - -flp1:warningsonly;logfile=msbuild.wrn - -flp2:errorsonly;logfile=msbuild.err - - - LOCALIZATION: The following should not be localized: - 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" - 2) the string "proj" that describes the extension we look for - 3) all switch names and their short forms e.g. -property, or -p - 4) all verbosity levels and their short forms e.g. quiet, or q - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -nodeReuse:<parameters> - Enables or Disables the reuse of MSBuild nodes. - The parameters are: - True --Nodes will remain after the build completes - and will be reused by subsequent builds (default) - False--Nodes will not remain after the build completes - (Short form: -nr) - Example: - -nr:true - - -nodeReuse:<parameters> - Enables or Disables the reuse of MSBuild nodes. - The parameters are: - True --Nodes will remain after the build completes - and will be reused by subsequent builds (default) - False--Nodes will not remain after the build completes - (Short form: -nr) - Example: - -nr:true - - - - - -preprocess[:file] - Creates a single, aggregated project file by - inlining all the files that would be imported during a - build, with their boundaries marked. This can be - useful for figuring out what files are being imported - and from where, and what they will contribute to - the build. By default the output is written to - the console window. If the path to an output file - is provided that will be used instead. - (Short form: -pp) - Example: - -pp:out.txt - - -preprocess[:file] - Creates a single, aggregated project file by - inlining all the files that would be imported during a - build, with their boundaries marked. This can be - useful for figuring out what files are being imported - and from where, and what they will contribute to - the build. By default the output is written to - the console window. If the path to an output file - is provided that will be used instead. - (Short form: -pp) - Example: - -pp:out.txt - - - - - -detailedSummary[:True|False] - Shows detailed information at the end of the build - about the configurations built and how they were - scheduled to nodes. - (Short form: -ds) - - -detailedSummary[:True|False] - Shows detailed information at the end of the build - about the configurations built and how they were - scheduled to nodes. - (Short form: -ds) - - - LOCALIZATION: "detailedSummary", "True" and "False" should not be localized. - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -warnAsError[:code[;code2]] - List of warning codes to treats as errors. Use a semicolon - or a comma to separate multiple warning codes. To treat all - warnings as errors use the switch with no values. - (Short form: -err[:c;[c2]]) - - Example: - -warnAsError:MSB4130 - - When a warning is treated as an error the target will - continue to execute as if it was a warning but the overall - build will fail. - - -warnAsError[:code[;code2]] - List of warning codes to treats as errors. Use a semicolon - or a comma to separate multiple warning codes. To treat all - warnings as errors use the switch with no values. - (Short form: -err[:c;[c2]]) - - Example: - -warnAsError:MSB4130 - - When a warning is treated as an error the target will - continue to execute as if it was a warning but the overall - build will fail. - - - LOCALIZATION: "-warnAsError" and "-err" should not be localized. - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -warnAsMessage[:code[;code2]] - List of warning codes to treats as low importance - messages. Use a semicolon or a comma to separate - multiple warning codes. - (Short form: -noWarn[:c;[c2]]) - - Example: - -warnAsMessage:MSB3026 - - -warnAsMessage[:code[;code2]] - List of warning codes to treats as low importance - messages. Use a semicolon or a comma to separate - multiple warning codes. - (Short form: -noWarn[:c;[c2]]) - - Example: - -warnAsMessage:MSB3026 - - - LOCALIZATION: "-warnAsMessage" and "-noWarn" should not be localized. - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -binaryLogger[:[LogFile=]output.binlog[;ProjectImports={None,Embed,ZipFile}]] - Serializes all build events to a compressed binary file. - By default the file is in the current directory and named - "msbuild.binlog". The binary log is a detailed description - of the build process that can later be used to reconstruct - text logs and used by other analysis tools. A binary log - is usually 10-20x smaller than the most detailed text - diagnostic-level log, but it contains more information. - (Short form: -bl) - - The binary logger by default collects the source text of - project files, including all imported projects and target - files encountered during the build. The optional - ProjectImports switch controls this behavior: - - ProjectImports=None - Don't collect the project - imports. - ProjectImports=Embed - Embed project imports in the - log file. - ProjectImports=ZipFile - Save project files to - output.projectimports.zip - where output is the same name - as the binary log file name. - - The default setting for ProjectImports is Embed. - Note: the logger does not collect non-MSBuild source files - such as .cs, .cpp etc. - - A .binlog file can be "played back" by passing it to - msbuild.exe as an argument instead of a project/solution. - Other loggers will receive the information contained - in the log file as if the original build was happening. - You can read more about the binary log and its usages at: - https://aka.ms/msbuild/binlog - - Examples: - -bl - -bl:output.binlog - -bl:output.binlog;ProjectImports=None - -bl:output.binlog;ProjectImports=ZipFile - -bl:..\..\custom.binlog - -binaryLogger - - -binaryLogger[:[LogFile=]output.binlog[;ProjectImports={None,Embed,ZipFile}]] - Serializes all build events to a compressed binary file. - By default the file is in the current directory and named - "msbuild.binlog". The binary log is a detailed description - of the build process that can later be used to reconstruct - text logs and used by other analysis tools. A binary log - is usually 10-20x smaller than the most detailed text - diagnostic-level log, but it contains more information. - (Short form: -bl) - - The binary logger by default collects the source text of - project files, including all imported projects and target - files encountered during the build. The optional - ProjectImports switch controls this behavior: - - ProjectImports=None - Don't collect the project - imports. - ProjectImports=Embed - Embed project imports in the - log file. - ProjectImports=ZipFile - Save project files to - output.projectimports.zip - where output is the same name - as the binary log file name. - - The default setting for ProjectImports is Embed. - Note: the logger does not collect non-MSBuild source files - such as .cs, .cpp etc. - - A .binlog file can be "played back" by passing it to - msbuild.exe as an argument instead of a project/solution. - Other loggers will receive the information contained - in the log file as if the original build was happening. - You can read more about the binary log and its usages at: - https://aka.ms/msbuild/binlog - - Examples: - -bl - -bl:output.binlog - -bl:output.binlog;ProjectImports=None - -bl:output.binlog;ProjectImports=ZipFile - -bl:..\..\custom.binlog - -binaryLogger - - - LOCALIZATION: The following should not be localized: - 1) "msbuild" - 2) the string "binlog" that describes the file extension - 3) all switch names and their short forms e.g. -bl and -binaryLogger - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - -restore[:True|False] - Runs a target named Restore prior to building - other targets and ensures the build for these - targets uses the latest restored build logic. - This is useful when your project tree requires - packages to be restored before it can be built. - Specifying -restore is the same as specifying - -restore:True. Use the parameter to override - a value that comes from a response file. - (Short form: -r) - - -restore[:True|False] - Runs a target named Restore prior to building - other targets and ensures the build for these - targets uses the latest restored build logic. - This is useful when your project tree requires - packages to be restored before it can be built. - Specifying -restore is the same as specifying - -restore:True. Use the parameter to override - a value that comes from a response file. - (Short form: -r) - - - LOCALIZATION: "-restore" and "-r" should not be localized. - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - MSBUILD : Configuration error MSB1043: The application could not start. {0} - MSBUILD : Configuration error MSB1043: The application could not start. {0} - - {StrBegin="MSBUILD : Configuration error MSB1043: "} - UE: This error is shown when the msbuild.exe.config file had invalid content. - LOCALIZATION: The prefix "MSBUILD : Configuration error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1061: Detailed summary value is not valid. {0} - MSBUILD : error MSB1061: Detailed summary value is not valid. {0} - - {StrBegin="MSBUILD : error MSB1061: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies a value for the -detailedSummary parameter that is not equivalent to Boolean.TrueString or Boolean.FalseString. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1057: Graph build value is not valid. - MSBUILD : error MSB1057: Graph build value is not valid. - - {StrBegin="MSBUILD : error MSB1057: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies a value for the -graphBuild parameter that is not equivalent to Boolean.TrueString or Boolean.FalseString. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1055: Interactive value is not valid. {0} - MSBUILD : error MSB1055: Interactive value is not valid. {0} - - {StrBegin="MSBUILD : error MSB1055: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies a value for the interactive parameter that is not equivalent to Boolean.TrueString or Boolean.FalseString. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1056: Isolate projects value is not valid. {0} - MSBUILD : error MSB1056: Isolate projects value is not valid. {0} - - {StrBegin="MSBUILD : error MSB1056: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies a value for the -isolateProjects parameter that is not equivalent to Boolean.TrueString or Boolean.FalseString. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1019: Logger switch was not correctly formed. - MSBUILD : error MSB1019: Logger switch was not correctly formed. - {StrBegin="MSBUILD : error MSB1019: "}UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user does any of the following: - msbuild.exe -logger:;"logger parameters" (missing logger class and assembly) - msbuild.exe -logger:loggerclass, (missing logger assembly) - msbuild.exe -logger:loggerclass,;"logger parameters" (missing logger assembly) - The correct way to specify a logger is to give both the logger class and logger assembly, or just the logger assembly (logger - parameters are optional). - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - MSBUILD : error MSB1030: Maximum CPU count is not valid. {0} - MSBUILD : error MSB1030: Maximum CPU count is not valid. {0} - - {StrBegin="MSBUILD : error MSB1030: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies an invalid CPU value. For example, -m:foo instead of -m:2. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1032: Maximum CPU count is not valid. Value must be an integer greater than zero and no more than 1024. - MSBUILD : error MSB1032: Maximum CPU count is not valid. Value must be an integer greater than zero and no more than 1024. - {StrBegin="MSBUILD : error MSB1032: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies a CPU value that is zero or less. For example, -m:0 instead of -m:2. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1033: Node number is not valid. {0}. - MSBUILD : error MSB1033: Node number is not valid. {0}. - - {StrBegin="MSBUILD : error MSB1033: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies a CPU value that is zero or less. For example, -nodeMode:foo instead of -nodeMode:2. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1034: Node number is not valid. Value must be an integer greater than zero. - MSBUILD : error MSB1034: Node number is not valid. Value must be an integer greater than zero. - {StrBegin="MSBUILD : error MSB1034: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies a CPU value that is zero or less. For example, -nodeMode:0 instead of -nodeMode:2. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1006: Property is not valid. - MSBUILD : error MSB1006: Property is not valid. - - {StrBegin="MSBUILD : error MSB1006: "}UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown if the user does any of the following: - msbuild.exe -property:foo (missing property value) - msbuild.exe -property:=4 (missing property name) - The user must pass in an actual property name and value following the switch, as in "msbuild.exe -property:Configuration=Debug". - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : MSB1046: The schema "{0}" is not valid. {1} - MSBUILD : MSB1046: The schema "{0}" is not valid. {1} - {StrBegin="MSBUILD : MSB1046: "}UE: This message is shown when the schema file provided for the validation of a project is itself not valid. - LOCALIZATION: "{0}" is the schema file path. "{1}" is a message from an FX exception that describes why the schema file is bad. - - - Switch: {0} - Switch: {0} - - UE: This is attached to error messages caused by an invalid switch. This message indicates what the invalid arg was. - For example, if an unknown switch is passed to MSBuild.exe, the error message will look like this: - MSBUILD : error MSB1001: Unknown switch. - Switch: -bogus - LOCALIZATION: {0} contains the invalid switch text. - - - - MSBUILD : error MSB1040: ToolsVersion is not valid. {0} - MSBUILD : error MSB1040: ToolsVersion is not valid. {0} - - {StrBegin="MSBUILD : error MSB1040: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies an unknown toolversion, eg -toolsVersion:99 - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1018: Verbosity level is not valid. - MSBUILD : error MSB1018: Verbosity level is not valid. - - {StrBegin="MSBUILD : error MSB1018: "}UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies an unknown verbosity level e.g. "msbuild -verbosity:foo". The only valid verbosities - (and their short forms) are: q[uiet], m[inimal], n[ormal], d[etailed], diag[nostic]. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1028: The logger failed unexpectedly. - MSBUILD : error MSB1028: The logger failed unexpectedly. - {StrBegin="MSBUILD : error MSB1028: "} - UE: This error is shown when a logger specified with the -logger switch throws an exception while being - initialized. This message is followed by the exception text including the stack trace. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - MSBUILD : Logger error {0}: {1} - MSBUILD : Logger error {0}: {1} - UE: This prefixes the error message emitted by a logger, when a logger fails in a controlled way using a LoggerException. - For example, the logger is indicating that it could not create its output file. - There's no error code because one was supplied by the logger. - LOCALIZATION: The word "Logger" should be localized, the words "MSBuild" and "error" should NOT be localized. - - - - MSBUILD : Logger error MSB1029: {0} - MSBUILD : Logger error MSB1029: {0} - {SubString="Logger", "{0}"}{StrBegin="MSBUILD : "} - UE: This prefixes the error message emitted by a logger, when a logger fails in a controlled way using a LoggerException. - For example, the logger is indicating that it could not create its output file. - This is like LoggerFailurePrefixNoErrorCode, but the logger didn't supply its own error code, so we have to provide one. - LOCALIZATION: The word "Logger" should be localized, the words "MSBuild" and "error" should NOT be localized. - - - - MSBuild executable path = "{0}" - MSBuild executable path = "{0}" - - - - MSBuild version = "{0}" - MSBuild version = "{0}" - - - - MSBUILD : error MSB1007: Specify a logger. - MSBUILD : error MSB1007: Specify a logger. - - {StrBegin="MSBUILD : error MSB1007: "}UE: This happens if the user does something like "msbuild.exe -logger". The user must pass in an actual logger class - following the switch, as in "msbuild.exe -logger:XMLLogger,MyLogger,Version=1.0.2,Culture=neutral". - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1031: Specify the maximum number of CPUs. - MSBUILD : error MSB1031: Specify the maximum number of CPUs. - - {StrBegin="MSBUILD : error MSB1031: "}UE: This happens if the user does something like "msbuild.exe -m". The user must pass in an actual number like -m:4. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1003: Specify a project or solution file. The current working directory does not contain a project or solution file. - MSBUILD : error MSB1003: Specify a project or solution file. The current working directory does not contain a project or solution file. - - {StrBegin="MSBUILD : error MSB1003: "}UE: The user must either specify a project or solution file to build, or there must be a project file in the current directory - with a file extension ending in "proj" (e.g., foo.csproj), or a solution file ending in "sln". - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1005: Specify a property and its value. - MSBUILD : error MSB1005: Specify a property and its value. - - {StrBegin="MSBUILD : error MSB1005: "}UE: This happens if the user does something like "msbuild.exe -property". The user must pass in an actual property - name and value following the switch, as in "msbuild.exe -property:Configuration=Debug". - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1012: Specify a response file. - MSBUILD : error MSB1012: Specify a response file. - - {StrBegin="MSBUILD : error MSB1012: "}UE: This error would occur if the user did something like "msbuild.exe @ foo.proj". The at-sign must be followed by a - response file. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1004: Specify the name of the target. - MSBUILD : error MSB1004: Specify the name of the target. - - {StrBegin="MSBUILD : error MSB1004: "}UE: This happens if the user does something like "msbuild.exe -target". The user must pass in an actual target name - following the switch, as in "msbuild.exe -target:blah". - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1039: Specify the version of the toolset. - MSBUILD : error MSB1039: Specify the version of the toolset. - - {StrBegin="MSBUILD : error MSB1039: "} - UE: This happens if the user does something like "msbuild.exe -toolsVersion". The user must pass in an actual toolsversion - name following the switch, as in "msbuild.exe -toolsVersion:3.5". - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1016: Specify the verbosity level. - MSBUILD : error MSB1016: Specify the verbosity level. - - {StrBegin="MSBUILD : error MSB1016: "}UE: This happens if the user does something like "msbuild.exe -verbosity". The user must pass in a verbosity level - after the switch e.g. "msbuild.exe -verbosity:detailed". - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1024: Only one schema can be specified for validation of the project. - MSBUILD : error MSB1024: Only one schema can be specified for validation of the project. - - {StrBegin="MSBUILD : error MSB1024: "}UE: The user did something like msbuild -validate:foo.xsd -validate:bar.xsd. We only allow one schema to be specified. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - The specified logger could not be created and will not be used. {0} - The specified logger could not be created and will not be used. {0} - - UE: This error is shown when a logger cannot be loaded and instantiated from its assembly. - LOCALIZATION: {0} contains the exception message explaining why the - logger could not be created -- this message comes from the CLR/FX and is localized. - - - - Some command line switches were read from the auto-response file "{0}". To disable this file, use the "-noAutoResponse" switch. - Some command line switches were read from the auto-response file "{0}". To disable this file, use the "-noAutoResponse" switch. - - UE: This message appears in high verbosity modes when we used some - switches from the auto-response file msbuild.rsp: otherwise the user may be unaware - where the switches are coming from. - - - - Process = "{0}" - Process = "{0}" - - - - MSBUILD : error MSB1009: Project file does not exist. - MSBUILD : error MSB1009: Project file does not exist. - {StrBegin="MSBUILD : error MSB1009: "}UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - Building the projects in this solution one at a time. To enable parallel build, please add the "-m" switch. - Building the projects in this solution one at a time. To enable parallel build, please add the "-m" switch. - - - - MSBUILD : MSB1045: Stopping because of syntax errors in project file. - MSBUILD : MSB1045: Stopping because of syntax errors in project file. - {StrBegin="MSBUILD : MSB1045: "} - - - MSBUILD : error MSB1023: Cannot read the response file. {0} - MSBUILD : error MSB1023: Cannot read the response file. {0} - {StrBegin="MSBUILD : error MSB1023: "}UE: This error is shown when the response file cannot be read off disk. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. {0} contains a localized message explaining - why the response file could not be read -- this message comes from the CLR/FX. - - - MSBUILD : error MSB1013: The response file was specified twice. A response file can be specified only once. Any files named "msbuild.rsp" in the directory of MSBuild.exe or in the directory of the first project or solution built (which if no project or solution is specified is the current working directory) were automatically used as response files. - MSBUILD : error MSB1013: The response file was specified twice. A response file can be specified only once. Any files named "msbuild.rsp" in the directory of MSBuild.exe or in the directory of the first project or solution built (which if no project or solution is specified is the current working directory) were automatically used as response files. - {StrBegin="MSBUILD : error MSB1013: "}UE: Response files are just text files that contain a bunch of command-line switches to be passed to MSBuild.exe. The - purpose is so you don't have to type the same switches over and over again ... you can just pass in the response file instead. - Response files can include the @ switch in order to further include other response files. In order to prevent a circular - reference here, we disallow the same response file from being included twice. This error message would be followed by the - exact @ switch that resulted in the duplicate response file. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - MSBUILD : error MSB1022: Response file does not exist. - MSBUILD : error MSB1022: Response file does not exist. - {StrBegin="MSBUILD : error MSB1022: "}UE: This message would show if the user did something like "msbuild @bogus.rsp" where bogus.rsp doesn't exist. This - message does not need in-line parameters because the exception takes care of displaying the invalid arg. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - Validating project using schema file "{0}". - Validating project using schema file "{0}". - LOCALIZATION: "{0}" is the location of the schema file. - - - MSBUILD : MSB1044: Project is not valid. {0} - MSBUILD : MSB1044: Project is not valid. {0} - {StrBegin="MSBUILD : MSB1044: "}UE: This error is shown when the user asks his project to be validated against a schema (-val switch for - MSBuild.exe), and the project has errors. "{0}" contains a message explaining the problem. - LOCALIZATION: "{0}" is a message from the System.XML schema validator and is already localized. - - - MSBUILD : error MSB1026: Schema file does not exist. - MSBUILD : error MSB1026: Schema file does not exist. - {StrBegin="MSBUILD : error MSB1026: "}UE: This error is shown when the user specifies a schema file using the -validate:<schema> switch, and the file - does not exist on disk. This message does not need in-line parameters because the exception takes care of displaying the - invalid arg. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - MSBUILD : error MSB1026: Schema file '{0}' does not exist. - MSBUILD : error MSB1026: Schema file '{0}' does not exist. - {StrBegin="MSBUILD : error MSB1026: "}UE: This error is printed if the default schema does not exist or in the extremely unlikely event - that an explicit schema file was passed and existed when the command line parameters were checked but was deleted from disk before this check was made. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - MSBUILD : error MSB1059: Targets could not be printed. {0} - MSBUILD : error MSB1059: Targets could not be printed. {0} - {StrBegin="MSBUILD : error MSB1059: "} - - - MSBUILD : error MSB1002: This switch does not take any parameters. - MSBUILD : error MSB1002: This switch does not take any parameters. - {StrBegin="MSBUILD : error MSB1002: "}UE: For example, if somebody types "msbuild.exe -noLogo:1", they would get this error because the -noLogo switch - should not be followed by any parameters ... it stands alone. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - MSBUILD : error MSB1001: Unknown switch. - MSBUILD : error MSB1001: Unknown switch. - {StrBegin="MSBUILD : error MSB1001: "}UE: This occurs when the user passes in an unrecognized switch on the MSBuild.exe command-line. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - MSBUILD : error MSB1015: MSBuild does not run on this version of the operating system. It is only supported on Windows 2000, Windows XP, and later versions. - MSBUILD : error MSB1015: MSBuild does not run on this version of the operating system. It is only supported on Windows 2000, Windows XP, and later versions. - {StrBegin="MSBUILD : error MSB1015: "}LOCALIZATION: The error prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - Forcing load of Microsoft.Build.Engine because MSBUILDOLDOM=1... - Forcing load of Microsoft.Build.Engine because MSBUILDOLDOM=1... - - - - MSBUILD : error MSB1035: Specify the project extensions to ignore. - MSBUILD : error MSB1035: Specify the project extensions to ignore. - {StrBegin="MSBUILD : error MSB1035: "} - UE: This happens if the user does something like "msbuild.exe -ignoreProjectExtensions". The user must pass in one or more - project extensions to ignore e.g. "msbuild.exe -ignoreProjectExtensions:.sln". - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1036: There is an invalid extension in the -ignoreProjectExtensions list. Extensions must start with a period ".", have one or more characters after the period and not contain any invalid path characters or wildcards. - MSBUILD : error MSB1036: There is an invalid extension in the -ignoreProjectExtensions list. Extensions must start with a period ".", have one or more characters after the period and not contain any invalid path characters or wildcards. - {StrBegin="MSBUILD : error MSB1036: "}LOCALIZATION: The error prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - MSBUILD : error MSB1037: Specify one or more parameters for the console logger if using the -consoleLoggerParameters switch - MSBUILD : error MSB1037: Specify one or more parameters for the console logger if using the -consoleLoggerParameters switch - {StrBegin="MSBUILD : error MSB1037: "} - UE: This happens if the user does something like "msbuild.exe -consoleLoggerParameters:". The user must pass in one or more parameters - after the switch e.g. "msbuild.exe -consoleLoggerParameters:ErrorSummary". - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1038: Specify one or more parameters for the file logger if using the -fileLoggerParameters switch - MSBUILD : error MSB1038: Specify one or more parameters for the file logger if using the -fileLoggerParameters switch - {StrBegin="MSBUILD : error MSB1038: "} - UE: This happens if the user does something like "msbuild.exe -fileLoggerParameters:". The user must pass in one or more parameters - after the switch e.g. "msbuild.exe -fileLoggerParameters:logfile=c:\temp\logfile". - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1041: Specify one or more parameters for node reuse if using the -nodeReuse switch - MSBUILD : error MSB1041: Specify one or more parameters for node reuse if using the -nodeReuse switch - {StrBegin="MSBUILD : error MSB1041: "} - UE: This happens if the user does something like "msbuild.exe -nodeReuse:" without a true or false - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1042: Node reuse value is not valid. {0}. - MSBUILD : error MSB1042: Node reuse value is not valid. {0}. - {StrBegin="MSBUILD : error MSB1042: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies a node reuse value that is not equivilant to Boolean.TrueString or Boolean.FalseString. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1042: Node reuse value is not valid. This version of MSBuild does not support node reuse. If specified, the node reuse switch value must be false. - MSBUILD : error MSB1042: Node reuse value is not valid. This version of MSBuild does not support node reuse. If specified, the node reuse switch value must be false. - {StrBegin="MSBUILD : error MSB1042: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies a node reuse value that is not equivalent to Boolean.TrueString or Boolean.FalseString. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB1052: Restore value is not valid. {0} - MSBUILD : error MSB1052: Restore value is not valid. {0} - {StrBegin="MSBUILD : error MSB1052: "} - UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies a restore value that is not equivalent to Boolean.TrueString or Boolean.FalseString. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - Attempting to cancel the build... - Attempting to cancel the build... - - - - MSBUILD : error MSB1047: File to preprocess to is not valid. {0} - MSBUILD : error MSB1047: File to preprocess to is not valid. {0} - {StrBegin="MSBUILD : error MSB1047: "} - - - MSBUILD : error MSB1021: Cannot create an instance of the logger. {0} - MSBUILD : error MSB1021: Cannot create an instance of the logger. {0} - {StrBegin="MSBUILD : error MSB1021: "} - UE: This error is shown when a logger cannot be loaded and instantiated from its assembly. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. {0} contains a message explaining why the - logger could not be created -- this message comes from the CLR/FX and is localized. - - - MSBUILD : error MSB1020: The logger was not found. Check the following: 1.) The logger name specified is the same as the name of the logger class. 2.) The logger class is "public" and implements the Microsoft.Build.Framework.ILogger interface. 3.) The path to the logger assembly is correct, or the logger can be loaded using only the assembly name provided. - MSBUILD : error MSB1020: The logger was not found. Check the following: 1.) The logger name specified is the same as the name of the logger class. 2.) The logger class is "public" and implements the Microsoft.Build.Framework.ILogger interface. 3.) The path to the logger assembly is correct, or the logger can be loaded using only the assembly name provided. - - {StrBegin="MSBUILD : error MSB1020: "}UE: This message does not need in-line parameters because the exception takes care of displaying the invalid arg. - This error is shown when a user specifies an logger that does not exist e.g. "msbuild -logger:FooLoggerClass,FooAssembly". The - logger class must exist in the given assembly. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - MSBUILD : error MSB4192: The project file "{0}" is in the ".vcproj" or ".dsp" file format, which MSBuild cannot build directly. Please convert the project by opening it in the Visual Studio IDE or running the conversion tool, or, for ".vcproj", use MSBuild to build the solution file containing the project instead. - MSBUILD : error MSB4192: The project file "{0}" is in the ".vcproj" or ".dsp" file format, which MSBuild cannot build directly. Please convert the project by opening it in the Visual Studio IDE or running the conversion tool, or, for ".vcproj", use MSBuild to build the solution file containing the project instead. - {StrBegin="MSBUILD : error MSB4192: "} LOC: ".vcproj" and ".dsp" should not be localized - - - If MSBuild debugging does not work correctly, please verify that the "Just My Code" feature is enabled in Visual Studio, and that you have selected the managed debugger. - If MSBuild debugging does not work correctly, please verify that the "Just My Code" feature is enabled in Visual Studio, and that you have selected the managed debugger. - - - - MSBUILD : error MSB1048: Solution files cannot be debugged directly. Run MSBuild first with an environment variable MSBUILDEMITSOLUTION=1 to create a corresponding ".sln.metaproj" file. Then debug that. - MSBUILD : error MSB1048: Solution files cannot be debugged directly. Run MSBuild first with an environment variable MSBUILDEMITSOLUTION=1 to create a corresponding ".sln.metaproj" file. Then debug that. - {StrBegin="MSBUILD : error MSB1048: "} LOC: ".SLN" should not be localized - - - MSBUILD : error MSB1049: The {0} parameter must be specified - MSBUILD : error MSB1049: The {0} parameter must be specified - {StrBegin="MSBUILD : error MSB1049: "} - - - Build started. - Build started. - - - - {0} ({1},{2}) - {0} ({1},{2}) - A file location to be embedded in a string. - - - MSBUILD : error MSB1051: Specify one or more warning codes to treat as low importance messages when using the -warnAsMessage switch. - MSBUILD : error MSB1051: Specify one or more warning codes to treat as low importance messages when using the -warnAsMessage switch. - - {StrBegin="MSBUILD : error MSB1051: "} - UE: This happens if the user does something like "msbuild.exe -warnAsMessage:" without any codes. - LOCALIZATION: The prefix "MSBUILD : error MSBxxxx:" should not be localized. - - - - -profileEvaluation:<file> - Profiles MSBuild evaluation and writes the result - to the specified file. If the extension of the specified - file is '.md', the result is generated in markdown - format. Otherwise, a tab separated file is produced. - - -profileEvaluation:<file> - Profiles MSBuild evaluation and writes the result - to the specified file. If the extension of the specified - file is '.md', the result is generated in markdown - format. Otherwise, a tab separated file is produced. - - - - - -restoreProperty:<n>=<v> - Set or override these project-level properties only - during restore and do not use properties specified - with the -property argument. <n> is the property - name, and <v> is the property value. Use a - semicolon or a comma to separate multiple properties, - or specify each property separately. - (Short form: -rp) - Example: - -restoreProperty:IsRestore=true;MyProperty=value - - -restoreProperty:<n>=<v> - Set or override these project-level properties only - during restore and do not use properties specified - with the -property argument. <n> is the property - name, and <v> is the property value. Use a - semicolon or a comma to separate multiple properties, - or specify each property separately. - (Short form: -rp) - Example: - -restoreProperty:IsRestore=true;MyProperty=value - - - LOCALIZATION: "-restoreProperty" and "-rp" should not be localized. - LOCALIZATION: None of the lines should be longer than a standard width console window, eg 80 chars. - - - - MSBUILD : error MSB1053: Provided filename is not valid. {0} - MSBUILD : error MSB1053: Provided filename is not valid. {0} - - - - MSBUILD :error MSB1054: A filename must be specified to generate the profiler result. - MSBUILD :error MSB1054: A filename must be specified to generate the profiler result. - - - - - \ No newline at end of file diff --git a/src/MSBuild/Resources/xlf/Strings.es.xlf b/src/MSBuild/Resources/xlf/Strings.es.xlf index 4b0ad7be159..e36eec845fb 100644 --- a/src/MSBuild/Resources/xlf/Strings.es.xlf +++ b/src/MSBuild/Resources/xlf/Strings.es.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/MSBuild/Resources/xlf/Strings.fr.xlf b/src/MSBuild/Resources/xlf/Strings.fr.xlf index 6c40786a49a..ecd04ae29ab 100644 --- a/src/MSBuild/Resources/xlf/Strings.fr.xlf +++ b/src/MSBuild/Resources/xlf/Strings.fr.xlf @@ -1,4 +1,4 @@ - + @@ -844,13 +844,12 @@ Copyright (C) Microsoft Corporation. Tous droits réservés. Paramètres supplémentaires pour les journaliseurs de fichiers. La présence de ce commutateur implique l'utilisation du commutateur -fileLogger[n] correspondant. - S'il est spécifié, "n" doit être un chiffre entre 1 et 9. + S'il est spécifié, "n" doit être un chiffre entre 1 et 9. -fileLoggerParameters est également utilisé par tous les - journaliseurs vers des fichiers. Consultez la description - de -distributedFileLogger. - (Forme abrégée : -flp[n]) + journaliseurs vers des fichiers. Consultez la description de -distributedFileLogger. + (Forme abrégée : -flp[n]) Les mêmes paramètres que ceux listés pour le journaliseur de la - console sont disponibles. Paramètres supplémentaires disponibles : + console sont disponibles. Paramètres supplémentaires disponibles : LogFile--Chemin du fichier journal dans lequel le journal de génération est écrit. Append--Détermine si le journal de génération est ajouté @@ -862,7 +861,7 @@ Copyright (C) Microsoft Corporation. Tous droits réservés. Encoding--Spécifie l'encodage du fichier, par exemple, UTF-8, Unicode ou ASCII Le niveau de détail par défaut est Detailed. - Exemples : + Exemples : -fileLoggerParameters:LogFile=MyLog.log;Append; Verbosity=diagnostic;Encoding=UTF-8 diff --git a/src/MSBuild/Resources/xlf/Strings.it.xlf b/src/MSBuild/Resources/xlf/Strings.it.xlf index 6ebbdf7964f..c37e60c4dc4 100644 --- a/src/MSBuild/Resources/xlf/Strings.it.xlf +++ b/src/MSBuild/Resources/xlf/Strings.it.xlf @@ -1,4 +1,4 @@ - + @@ -315,7 +315,7 @@ Copyright (C) Microsoft Corporation. Tutti i diritti sono riservati. @<file> Inserisce le impostazioni della riga di comando da un file di testo. Per specificare più file di risposta, specificare ciascun file separatamente. - + Qualsiasi file di risposta denominato "msbuild.rsp" viene usato automaticamente dai percorsi seguenti: (1) la directory di msbuild.exe @@ -815,6 +815,7 @@ Copyright (C) Microsoft Corporation. Tutti i diritti sono riservati. file viene assegnato il nome "MSBuild<idnodo>.log". Il percorso dei file e altri parametri di fileLogger possono essere specificati aggiungendo l'opzione + "-fileLoggerParameters". Se il nome di un file di log viene impostato con l'opzione fileLoggerParameters, il logger distribuito userà il nome @@ -1685,7 +1686,8 @@ Copyright (C) Microsoft Corporation. Tutti i diritti sono riservati. file is '.md', the result is generated in markdown format. Otherwise, a tab separated file is produced. - -profileEvaluation:<file> Esegue la profilatura della valutazione di MSBuild e scrive + -profileEvaluation:<file> +Esegue la profilatura della valutazione di MSBuild e scrive il risultato nel file specificato. Se l'estensione del file specificato è '.md', il risultato viene generato in formato Markdown. In caso contrario, viene prodotto un file diff --git a/src/MSBuild/Resources/xlf/Strings.ja.xlf b/src/MSBuild/Resources/xlf/Strings.ja.xlf index 089b3db5c9e..58adc2124db 100644 --- a/src/MSBuild/Resources/xlf/Strings.ja.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ja.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/MSBuild/Resources/xlf/Strings.ko.xlf b/src/MSBuild/Resources/xlf/Strings.ko.xlf index 2d320eb0ef0..4e641e8d1e0 100644 --- a/src/MSBuild/Resources/xlf/Strings.ko.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ko.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/MSBuild/Resources/xlf/Strings.pl.xlf b/src/MSBuild/Resources/xlf/Strings.pl.xlf index c14e0ffa53d..548f0673c44 100644 --- a/src/MSBuild/Resources/xlf/Strings.pl.xlf +++ b/src/MSBuild/Resources/xlf/Strings.pl.xlf @@ -1,4 +1,4 @@ - + @@ -412,7 +412,7 @@ Copyright (C) Microsoft Corporation. Wszelkie prawa zastrzeżone. [<klasa rejestratora>,]<zestaw rejestratora> [;<parametry rejestratora>] Składnia elementu <klasa rejestratora>: - <częściowa lub pełna przestrzeń nazw>.] + [<częściowa lub pełna przestrzeń nazw>.] <nazwa klasy rejestratora> Składnia elementu <zestaw rejestratora>: {<nazwa zestawu>[,<strong name>] | <plik zestawu>} @@ -807,6 +807,7 @@ Copyright (C) Microsoft Corporation. Wszelkie prawa zastrzeżone. Domyślnie pliki mają nazwę „MSBuild<identyfikator węzła>.log”. Lokalizację plików i inne parametry rejestratora plików można określić + przez dodanie przełącznika „-fileLoggerParameters”. Jeśli nazwa pliku zostanie ustawiona za pomocą przełącznika @@ -1641,7 +1642,7 @@ dzienników tekstowych i wykorzystać w innych narzędziach przywrócenia pakietów przed ich skompilowaniem. Podanie parametru -restore jest równoznaczne z podaniem parametru -restore:True. Za pomocą tego parametru można przesłonić wartość pochodzącą - z pliku odpowiedzi. + z pliku odpowiedzi. (Krótka forma: -r) diff --git a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf index 751fc9b422d..e41b484c1a6 100644 --- a/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf +++ b/src/MSBuild/Resources/xlf/Strings.pt-BR.xlf @@ -1,4 +1,4 @@ - + @@ -1623,9 +1623,12 @@ isoladamente. -restore[:True|False] Executa um destino chamado Restore antes de compilar - outros destinos e garante o build desses   destinos usando uma lógica de build restaurada. Isso é útil quando sua árvore de -projeto precisar      que pacotes sejam restaurados antes de serem compilados.          Especificar -restore é o mesmo que - especificar -restore:True. Use o parâmetro para + outros destinos e garante o build desses + destinos usando uma lógica de build restaurada. + Isso é útil quando sua árvore de projeto precisar + que pacotes sejam restaurados antes de serem compilados. + Especificar -restore é o mesmo que + -restore:True. Use o parâmetro para substituir um valor originado de um arquivo de resposta. (Forma abreviada: -r) @@ -1707,4 +1710,4 @@ projeto precisar      - + \ No newline at end of file diff --git a/src/MSBuild/Resources/xlf/Strings.ru.xlf b/src/MSBuild/Resources/xlf/Strings.ru.xlf index 75212bac50f..4ef8b52a05a 100644 --- a/src/MSBuild/Resources/xlf/Strings.ru.xlf +++ b/src/MSBuild/Resources/xlf/Strings.ru.xlf @@ -1,4 +1,4 @@ - + @@ -366,7 +366,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. -property:WarningLevel=2;OutDir=bin\Debug\ -property:<n>=<v> Установите или переопределите свойства уровня проекта. <n> является - именем свойства, а <v> — его значением. Используйте + именем свойства, а <v> — его значением. Используйте точку с запятой или запятую, чтобы разделить несколько свойств, или укажите каждое свойство отдельно. (Краткая форма: -p) Пример: @@ -572,12 +572,11 @@ Copyright (C) Microsoft Corporation. All rights reserved. MSBuild will use up to the number of processors on the computer. (Short form: -m[:n]) - -maxCpuCount[:n] Указывает максимальное количество - параллельных процессов сборки. Если ключ - не используется, применяется значение по умолчанию 1. - Если ключ используется без значения, MSBuild - будет использовать то количество процессоров, - которое установлено на компьютере. (Краткая форма: -m[:n]) + -maxCpuCount[:n] Указывает максимальное количество параллельных + процессов сборки. Если ключ не используется, применяется значение + по умолчанию 1. Если ключ используется без значения, + MSBuild будет использовать то количество процессоров, которое установлено на + компьютере. (Краткая форма: -m[:n]) LOCALIZATION: "maxCpuCount" should not be localized. diff --git a/src/MSBuild/Resources/xlf/Strings.tr.xlf b/src/MSBuild/Resources/xlf/Strings.tr.xlf index f8ed2f2d849..8bed4f53eff 100644 --- a/src/MSBuild/Resources/xlf/Strings.tr.xlf +++ b/src/MSBuild/Resources/xlf/Strings.tr.xlf @@ -1,4 +1,4 @@ - + @@ -506,7 +506,7 @@ Telif Hakkı (C) Microsoft Corporation. Tüm hakları saklıdır. devre dışı bırak. EnableMPLogging--Çok işlemci olmayan modda çalışırken bile çok işlemcili günlük stilini etkinleştir. Bu - günlük stili varsayılan olarak açıktır. + günlük stili varsayılan olarak açıktır. ForceConsoleColor--Konsol desteklemese bile ANSI konsol renklerini kullan Verbosity--Bu günlükçü için /verbosity ayarını @@ -789,7 +789,7 @@ Telif Hakkı (C) Microsoft Corporation. Tüm hakları saklıdır. template and append the node id to this fileName to create a log file for each node. - -distributedFileLogger + -distributedFileLogger Derleme çıkışını, her MSBuild düğümü için bir günlük dosyası olmak üzere birden çok günlük dosyasına kaydeder. Bu dosyaların ilk konumu geçerli dizindir. Dosyaların @@ -1631,7 +1631,7 @@ Telif Hakkı (C) Microsoft Corporation. Tüm hakları saklıdır. Bu, proje ağacınızın paketlerin oluşturulabilmesi için önce geri yüklenmesini gerektirdiği durumlarda yararlıdır. -restore değerinin belirtilmesi, -restore:True değerinin - belirtilmesiyle aynıdır. Yanıt dosyasından gelen bir değeri + belirtilmesiyle aynıdır. Yanıt dosyasından gelen bir değeri geçersiz kılmak için parametreyi kullanın. (Kısa biçim: -r) diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf index bf57d51080e..011389fa1a6 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hans.xlf @@ -1,4 +1,4 @@ - + @@ -800,7 +800,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. 开关设置的,分布式记录器将使用 fileName 作为 模板并将节点 ID 附加到此 fileName 以便为每个节点创建一个日志文件。 - + LOCALIZATION: The following should not be localized: 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" @@ -868,7 +868,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. -flp:Summary;Verbosity=minimal;LogFile=msbuild.sum -flp1:warningsonly;logfile=msbuild.wrn -flp2:errorsonly;logfile=msbuild.err - + LOCALIZATION: The following should not be localized: 1) "MSBuild", "MSBuild.exe" and "MSBuild.rsp" diff --git a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf index 066da1aa35f..a3b1e39cf03 100644 --- a/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf +++ b/src/MSBuild/Resources/xlf/Strings.zh-Hant.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/MSBuild/XMake.cs b/src/MSBuild/XMake.cs index 8c3d67a3593..118590c7242 100644 --- a/src/MSBuild/XMake.cs +++ b/src/MSBuild/XMake.cs @@ -3142,7 +3142,9 @@ private static void ProcessFileLoggers(string[][] groupedFileLoggerParameters, L LoggerVerbosity defaultFileLoggerVerbosity = LoggerVerbosity.Detailed; fileLogger.Verbosity = defaultFileLoggerVerbosity; - if (cpuCount == 1) + // Check to see if there is a possibility we will be logging from an out-of-proc node. + // If so (we're multi-proc or the in-proc node is disabled), we register a distributed logger. + if (cpuCount == 1 && Environment.GetEnvironmentVariable("MSBUILDNOINPROCNODE") != "1") { // We've decided to use the MP logger even in single proc mode. // Switch it on here, rather than in the logger, so that other hosts that use @@ -3206,7 +3208,9 @@ List loggers consoleParameters = AggregateParameters(consoleParameters, consoleLoggerParameters); } - if (cpuCount == 1) + // Check to see if there is a possibility we will be logging from an out-of-proc node. + // If so (we're multi-proc or the in-proc node is disabled), we register a distributed logger. + if (cpuCount == 1 && Environment.GetEnvironmentVariable("MSBUILDNOINPROCNODE") != "1") { // We've decided to use the MP logger even in single proc mode. // Switch it on here, rather than in the logger, so that other hosts that use diff --git a/src/MSBuild/app.amd64.config b/src/MSBuild/app.amd64.config index 9cdcd0cf454..4cea82e6473 100644 --- a/src/MSBuild/app.amd64.config +++ b/src/MSBuild/app.amd64.config @@ -10,7 +10,11 @@ - + + + @@ -46,11 +50,6 @@ - - - - - @@ -69,6 +68,7 @@ + @@ -92,11 +92,11 @@ - + - + @@ -104,11 +104,14 @@ - + - + + @@ -120,25 +123,25 @@ - + - + - + - + - + @@ -166,7 +169,7 @@ - + diff --git a/src/MSBuild/app.config b/src/MSBuild/app.config index 66212ab9f4b..2f3d03110b7 100644 --- a/src/MSBuild/app.config +++ b/src/MSBuild/app.config @@ -10,6 +10,7 @@ + @@ -82,11 +83,11 @@ - + - + @@ -94,11 +95,14 @@ - + - + + @@ -110,32 +114,27 @@ - - + + - - - - - - - + + - + - + - + @@ -149,6 +148,14 @@ + + + + + + + + diff --git a/src/MSBuildTaskHost/FileSystem/MSBuildTaskHostFileSystem.cs b/src/MSBuildTaskHost/FileSystem/MSBuildTaskHostFileSystem.cs index 103061df36c..14949660eac 100644 --- a/src/MSBuildTaskHost/FileSystem/MSBuildTaskHostFileSystem.cs +++ b/src/MSBuildTaskHost/FileSystem/MSBuildTaskHostFileSystem.cs @@ -16,7 +16,7 @@ internal class MSBuildTaskHostFileSystem : IFileSystem public static MSBuildTaskHostFileSystem Singleton() => Instance; - public bool DirectoryEntryExists(string path) + public bool FileOrDirectoryExists(string path) { return NativeMethodsShared.FileOrDirectoryExists(path); } diff --git a/src/MSBuildTaskHost/MSBuildTaskHost.csproj b/src/MSBuildTaskHost/MSBuildTaskHost.csproj index 37d0b1c70d6..66f5bc78f00 100644 --- a/src/MSBuildTaskHost/MSBuildTaskHost.csproj +++ b/src/MSBuildTaskHost/MSBuildTaskHost.csproj @@ -13,7 +13,7 @@ - win7-x86;win7-x64 + win7-x86;win7-x64 false $(DefineConstants);CLR2COMPATIBILITY;TASKHOST @@ -204,7 +204,6 @@ - diff --git a/src/Package/Localization/Localization.csproj b/src/Package/Localization/Localization.csproj index 0326009105b..e15af6ed02a 100644 --- a/src/Package/Localization/Localization.csproj +++ b/src/Package/Localization/Localization.csproj @@ -1,6 +1,6 @@  - net5.0 + net6.0 net472 Microsoft.Build.Localization.nuspec false diff --git a/src/Package/Localization/Microsoft.Build.Localization.nuspec b/src/Package/Localization/Microsoft.Build.Localization.nuspec index aa36ab55a94..f2c99374064 100644 --- a/src/Package/Localization/Microsoft.Build.Localization.nuspec +++ b/src/Package/Localization/Microsoft.Build.Localization.nuspec @@ -22,7 +22,6 @@ - @@ -38,7 +37,6 @@ - @@ -54,7 +52,6 @@ - @@ -70,7 +67,6 @@ - diff --git a/src/Package/MSBuild.VSSetup/files.swr b/src/Package/MSBuild.VSSetup/files.swr index 9e82ee87cae..67e5764974d 100644 --- a/src/Package/MSBuild.VSSetup/files.swr +++ b/src/Package/MSBuild.VSSetup/files.swr @@ -21,7 +21,7 @@ vs.relatedProcessFiles folder InstallDir:\MSBuild\Current file source=$(X86BinPath)Microsoft.Common.props - file source=$(X86BinPath)Microsoft.VisualStudioVersion.v16.Common.props + file source=$(X86BinPath)Microsoft.VisualStudioVersion.v17.Common.props file source=$(ThirdPartyNotice) folder InstallDir:\MSBuild\Current\Bin @@ -36,20 +36,19 @@ folder InstallDir:\MSBuild\Current\Bin file source=$(X86BinPath)MSBuild.exe.config file source=$(TaskHostBinPath)MSBuildTaskHost.exe vs.file.ngenArchitecture=x86 file source=$(TaskHostBinPath)MSBuildTaskHost.exe.config - file source=$(X86BinPath)System.Buffers.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)System.Memory.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)System.Text.Json.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)Microsoft.Bcl.AsyncInterfaces.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)System.Text.Encodings.Web.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)System.Threading.Tasks.Extensions.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)System.ValueTuple.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)System.Numerics.Vectors.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)System.Resources.Extensions.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)System.Runtime.CompilerServices.Unsafe.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)System.Threading.Tasks.Dataflow.dll vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)System.Buffers.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)System.Memory.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)System.Text.Json.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)Microsoft.Bcl.AsyncInterfaces.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)System.Text.Encodings.Web.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)System.Threading.Tasks.Extensions.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)System.Numerics.Vectors.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)System.Resources.Extensions.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)System.Runtime.CompilerServices.Unsafe.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)System.Threading.Tasks.Dataflow.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 + file source=$(X86BinPath)System.Collections.Immutable.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 file source=$(X86BinPath)Microsoft.NET.StringTools.dll vs.file.ngenArchitecture=all file source=$(TaskHostBinPath)Microsoft.NET.StringTools.net35.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)System.Collections.Immutable.dll vs.file.ngenApplications="[installDir]\MSBuild\Current\Bin\MSBuild.exe" vs.file.ngenArchitecture=all vs.file.ngenPriority=1 file source=$(X86BinPath)Microsoft.Common.CurrentVersion.targets file source=$(X86BinPath)Microsoft.Common.CrossTargeting.targets file source=$(X86BinPath)Microsoft.Common.overridetasks @@ -101,12 +100,6 @@ folder InstallDir:\MSBuild\Current\Bin\de file source=$(X86BinPath)de\Microsoft.Build.Utilities.Core.resources.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)de\MSBuild.resources.dll vs.file.ngenArchitecture=all file source=$(TaskHostBinPath)de\MSBuildTaskHost.resources.dll vs.file.ngenArchitecture=all -folder InstallDir:\MSBuild\Current\Bin\en - file source=$(X86BinPath)en\Microsoft.Build.resources.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)en\Microsoft.Build.Tasks.Core.resources.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)en\Microsoft.Build.Utilities.Core.resources.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)en\MSBuild.resources.dll vs.file.ngenArchitecture=all - file source=$(TaskHostBinPath)en\MSBuildTaskHost.resources.dll vs.file.ngenArchitecture=all folder InstallDir:\MSBuild\Current\Bin\es file source=$(X86BinPath)es\Microsoft.Build.resources.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)es\Microsoft.Build.Tasks.Core.resources.dll vs.file.ngenArchitecture=all @@ -193,7 +186,6 @@ folder InstallDir:\MSBuild\Current\Bin\amd64 file source=$(X86BinPath)Microsoft.Bcl.AsyncInterfaces.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)System.Text.Encodings.Web.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)System.Threading.Tasks.Extensions.dll vs.file.ngenArchitecture=all - file source=$(X86BinPath)System.ValueTuple.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)System.Numerics.Vectors.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)System.Resources.Extensions.dll vs.file.ngenArchitecture=all file source=$(X86BinPath)System.Runtime.CompilerServices.Unsafe.dll vs.file.ngenArchitecture=all @@ -245,12 +237,6 @@ folder InstallDir:\MSBuild\Current\Bin\amd64\de file source=$(X64BinPath)de\Microsoft.Build.Utilities.Core.resources.dll vs.file.ngenArchitecture=all file source=$(X64BinPath)de\MSBuild.resources.dll vs.file.ngenArchitecture=all file source=$(TaskHostX64BinPath)de\MSBuildTaskHost.resources.dll vs.file.ngenArchitecture=all -folder InstallDir:\MSBuild\Current\Bin\amd64\en - file source=$(X64BinPath)en\Microsoft.Build.resources.dll vs.file.ngenArchitecture=all - file source=$(X64BinPath)en\Microsoft.Build.Tasks.Core.resources.dll vs.file.ngenArchitecture=all - file source=$(X64BinPath)en\Microsoft.Build.Utilities.Core.resources.dll vs.file.ngenArchitecture=all - file source=$(X64BinPath)en\MSBuild.resources.dll vs.file.ngenArchitecture=all - file source=$(TaskHostX64BinPath)en\MSBuildTaskHost.resources.dll vs.file.ngenArchitecture=all folder InstallDir:\MSBuild\Current\Bin\amd64\es file source=$(X64BinPath)es\Microsoft.Build.resources.dll vs.file.ngenArchitecture=all file source=$(X64BinPath)es\Microsoft.Build.Tasks.Core.resources.dll vs.file.ngenArchitecture=all @@ -322,8 +308,6 @@ folder InstallDir:\Common7\IDE\CommonExtensions\MSBuild file source=$(SourceDir)Package\MSBuild.VSSetup\MSBuild.clientenabledpkg file source=$(SourceDir)Framework\Microsoft.Build.Framework.pkgdef file source=$(SourceDir)Build\Microsoft.Build.pkgdef - file source=$(SourceDir)Build\System.Text.Encodings.Web.pkgdef - file source=$(SourceDir)Build\System.Text.Json.pkgdef file source=$(SourceDir)StringTools\StringTools.pkgdef file source=$(SourceDir)Tasks\Microsoft.Build.Tasks.Core.pkgdef file source=$(SourceDir)Tasks\System.Resources.Extensions.pkgdef diff --git a/src/Samples/NetCoreCompileTest/App.config b/src/Samples/NetCoreCompileTest/App.config deleted file mode 100644 index 88fa4027bda..00000000000 --- a/src/Samples/NetCoreCompileTest/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/src/Samples/NetCoreCompileTest/NetCoreCompileTest.csproj b/src/Samples/NetCoreCompileTest/NetCoreCompileTest.csproj deleted file mode 100644 index 56e7ebc0301..00000000000 --- a/src/Samples/NetCoreCompileTest/NetCoreCompileTest.csproj +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - Debug - AnyCPU - {11B5D53E-90E4-4BD5-9883-B5921F7DE854} - Exe - Properties - NetCoreCompileTest - NetCoreCompileTest - v4.5.2 - 512 - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Samples/NetCoreCompileTest/Program.cs b/src/Samples/NetCoreCompileTest/Program.cs deleted file mode 100644 index 4b6727a4db1..00000000000 --- a/src/Samples/NetCoreCompileTest/Program.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) Microsoft. 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.Linq; -//using System.Text; -//using System.Threading.Tasks; - -namespace NetCoreCompileTest -{ - internal class Program - { - private static void Main(string[] args) - { - Console.WriteLine("Hello, World"); - } - } -} diff --git a/src/Samples/NetCoreCompileTest/Properties/AssemblyInfo.cs b/src/Samples/NetCoreCompileTest/Properties/AssemblyInfo.cs deleted file mode 100644 index 2767b66e115..00000000000 --- a/src/Samples/NetCoreCompileTest/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("NetCoreCompileTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("NetCoreCompileTest")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("11b5d53e-90e4-4bd5-9883-b5921f7de854")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/Samples/PortableTask/PortableTask.csproj b/src/Samples/PortableTask/PortableTask.csproj index 4f5461e1f22..6a4541787a1 100644 --- a/src/Samples/PortableTask/PortableTask.csproj +++ b/src/Samples/PortableTask/PortableTask.csproj @@ -1,9 +1,9 @@ - + true false false - netstandard1.3 + netstandard2.0 diff --git a/src/Samples/ProjectCachePlugin/AssemblyMockCache.cs b/src/Samples/ProjectCachePlugin/AssemblyMockCache.cs index c89e8c9e3c6..4277bdad314 100644 --- a/src/Samples/ProjectCachePlugin/AssemblyMockCache.cs +++ b/src/Samples/ProjectCachePlugin/AssemblyMockCache.cs @@ -2,11 +2,14 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Build.Execution; using Microsoft.Build.Experimental.ProjectCache; using Microsoft.Build.Framework; +using Microsoft.Build.Graph; +using Shouldly; namespace MockCacheFromAssembly { @@ -21,6 +24,15 @@ public override Task BeginBuildAsync(CacheContext context, PluginLoggerBase logg { logger.LogMessage($"{nameof(AssemblyMockCache)}: BeginBuildAsync", MessageImportance.High); + foreach (var ep in context.GraphEntryPoints ?? Enumerable.Empty()) + { + var globalPropertyString = ep.GlobalProperties is not null + ? string.Join("\n\t", ep.GlobalProperties.Select(gp => $"{gp.Key}:{gp.Value}")) + : string.Empty; + + logger.LogMessage($"EntryPoint: {ep.ProjectFile} \n(\n\t{globalPropertyString}\n)"); + } + ErrorFrom(nameof(BeginBuildAsync), logger); return Task.CompletedTask; @@ -33,6 +45,8 @@ public override Task GetCacheResultAsync( { logger.LogMessage($"{nameof(AssemblyMockCache)}: GetCacheResultAsync for {buildRequest.ProjectFullPath}", MessageImportance.High); + buildRequest.ProjectInstance.ShouldNotBeNull("The cache plugin expects evaluated projects."); + ErrorFrom(nameof(GetCacheResultAsync), logger); return Task.FromResult(CacheResult.IndicateNonCacheHit(CacheResultType.CacheNotApplicable)); @@ -54,6 +68,7 @@ private static void ErrorFrom(string errorLocation, PluginLoggerBase pluginLogge switch (errorKind) { case "Exception": + pluginLoggerBase?.LogMessage($"{errorLocation} is going to throw an exception", MessageImportance.High); throw new Exception($"Cache plugin exception from {errorLocation}"); case "LoggedError": pluginLoggerBase?.LogError($"Cache plugin logged error from {errorLocation}"); diff --git a/src/Samples/ProjectCachePlugin/ProjectCachePlugin.csproj b/src/Samples/ProjectCachePlugin/ProjectCachePlugin.csproj index df35ae1ca6b..6972b5e9ab0 100644 --- a/src/Samples/ProjectCachePlugin/ProjectCachePlugin.csproj +++ b/src/Samples/ProjectCachePlugin/ProjectCachePlugin.csproj @@ -1,15 +1,19 @@ - + true false false - net5.0 - $(FullFrameworkTFM);net5.0 + net6.0 + $(FullFrameworkTFM);net6.0 $(RuntimeOutputTargetFrameworks) + + + + diff --git a/src/Shared/AssemblyNameExtension.cs b/src/Shared/AssemblyNameExtension.cs index f25ec20c73f..6b6af7c67cd 100644 --- a/src/Shared/AssemblyNameExtension.cs +++ b/src/Shared/AssemblyNameExtension.cs @@ -148,7 +148,6 @@ private AssemblyNameExtension(SerializationInfo info, StreamingContext context) var hashAlgorithm = (System.Configuration.Assemblies.AssemblyHashAlgorithm) info.GetInt32("hashAlg"); var versionCompatibility = (AssemblyVersionCompatibility) info.GetInt32("verCompat"); var codeBase = info.GetString("codebase"); - var keyPair = (StrongNameKeyPair) info.GetValue("keypair", typeof(StrongNameKeyPair)); asAssemblyName = new AssemblyName { @@ -160,7 +159,6 @@ private AssemblyNameExtension(SerializationInfo info, StreamingContext context) HashAlgorithm = hashAlgorithm, VersionCompatibility = versionCompatibility, CodeBase = codeBase, - KeyPair = keyPair }; asAssemblyName.SetPublicKey(publicKey); @@ -644,7 +642,7 @@ private static int CompareBaseNamesStringWise(string asString1, string asString2 /// internal AssemblyNameExtension Clone() { - AssemblyNameExtension newExtension = new AssemblyNameExtension(); + AssemblyNameExtension newExtension = new(); if (asAssemblyName != null) { @@ -1010,7 +1008,6 @@ public void GetObjectData(SerializationInfo info, StreamingContext context) info.AddValue("hashAlg", asAssemblyName.HashAlgorithm); info.AddValue("verCompat", asAssemblyName.VersionCompatibility); info.AddValue("codebase", asAssemblyName.CodeBase); - info.AddValue("keypair", asAssemblyName.KeyPair); } info.AddValue("asStr", asString); diff --git a/src/Shared/BinaryTranslator.cs b/src/Shared/BinaryTranslator.cs index 6c2b6337393..56a1b1f3803 100644 --- a/src/Shared/BinaryTranslator.cs +++ b/src/Shared/BinaryTranslator.cs @@ -64,6 +64,14 @@ public BinaryReadTranslator(Stream packetStream, SharedReadBuffer buffer) _reader = InterningBinaryReader.Create(packetStream, buffer); } + /// + /// Delegates the Dispose call the to the underlying BinaryReader. + /// + public void Dispose() + { + _reader.Close(); + } + /// /// Gets the reader, if any. /// @@ -660,12 +668,31 @@ public void TranslateDictionary(ref D dictionary, ObjectTranslator obje } } - /// - /// Reads in the boolean which says if this object is null or not. - /// - /// The type of object to test. - /// True if the object should be read, false otherwise. - public bool TranslateNullable(T value) + public void TranslateDictionary(ref Dictionary dictionary, StringComparer comparer) + { + if (!TranslateNullable(dictionary)) + { + return; + } + + int count = _reader.ReadInt32(); + dictionary = new(count, comparer); + string key = string.Empty; + DateTime val = DateTime.MinValue; + for (int i = 0; i < count; i++) + { + Translate(ref key); + Translate(ref val); + dictionary.Add(key, val); + } + } + + /// + /// Reads in the boolean which says if this object is null or not. + /// + /// The type of object to test. + /// True if the object should be read, false otherwise. + public bool TranslateNullable(T value) { bool haveRef = _reader.ReadBoolean(); return haveRef; @@ -697,6 +724,14 @@ public BinaryWriteTranslator(Stream packetStream) _writer = new BinaryWriter(packetStream); } + /// + /// Delegates the Dispose call the to the underlying BinaryWriter. + /// + public void Dispose() + { + _writer.Close(); + } + /// /// Gets the reader, if any. /// @@ -1254,6 +1289,29 @@ public void TranslateDictionary(ref D dictionary, ObjectTranslator obje } } + /// + /// Translates a dictionary of { string, DateTime }. + /// + /// The dictionary to be translated. + /// Key comparer + public void TranslateDictionary(ref Dictionary dictionary, StringComparer comparer) + { + if (!TranslateNullable(dictionary)) + { + return; + } + + int count = dictionary.Count; + _writer.Write(count); + foreach (KeyValuePair kvp in dictionary) + { + string key = kvp.Key; + DateTime val = kvp.Value; + Translate(ref key); + Translate(ref val); + } + } + /// /// Writes out the boolean which says if this object is null or not. /// diff --git a/src/Shared/BuildEnvironmentHelper.cs b/src/Shared/BuildEnvironmentHelper.cs index 3785175e7fd..f35dc2f8a6e 100644 --- a/src/Shared/BuildEnvironmentHelper.cs +++ b/src/Shared/BuildEnvironmentHelper.cs @@ -15,7 +15,7 @@ internal class BuildEnvironmentHelper { // Since this class is added as 'link' to shared source in multiple projects, // MSBuildConstants.CurrentVisualStudioVersion is not available in all of them. - private const string CurrentVisualStudioVersion = "16.0"; + private const string CurrentVisualStudioVersion = "17.0"; // MSBuildConstants.CurrentToolsVersion private const string CurrentToolsVersion = "Current"; @@ -125,7 +125,7 @@ private static BuildEnvironment TryFromEnvironmentVariable() return msBuildExePath == null ? null - : TryFromMSBuildAssemblyUnderVisualStudio(msBuildExePath, msBuildExePath, true) ?? TryFromStandaloneMSBuildExe(msBuildExePath); + : TryFromMSBuildExeUnderVisualStudio(msBuildExePath, allowLegacyToolsVersion: true) ?? TryFromStandaloneMSBuildExe(msBuildExePath); } private static BuildEnvironment TryFromVisualStudioProcess() @@ -183,7 +183,7 @@ private static BuildEnvironment TryFromMSBuildAssembly() var msBuildDll = Path.Combine(FileUtilities.GetFolderAbove(buildAssembly), "MSBuild.dll"); // First check if we're in a VS installation - var environment = TryFromMSBuildAssemblyUnderVisualStudio(buildAssembly, msBuildExe); + var environment = TryFromMSBuildExeUnderVisualStudio(msBuildExe); if (environment != null) { return environment; @@ -208,25 +208,22 @@ private static BuildEnvironment TryFromMSBuildAssembly() return null; } - private static BuildEnvironment TryFromMSBuildAssemblyUnderVisualStudio(string msbuildAssembly, string msbuildExe, bool allowLegacyToolsVersion = false) + private static BuildEnvironment TryFromMSBuildExeUnderVisualStudio(string msbuildExe, bool allowLegacyToolsVersion = false) { string msBuildPathPattern = allowLegacyToolsVersion ? $@".*\\MSBuild\\({CurrentToolsVersion}|\d+\.0)\\Bin\\.*" : $@".*\\MSBuild\\{CurrentToolsVersion}\\Bin\\.*"; if (NativeMethodsShared.IsWindows && - Regex.IsMatch(msbuildAssembly, msBuildPathPattern, RegexOptions.IgnoreCase)) + Regex.IsMatch(msbuildExe, msBuildPathPattern, RegexOptions.IgnoreCase)) { - // In a Visual Studio path we must have MSBuild.exe - if (FileSystems.Default.FileExists(msbuildExe)) - { - return new BuildEnvironment( + string visualStudioRoot = GetVsRootFromMSBuildAssembly(msbuildExe); + return new BuildEnvironment( BuildEnvironmentMode.VisualStudio, - msbuildExe, + GetMSBuildExeFromVsRoot(visualStudioRoot), runningTests: s_runningTests(), runningInVisualStudio: false, - visualStudioPath: GetVsRootFromMSBuildAssembly(msbuildExe)); - } + visualStudioPath: visualStudioRoot); } return null; @@ -320,7 +317,8 @@ private static BuildEnvironment TryFromStandaloneMSBuildExe(string msBuildExePat private static string GetVsRootFromMSBuildAssembly(string msBuildAssembly) { return FileUtilities.GetFolderAbove(msBuildAssembly, - Regex.IsMatch(msBuildAssembly, $@"\\Bin\\Amd64\\MSBuild\.exe", RegexOptions.IgnoreCase) + Path.GetDirectoryName(msBuildAssembly) + .EndsWith(@"\amd64", StringComparison.OrdinalIgnoreCase) ? 5 : 4); } diff --git a/src/Shared/CommunicationsUtilities.cs b/src/Shared/CommunicationsUtilities.cs index e5333429e68..4b118f31594 100644 --- a/src/Shared/CommunicationsUtilities.cs +++ b/src/Shared/CommunicationsUtilities.cs @@ -13,7 +13,11 @@ using Microsoft.Build.Shared; using System.Reflection; +using Microsoft.Build.Utilities; +#if !CLR2COMPATIBILITY +using Microsoft.Build.Shared.Debugging; +#endif #if !FEATURE_APM using System.Threading.Tasks; #endif @@ -131,7 +135,7 @@ static internal class CommunicationsUtilities /// /// Whether to trace communications /// - private static bool s_trace = String.Equals(Environment.GetEnvironmentVariable("MSBUILDDEBUGCOMM"), "1", StringComparison.Ordinal); + private static bool s_trace = Traits.Instance.DebugNodeCommunication; /// /// Place to dump trace @@ -175,6 +179,13 @@ static internal int NodeConnectionTimeout /// internal static Dictionary GetEnvironmentVariables() { +#if !CLR2COMPATIBILITY + // The DebugUtils static constructor can set the MSBUILDDEBUGPATH environment variable to propagate the debug path to out of proc nodes. + // Need to ensure that constructor is called before this method returns in order to capture its env var write. + // Otherwise the env var is not captured and thus gets deleted when RequiestBuilder resets the environment based on the cached results of this method. + ErrorUtilities.VerifyThrowInternalNull(DebugUtils.ProcessInfoString, nameof(DebugUtils.DebugPath)); +#endif + Dictionary table = new Dictionary(200, StringComparer.OrdinalIgnoreCase); // Razzle has 150 environment variables if (NativeMethodsShared.IsWindows) @@ -332,14 +343,14 @@ internal static void WriteIntForHandshake(this PipeStream stream, int value) } internal static void ReadEndOfHandshakeSignal(this PipeStream stream, bool isProvider -#if NETCOREAPP2_1 || MONO +#if NETCOREAPP2_1_OR_GREATER || MONO , int timeout #endif ) { // Accept only the first byte of the EndOfHandshakeSignal int valueRead = stream.ReadIntForHandshake(null -#if NETCOREAPP2_1 || MONO +#if NETCOREAPP2_1_OR_GREATER || MONO , timeout #endif ); @@ -363,14 +374,14 @@ internal static void ReadEndOfHandshakeSignal(this PipeStream stream, bool isPro /// If specified, leading byte matches one in the supplied array if any, returns rejection byte and throws IOException. /// internal static int ReadIntForHandshake(this PipeStream stream, byte? byteToAccept -#if NETCOREAPP2_1 || MONO +#if NETCOREAPP2_1_OR_GREATER || MONO , int timeout #endif ) { byte[] bytes = new byte[4]; -#if NETCOREAPP2_1 || MONO +#if NETCOREAPP2_1_OR_GREATER || MONO if (!NativeMethodsShared.IsWindows) { // Enforce a minimum timeout because the Windows code can pass @@ -552,7 +563,14 @@ internal static void Trace(int nodeId, string format, params object[] args) { if (s_debugDumpPath == null) { - s_debugDumpPath = Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); + s_debugDumpPath = +#if CLR2COMPATIBILITY + Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); +#else + ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0) + ? DebugUtils.DebugPath + : Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); +#endif if (String.IsNullOrEmpty(s_debugDumpPath)) { diff --git a/src/Shared/Constants.cs b/src/Shared/Constants.cs index e0c4da0540e..6289ef54bef 100644 --- a/src/Shared/Constants.cs +++ b/src/Shared/Constants.cs @@ -53,7 +53,7 @@ internal static class MSBuildConstants /// /// The most current Visual Studio Version known to this version of MSBuild. /// - internal const string CurrentVisualStudioVersion = "16.0"; + internal const string CurrentVisualStudioVersion = "17.0"; /// /// The most current ToolsVersion known to this version of MSBuild. @@ -70,7 +70,7 @@ internal static class MSBuildConstants /// /// Current version of this MSBuild Engine assembly in the form, e.g, "12.0" /// - internal const string CurrentProductVersion = "16.0"; + internal const string CurrentProductVersion = "17.0"; /// /// Symbol used in ProjectReferenceTarget items to represent default targets diff --git a/src/Shared/CoreCLRAssemblyLoader.cs b/src/Shared/CoreCLRAssemblyLoader.cs index 14cd04a244d..19b6d03f5d0 100644 --- a/src/Shared/CoreCLRAssemblyLoader.cs +++ b/src/Shared/CoreCLRAssemblyLoader.cs @@ -153,26 +153,23 @@ private Assembly TryResolveAssemblyFromPaths(AssemblyLoadContext context, Assemb { foreach (var searchPath in searchPaths) { - foreach (var extension in MSBuildLoadContext.Extensions) + var candidatePath = Path.Combine(searchPath, + cultureSubfolder, + $"{assemblyName.Name}.dll"); + + if (IsAssemblyAlreadyLoaded(candidatePath) || + !FileSystems.Default.FileExists(candidatePath)) { - var candidatePath = Path.Combine(searchPath, - cultureSubfolder, - $"{assemblyName.Name}.{extension}"); - - if (IsAssemblyAlreadyLoaded(candidatePath) || - !FileSystems.Default.FileExists(candidatePath)) - { - continue; - } - - AssemblyName candidateAssemblyName = AssemblyLoadContext.GetAssemblyName(candidatePath); - if (candidateAssemblyName.Version != assemblyName.Version) - { - continue; - } - - return LoadAndCache(context, candidatePath); + continue; } + + AssemblyName candidateAssemblyName = AssemblyLoadContext.GetAssemblyName(candidatePath); + if (candidateAssemblyName.Version != assemblyName.Version) + { + continue; + } + + return LoadAndCache(context, candidatePath); } } diff --git a/src/Shared/Debugging/DebugUtils.cs b/src/Shared/Debugging/DebugUtils.cs new file mode 100644 index 00000000000..aa83426e5af --- /dev/null +++ b/src/Shared/Debugging/DebugUtils.cs @@ -0,0 +1,104 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.Utilities; +using System; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; + +namespace Microsoft.Build.Shared.Debugging +{ + internal static class DebugUtils + { + private enum NodeMode + { + CentralNode, + OutOfProcNode, + OutOfProcTaskHostNode + } + + static DebugUtils() + { + string environmentDebugPath = Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); + var debugDirectory = environmentDebugPath; + + if (Traits.Instance.DebugEngine) + { + debugDirectory ??= Path.Combine(Directory.GetCurrentDirectory(), "MSBuild_Logs"); + + // Out of proc nodes do not know the startup directory so set the environment variable for them. + if (string.IsNullOrWhiteSpace(environmentDebugPath)) + { + Environment.SetEnvironmentVariable("MSBUILDDEBUGPATH", debugDirectory); + } + } + + if (debugDirectory is not null) + { + FileUtilities.EnsureDirectoryExists(debugDirectory); + } + + DebugPath = debugDirectory; + } + + private static readonly Lazy ProcessNodeMode = new( + () => + { + return ScanNodeMode(Environment.CommandLine); + + NodeMode ScanNodeMode(string input) + { + var match = Regex.Match(input, @"/nodemode:(?[12\s])(\s|$)", RegexOptions.IgnoreCase); + + if (!match.Success) + { + return NodeMode.CentralNode; + } + var nodeMode = match.Groups["nodemode"].Value; + + Trace.Assert(!string.IsNullOrEmpty(nodeMode)); + + return nodeMode switch + { + "1" => NodeMode.OutOfProcNode, + "2" => NodeMode.OutOfProcTaskHostNode, + _ => throw new NotImplementedException(), + }; + } + }); + + private static bool CurrentProcessMatchesDebugName() + { + var processNameToBreakInto = Environment.GetEnvironmentVariable("MSBuildDebugProcessName"); + var thisProcessMatchesName = string.IsNullOrWhiteSpace(processNameToBreakInto) || + Process.GetCurrentProcess().ProcessName.Contains(processNameToBreakInto); + + return thisProcessMatchesName; + } + + public static readonly string ProcessInfoString = + $"{ProcessNodeMode.Value}_{Process.GetCurrentProcess().ProcessName}_PID={Process.GetCurrentProcess().Id}_x{(Environment.Is64BitProcess ? "64" : "86")}"; + + public static readonly bool ShouldDebugCurrentProcess = CurrentProcessMatchesDebugName(); + + public static string DebugPath { get; } + + public static string FindNextAvailableDebugFilePath(string fileName) + { + var extension = Path.GetExtension(fileName); + var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); + + var fullPath = Path.Combine(DebugPath, fileName); + + var counter = 0; + while (File.Exists(fullPath)) + { + fileName = $"{fileNameWithoutExtension}_{counter++}{extension}"; + fullPath = Path.Combine(DebugPath, fileName); + } + + return fullPath; + } + } +} diff --git a/src/Shared/Debugging/PrintLineDebugger.cs b/src/Shared/Debugging/PrintLineDebugger.cs index bfcdbfd57c4..28fe6c8587a 100644 --- a/src/Shared/Debugging/PrintLineDebugger.cs +++ b/src/Shared/Debugging/PrintLineDebugger.cs @@ -20,13 +20,6 @@ namespace Microsoft.Build.Shared.Debugging /// internal class PrintLineDebugger : IDisposable { - internal enum NodeMode - { - CentralNode, - OutOfProcNode, - OutOfProcTaskHostNode - } - private static readonly Lazy CommonWriterProperty = new Lazy( () => { @@ -45,41 +38,10 @@ internal enum NodeMode public static Lazy DefaultWithProcessInfo = new Lazy(() => Create(null, null, true)); - private static readonly Lazy ProcessNodeMode = new Lazy( - () => - { - return ScanNodeMode(Environment.CommandLine); - - NodeMode ScanNodeMode(string input) - { - var match = Regex.Match(input, @"/nodemode:(?[12\s])(\s|$)", RegexOptions.IgnoreCase); - - if (!match.Success) - { - return NodeMode.CentralNode; - } - var nodeMode = match.Groups["nodemode"].Value; - - Trace.Assert(!string.IsNullOrEmpty(nodeMode)); - - return nodeMode switch - { - "1" => NodeMode.OutOfProcNode, - "2" => NodeMode.OutOfProcTaskHostNode, - _ => throw new NotImplementedException(), - }; - } - }); - private readonly string _id; private readonly CommonWriterType _writerSetByThisInstance; - public static string ProcessInfo - => - $"{ProcessNodeMode.Value}_PID={Process.GetCurrentProcess() .Id}({Process.GetCurrentProcess() .ProcessName})x{(Environment.Is64BitProcess ? "64" : "86")}" - ; - public PrintLineDebugger(string id, CommonWriterType writer) { _id = id ?? string.Empty; @@ -147,7 +109,7 @@ public static PrintLineDebugger Create( { return new PrintLineDebugger( prependProcessInfo - ? $"{ProcessInfo}_{id}" + ? $"{DebugUtils.ProcessInfoString}_{id}" : id, writer); } diff --git a/src/Shared/ErrorUtilities.cs b/src/Shared/ErrorUtilities.cs index 2731c90b61a..9bb3502596b 100644 --- a/src/Shared/ErrorUtilities.cs +++ b/src/Shared/ErrorUtilities.cs @@ -47,6 +47,14 @@ public static void DebugTraceMessage(string category, string formatstring, param #if !BUILDINGAPPXTASKS #region VerifyThrow -- for internal errors + internal static void VerifyThrowInternalError(bool condition, string message, params object[] args) + { + if (s_throwExceptions && !condition) + { + throw new InternalErrorException(ResourceUtilities.FormatString(message, args)); + } + } + /// /// Throws InternalErrorException. /// This is only for situations that would mean that there is a bug in MSBuild itself. diff --git a/src/Shared/ExceptionHandling.cs b/src/Shared/ExceptionHandling.cs index ae0dac9bfe3..8ba3e225520 100644 --- a/src/Shared/ExceptionHandling.cs +++ b/src/Shared/ExceptionHandling.cs @@ -19,6 +19,10 @@ namespace Microsoft.Build.AppxPackage.Shared using Microsoft.Build.Shared.FileSystem; using System.Xml.Schema; using System.Runtime.Serialization; +#if !CLR2COMPATIBILITY +using Microsoft.Build.Shared.Debugging; +#endif +using Microsoft.Build.Utilities; namespace Microsoft.Build.Shared #endif @@ -41,7 +45,16 @@ static ExceptionHandling() /// private static string GetDebugDumpPath() { - string debugPath = Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); + string debugPath = +// Cannot access change wave logic from these assemblies (https://github.com/dotnet/msbuild/issues/6707) +#if CLR2COMPATIBILITY || MICROSOFT_BUILD_ENGINE_OM_UNITTESTS + Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); +#else + ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0) + ? DebugUtils.DebugPath + : Environment.GetEnvironmentVariable("MSBUILDDEBUGPATH"); +#endif + return !string.IsNullOrEmpty(debugPath) ? debugPath : Path.GetTempPath(); diff --git a/src/Shared/FileMatcher.cs b/src/Shared/FileMatcher.cs index 1bfdc57490e..9af0619bab7 100644 --- a/src/Shared/FileMatcher.cs +++ b/src/Shared/FileMatcher.cs @@ -54,7 +54,7 @@ internal class FileMatcher private static class FileSpecRegexParts { - internal const string FixedDirGroupStart = "^(?"; + internal const string BeginningOfLine = "^"; internal const string WildcardGroupStart = "(?"; internal const string FilenameGroupStart = "(?"; internal const string GroupEnd = ")"; @@ -71,10 +71,10 @@ private static class FileSpecRegexParts } /* - * MAX_PATH + FileSpecRegexParts.BeginningOfLine.Length + FileSpecRegexParts.FixedDirWildcardDirSeparator.Length - + FileSpecRegexParts.WildcardDirFilenameSeparator.Length + FileSpecRegexParts.EndOfLine.Length; + * FileSpecRegexParts.BeginningOfLine.Length + FileSpecRegexParts.WildcardGroupStart.Length + FileSpecRegexParts.GroupEnd.Length + + FileSpecRegexParts.FilenameGroupStart.Length + FileSpecRegexParts.GroupEnd.Length + FileSpecRegexParts.EndOfLine.Length; */ - private const int FileSpecRegexMinLength = 44; + private const int FileSpecRegexMinLength = 31; /// /// The Default FileMatcher does not cache directory enumeration. @@ -131,7 +131,7 @@ internal FileMatcher(IFileSystem fileSystem, GetFileSystemEntries getFileSystemE "*", directory, false)); - IEnumerable filteredEntriesForPath = (pattern != null && pattern != "*" && pattern != "*.*") + IEnumerable filteredEntriesForPath = (pattern != null && !IsAllFilesWildcard(pattern)) ? allEntriesForPath.Where(o => IsMatch(Path.GetFileName(o), pattern)) : allEntriesForPath; return stripProjectDirectory @@ -501,7 +501,7 @@ GetFileSystemEntries getFileSystemEntries else { // Relative - pathRoot = String.Empty; + pathRoot = string.Empty; startingElement = 0; } } @@ -516,7 +516,7 @@ GetFileSystemEntries getFileSystemEntries // If there is a zero-length part, then that means there was an extra slash. if (parts[i].Length == 0) { - longParts[i - startingElement] = String.Empty; + longParts[i - startingElement] = string.Empty; } else { @@ -556,7 +556,7 @@ GetFileSystemEntries getFileSystemEntries } } - return pathRoot + String.Join(s_directorySeparator, longParts); + return pathRoot + string.Join(s_directorySeparator, longParts); } /// @@ -630,8 +630,8 @@ out string filenamePart * * ** */ - fixedDirectoryPart = String.Empty; - wildcardDirectoryPart = String.Empty; + fixedDirectoryPart = string.Empty; + wildcardDirectoryPart = string.Empty; filenamePart = filespec; return; } @@ -661,7 +661,7 @@ out string filenamePart // We know the fixed director part now. fixedDirectoryPart = filespec.Substring(0, indexOfLastDirectorySeparator + 1); - wildcardDirectoryPart = String.Empty; + wildcardDirectoryPart = string.Empty; filenamePart = filespec.Substring(indexOfLastDirectorySeparator + 1); return; } @@ -682,7 +682,7 @@ out string filenamePart * * dir?\** */ - fixedDirectoryPart = String.Empty; + fixedDirectoryPart = string.Empty; wildcardDirectoryPart = filespec.Substring(0, indexOfLastDirectorySeparator + 1); filenamePart = filespec.Substring(indexOfLastDirectorySeparator + 1); return; @@ -886,7 +886,7 @@ private void GetFilesRecursive( // The wildcard path portion of the excluded search matches the include search searchToExclude.RemainingWildcardDirectory == recursionState.RemainingWildcardDirectory && // The exclude search will match ALL filenames OR - (searchToExclude.SearchData.Filespec == "*" || searchToExclude.SearchData.Filespec == "*.*" || + (IsAllFilesWildcard(searchToExclude.SearchData.Filespec) || // The exclude search filename pattern matches the include search's pattern searchToExclude.SearchData.Filespec == recursionState.SearchData.Filespec)) { @@ -1091,7 +1091,11 @@ private IEnumerable GetFilesForStep( private static bool MatchFileRecursionStep(RecursionState recursionState, string file) { - if (recursionState.SearchData.Filespec != null) + if (IsAllFilesWildcard(recursionState.SearchData.Filespec)) + { + return true; + } + else if (recursionState.SearchData.Filespec != null) { return IsMatch(Path.GetFileName(file), recursionState.SearchData.Filespec); } @@ -1203,10 +1207,10 @@ string filenamePart { #if DEBUG ErrorUtilities.VerifyThrow( - FileSpecRegexMinLength == FileSpecRegexParts.FixedDirGroupStart.Length + FileSpecRegexMinLength == FileSpecRegexParts.BeginningOfLine.Length + FileSpecRegexParts.WildcardGroupStart.Length + FileSpecRegexParts.FilenameGroupStart.Length - + (FileSpecRegexParts.GroupEnd.Length * 3) + + (FileSpecRegexParts.GroupEnd.Length * 2) + FileSpecRegexParts.EndOfLine.Length, "Checked-in length of known regex components differs from computed length. Update checked-in constant." ); @@ -1274,7 +1278,7 @@ private static bool HasMisplacedRecursiveOperator(string str) /// private static void AppendRegularExpressionFromFixedDirectory(ReuseableStringBuilder regex, string fixedDir) { - regex.Append(FileSpecRegexParts.FixedDirGroupStart); + regex.Append(FileSpecRegexParts.BeginningOfLine); bool isUncPath = NativeMethodsShared.IsWindows && fixedDir.Length > 1 && fixedDir[0] == '\\' && fixedDir[1] == '\\'; @@ -1288,8 +1292,6 @@ private static void AppendRegularExpressionFromFixedDirectory(ReuseableStringBui { AppendRegularExpressionFromChar(regex, fixedDir[i]); } - - regex.Append(FileSpecRegexParts.GroupEnd); } /// @@ -1565,9 +1567,9 @@ internal void GetFileSpecInfo( FixupParts fixupParts = null) { needsRecursion = false; - fixedDirectoryPart = String.Empty; - wildcardDirectoryPart = String.Empty; - filenamePart = String.Empty; + fixedDirectoryPart = string.Empty; + wildcardDirectoryPart = string.Empty; + filenamePart = string.Empty; if (!RawFileSpecIsValid(filespec)) { @@ -1659,9 +1661,7 @@ internal Result() internal bool isLegalFileSpec; // initially false internal bool isMatch; // initially false internal bool isFileSpecRecursive; // initially false - internal string fixedDirectoryPart = String.Empty; - internal string wildcardDirectoryPart = String.Empty; - internal string filenamePart = String.Empty; + internal string wildcardDirectoryPart = string.Empty; } /// @@ -1853,9 +1853,8 @@ out matchResult.isLegalFileSpec fileToMatch, regexFileMatch, out matchResult.isMatch, - out matchResult.fixedDirectoryPart, out matchResult.wildcardDirectoryPart, - out matchResult.filenamePart); + out _); } return matchResult; @@ -1865,20 +1864,17 @@ internal static void GetRegexMatchInfo( string fileToMatch, Regex fileSpecRegex, out bool isMatch, - out string fixedDirectoryPart, out string wildcardDirectoryPart, out string filenamePart) { Match match = fileSpecRegex.Match(fileToMatch); isMatch = match.Success; - fixedDirectoryPart = string.Empty; - wildcardDirectoryPart = String.Empty; + wildcardDirectoryPart = string.Empty; filenamePart = string.Empty; if (isMatch) { - fixedDirectoryPart = match.Groups["FIXEDDIR"].Value; wildcardDirectoryPart = match.Groups["WILDCARDDIR"].Value; filenamePart = match.Groups["FILENAME"].Value; } @@ -2089,7 +2085,7 @@ out bool isLegalFileSpec return SearchAction.ReturnEmptyList; } - stripProjectDirectory = !String.Equals(fixedDirectoryPart, oldFixedDirectoryPart, StringComparison.OrdinalIgnoreCase); + stripProjectDirectory = !string.Equals(fixedDirectoryPart, oldFixedDirectoryPart, StringComparison.OrdinalIgnoreCase); } else { @@ -2564,6 +2560,17 @@ private static bool DirectoryEndsWithPattern(string directoryPath, string patter return (index != -1 && IsMatch(directoryPath.Substring(index + 1), pattern)); } + /// + /// Returns true if is * or *.*. + /// + /// The filename pattern to check. + private static bool IsAllFilesWildcard(string pattern) => pattern?.Length switch + { + 1 => pattern[0] == '*', + 3 => pattern[0] == '*' && pattern[1] == '.' && pattern[2] == '*', + _ => false + }; + internal static bool IsRecursiveDirectoryMatch(string path) => path.TrimTrailingSlashes() == recursiveDirectoryMatch; } } diff --git a/src/Shared/FileSystem/CachingFileSystemWrapper.cs b/src/Shared/FileSystem/CachingFileSystemWrapper.cs index c3b3b141f20..b541f1b44f7 100644 --- a/src/Shared/FileSystem/CachingFileSystemWrapper.cs +++ b/src/Shared/FileSystem/CachingFileSystemWrapper.cs @@ -19,9 +19,9 @@ public CachingFileSystemWrapper(IFileSystem fileSystem) _fileSystem = fileSystem; } - public bool DirectoryEntryExists(string path) + public bool FileOrDirectoryExists(string path) { - return CachedExistenceCheck(path, p => _fileSystem.DirectoryEntryExists(p)); + return CachedExistenceCheck(path, p => _fileSystem.FileOrDirectoryExists(p)); } public FileAttributes GetAttributes(string path) diff --git a/src/Shared/FileSystem/IFileSystem.cs b/src/Shared/FileSystem/IFileSystem.cs index 0ef03e74c65..c5e32373eec 100644 --- a/src/Shared/FileSystem/IFileSystem.cs +++ b/src/Shared/FileSystem/IFileSystem.cs @@ -42,6 +42,6 @@ internal interface IFileSystem bool FileExists(string path); - bool DirectoryEntryExists(string path); + bool FileOrDirectoryExists(string path); } } diff --git a/src/Shared/FileSystem/MSBuildOnWindowsFileSystem.cs b/src/Shared/FileSystem/MSBuildOnWindowsFileSystem.cs index 188efaf27fa..e0be3971d7a 100644 --- a/src/Shared/FileSystem/MSBuildOnWindowsFileSystem.cs +++ b/src/Shared/FileSystem/MSBuildOnWindowsFileSystem.cs @@ -74,9 +74,9 @@ public bool FileExists(string path) return WindowsFileSystem.Singleton().FileExists(path); } - public bool DirectoryEntryExists(string path) + public bool FileOrDirectoryExists(string path) { - return WindowsFileSystem.Singleton().DirectoryEntryExists(path); + return WindowsFileSystem.Singleton().FileOrDirectoryExists(path); } } } diff --git a/src/Shared/FileSystem/ManagedFileSystem.cs b/src/Shared/FileSystem/ManagedFileSystem.cs index 201a62e7436..6d8bd32fb4e 100644 --- a/src/Shared/FileSystem/ManagedFileSystem.cs +++ b/src/Shared/FileSystem/ManagedFileSystem.cs @@ -73,7 +73,7 @@ public virtual bool FileExists(string path) return File.Exists(path); } - public virtual bool DirectoryEntryExists(string path) + public virtual bool FileOrDirectoryExists(string path) { return FileExists(path) || DirectoryExists(path); } diff --git a/src/Shared/FileSystem/WindowsFileSystem.cs b/src/Shared/FileSystem/WindowsFileSystem.cs index 9f4e3bf1dda..60b7d8ececa 100644 --- a/src/Shared/FileSystem/WindowsFileSystem.cs +++ b/src/Shared/FileSystem/WindowsFileSystem.cs @@ -59,7 +59,7 @@ public override bool FileExists(string path) return NativeMethodsShared.FileExistsWindows(path); } - public override bool DirectoryEntryExists(string path) + public override bool FileOrDirectoryExists(string path) { return NativeMethodsShared.FileOrDirectoryExistsWindows(path); } diff --git a/src/Shared/FileUtilities.cs b/src/Shared/FileUtilities.cs index 707657933d1..76155b33800 100644 --- a/src/Shared/FileUtilities.cs +++ b/src/Shared/FileUtilities.cs @@ -586,7 +586,7 @@ internal static bool LooksLikeUnixFilePath(ReadOnlySpan value, string base ReadOnlySpan directory = value.Slice(0, directoryLength); return (shouldCheckDirectory && DefaultFileSystem.DirectoryExists(Path.Combine(baseDirectory, directory.ToString()))) - || (shouldCheckFileOrDirectory && DefaultFileSystem.DirectoryEntryExists(value.ToString())); + || (shouldCheckFileOrDirectory && DefaultFileSystem.FileOrDirectoryExists(value.ToString())); } #endif @@ -951,8 +951,8 @@ internal static bool FileOrDirectoryExistsNoThrow(string fullPath, IFileSystem f fileSystem ??= DefaultFileSystem; return Traits.Instance.CacheFileExistence - ? FileExistenceCache.GetOrAdd(fullPath, fileSystem.DirectoryEntryExists) - : fileSystem.DirectoryEntryExists(fullPath); + ? FileExistenceCache.GetOrAdd(fullPath, fileSystem.FileOrDirectoryExists) + : fileSystem.FileOrDirectoryExists(fullPath); } catch { @@ -1078,7 +1078,12 @@ internal static string MakeRelative(string basePath, string path) { sb.Append(splitPath[i]).Append(Path.DirectorySeparatorChar); } - sb.Length--; + + if (fullPath[fullPath.Length - 1] != Path.DirectorySeparatorChar) + { + sb.Length--; + } + return StringBuilderCache.GetStringAndRelease(sb); } diff --git a/src/Shared/FrameworkLocationHelper.cs b/src/Shared/FrameworkLocationHelper.cs index e567fc1f64c..afdbc7fefec 100644 --- a/src/Shared/FrameworkLocationHelper.cs +++ b/src/Shared/FrameworkLocationHelper.cs @@ -307,6 +307,25 @@ internal static class FrameworkLocationHelper dotNetFrameworkVersion472, dotNetFrameworkVersion48, }), + + // VS17 + new VisualStudioSpec(visualStudioVersion170, "NETFXSDK\\{0}", "v10.0", "InstallationFolder", new [] + { + dotNetFrameworkVersion11, + dotNetFrameworkVersion20, + dotNetFrameworkVersion35, + dotNetFrameworkVersion40, + dotNetFrameworkVersion45, + dotNetFrameworkVersion451, + dotNetFrameworkVersion452, + dotNetFrameworkVersion46, + dotNetFrameworkVersion461, + dotNetFrameworkVersion462, + dotNetFrameworkVersion47, + dotNetFrameworkVersion471, + dotNetFrameworkVersion472, + dotNetFrameworkVersion48, + }), }; #if FEATURE_WIN32_REGISTRY @@ -480,8 +499,8 @@ private static string FallbackDotNetFrameworkSdkInstallPath if (EnvironmentUtilities.Is64BitProcess && s_fallbackDotNetFrameworkSdkInstallPath == null) { - // Since we're 64-bit, what we just checked was the 64-bit fallback key -- so now let's - // check the 32-bit one too, just in case. + // Since we're 64-bit, what we just checked was the 64-bit fallback key -- so now let's + // check the 32-bit one too, just in case. s_fallbackDotNetFrameworkSdkInstallPath = FindRegistryValueUnderKey( fallbackDotNetFrameworkSdkRegistryInstallPath, @@ -751,7 +770,7 @@ DotNetFrameworkArchitecture architecture return directoryExists(frameworkPath) ? frameworkPath : null; } - // If the COMPLUS variables are set, they override everything -- that's the directory we want. + // If the COMPLUS variables are set, they override everything -- that's the directory we want. string complusInstallRoot = Environment.GetEnvironmentVariable("COMPLUS_INSTALLROOT"); string complusVersion = Environment.GetEnvironmentVariable("COMPLUS_VERSION"); @@ -761,7 +780,7 @@ DotNetFrameworkArchitecture architecture } // If the current runtime starts with correct prefix, then this is the runtime we want to use. - // However, only if we're requesting current architecture -- otherwise, the base path may be different, so we'll need to look it up. + // However, only if we're requesting current architecture -- otherwise, the base path may be different, so we'll need to look it up. string leaf = Path.GetFileName(currentRuntimePath); if (leaf.StartsWith(prefix, StringComparison.Ordinal) && architecture == DotNetFrameworkArchitecture.Current) { @@ -777,19 +796,19 @@ DotNetFrameworkArchitecture architecture if (indexOfFramework64 != -1 && architecture == DotNetFrameworkArchitecture.Bitness32) { - // need to get rid of just the 64, but want to look up 'Framework64' rather than '64' to avoid the case where - // the path is something like 'C:\MyPath\64\Framework64'. 9 = length of 'Framework', to make the index match - // the location of the '64'. + // need to get rid of just the 64, but want to look up 'Framework64' rather than '64' to avoid the case where + // the path is something like 'C:\MyPath\64\Framework64'. 9 = length of 'Framework', to make the index match + // the location of the '64'. int indexOf64 = indexOfFramework64 + 9; string tempLocation = baseLocation; baseLocation = tempLocation.Substring(0, indexOf64) + tempLocation.Substring(indexOf64 + 2, tempLocation.Length - indexOf64 - 2); } else if (indexOfFramework64 == -1 && architecture == DotNetFrameworkArchitecture.Bitness64) { - // need to add 64 -- since this is a heuristic, we assume that we just need to append. + // need to add 64 -- since this is a heuristic, we assume that we just need to append. baseLocation += "64"; } - // we don't need to do anything if it's DotNetFrameworkArchitecture.Current. + // we don't need to do anything if it's DotNetFrameworkArchitecture.Current. string[] directories; @@ -799,7 +818,7 @@ DotNetFrameworkArchitecture architecture } else { - // If we can't even find the base path, might as well give up now. + // If we can't even find the base path, might as well give up now. return null; } @@ -813,7 +832,7 @@ DotNetFrameworkArchitecture architecture // The intention here is to choose the alphabetical maximum. string max = directories[0]; - // the max.EndsWith condition: pre beta 2 versions of v3.5 have build number like v3.5.20111. + // the max.EndsWith condition: pre beta 2 versions of v3.5 have build number like v3.5.20111. // This was removed in beta2 // We should favor \v3.5 over \v3.5.xxxxx // versions previous to 2.0 have .xxxx version numbers. 3.0 and 3.5 do not. @@ -874,15 +893,15 @@ internal static string GenerateProgramFiles64() string programFilesX64; if (string.Equals(programFiles, programFiles32)) { - // either we're in a 32-bit window, or we're on a 32-bit machine. + // either we're in a 32-bit window, or we're on a 32-bit machine. // if we're on a 32-bit machine, ProgramW6432 won't exist - // if we're on a 64-bit machine, ProgramW6432 will point to the correct Program Files. + // if we're on a 64-bit machine, ProgramW6432 will point to the correct Program Files. programFilesX64 = Environment.GetEnvironmentVariable("ProgramW6432"); } else { - // 64-bit window on a 64-bit machine; %ProgramFiles% points to the 64-bit - // Program Files already. + // 64-bit window on a 64-bit machine; %ProgramFiles% points to the 64-bit + // Program Files already. programFilesX64 = programFiles; } @@ -1141,8 +1160,8 @@ private static void RedirectVersionsIfNecessary(ref Version dotNetFrameworkVersi if (dotNetFrameworkVersion == dotNetFrameworkVersion35 && visualStudioVersion > visualStudioVersion110) { - // Fall back to Dev11 location -- 3.5 tools MSI was reshipped unchanged, so there - // essentially are no 12-specific 3.5 tools. + // Fall back to Dev11 location -- 3.5 tools MSI was reshipped unchanged, so there + // essentially are no 12-specific 3.5 tools. visualStudioVersion = visualStudioVersion110; return; } @@ -1348,8 +1367,8 @@ public virtual string GetPathToDotNetFramework(DotNetFrameworkArchitecture archi } #if FEATURE_WIN32_REGISTRY - // Otherwise, check to see if we're even installed. If not, return null -- no point in setting the static - // variables to null when that's what they are already. + // Otherwise, check to see if we're even installed. If not, return null -- no point in setting the static + // variables to null when that's what they are already. if (NativeMethodsShared.IsWindows && !CheckForFrameworkInstallation( this._dotNetFrameworkRegistryKey, this._dotNetFrameworkSetupRegistryInstalledName @@ -1422,7 +1441,7 @@ public virtual string GetPathToDotNetFrameworkSdkTools(VisualStudioSpec visualSt // For the Dev10 SDK, we check the registry that corresponds to the current process' bitness, rather than // always the 32-bit one the way we do for Dev11 and onward, since that's what we did in Dev10 as well. - // As of Dev11, the SDK reg keys are installed in the 32-bit registry. + // As of Dev11, the SDK reg keys are installed in the 32-bit registry. RegistryView registryView = visualStudioSpec.Version == visualStudioVersion100 ? RegistryView.Default : RegistryView.Registry32; generatedPathToDotNetFrameworkSdkTools = FindRegistryValueUnderKey( @@ -1537,7 +1556,7 @@ public virtual string GetPathToWindowsSdk() string registryPath = string.Join(@"\", MicrosoftSDKsRegistryKey, "Windows", visualStudioSpec.WindowsSdkRegistryKey); - // As of Dev11, the SDK reg keys are installed in the 32-bit registry. + // As of Dev11, the SDK reg keys are installed in the 32-bit registry. this._pathToWindowsSdk = FindRegistryValueUnderKey( registryPath, visualStudioSpec.WindowsSdkRegistryInstallationFolderName, diff --git a/src/Shared/ITranslator.cs b/src/Shared/ITranslator.cs index b1acb85ec2f..c3360d19224 100644 --- a/src/Shared/ITranslator.cs +++ b/src/Shared/ITranslator.cs @@ -62,7 +62,7 @@ internal enum TranslationDirection /// that by ensuring a single Translate method on a given object can handle both reads and /// writes without referencing any field more than once. /// - internal interface ITranslator + internal interface ITranslator : IDisposable { /// /// Returns the current serialization mode. @@ -301,6 +301,8 @@ void TranslateArray(ref T[] array) void TranslateDictionary(ref IDictionary dictionary, NodePacketCollectionCreator> collectionCreator); + void TranslateDictionary(ref Dictionary dictionary, StringComparer comparer); + void TranslateDictionary(ref IDictionary dictionary, ObjectTranslator keyTranslator, ObjectTranslator valueTranslator, NodePacketCollectionCreator> dictionaryCreator); /// diff --git a/src/Shared/InterningBinaryReader.cs b/src/Shared/InterningBinaryReader.cs index 40589a0becb..389450a2242 100644 --- a/src/Shared/InterningBinaryReader.cs +++ b/src/Shared/InterningBinaryReader.cs @@ -5,6 +5,11 @@ using System.Text; using System.IO; using System.Diagnostics; +using System.Threading; + +#if !CLR2COMPATIBILITY +using System.Buffers; +#endif using ErrorUtilities = Microsoft.Build.Shared.ErrorUtilities; @@ -26,11 +31,23 @@ internal class InterningBinaryReader : BinaryReader private const int MaxCharsBuffer = 20000; #endif + /// + /// A cache of recently used buffers. This is a pool of size 1 to avoid allocating moderately sized + /// objects repeatedly. Used in scenarios that don't have a good context to attach + /// a shared buffer to. + /// + private static Buffer s_bufferPool; + /// /// Shared buffer saves allocating these arrays many times. /// private Buffer _buffer; + /// + /// True if is owned by this instance, false if it was passed by the caller. + /// + private bool _isPrivateBuffer; + /// /// The decoder used to translate from UTF8 (or whatever). /// @@ -39,7 +56,7 @@ internal class InterningBinaryReader : BinaryReader /// /// Comment about constructing. /// - private InterningBinaryReader(Stream input, Buffer buffer) + private InterningBinaryReader(Stream input, Buffer buffer, bool isPrivateBuffer) : base(input, Encoding.UTF8) { if (input == null) @@ -48,6 +65,7 @@ private InterningBinaryReader(Stream input, Buffer buffer) } _buffer = buffer; + _isPrivateBuffer = isPrivateBuffer; _decoder = Encoding.UTF8.GetDecoder(); } @@ -57,6 +75,7 @@ private InterningBinaryReader(Stream input, Buffer buffer) /// override public String ReadString() { + char[] resultBuffer = null; try { MemoryStream memoryStream = this.BaseStream as MemoryStream; @@ -80,7 +99,6 @@ override public String ReadString() } char[] charBuffer = _buffer.CharBuffer; - char[] resultBuffer = null; do { readLength = ((stringLength - currPos) > MaxCharsBuffer) ? MaxCharsBuffer : (stringLength - currPos); @@ -132,31 +150,82 @@ override public String ReadString() charsRead = _decoder.GetChars(rawBuffer, rawPosition, n, charBuffer, 0); return Strings.WeakIntern(charBuffer.AsSpan(0, charsRead)); } - +#if !CLR2COMPATIBILITY + resultBuffer ??= ArrayPool.Shared.Rent(stringLength); // Actual string length in chars may be smaller. +#else + // Since NET35 is only used in rare TaskHost processes, we decided to leave it as-is. resultBuffer ??= new char[stringLength]; // Actual string length in chars may be smaller. +#endif charsRead += _decoder.GetChars(rawBuffer, rawPosition, n, resultBuffer, charsRead); currPos += n; } while (currPos < stringLength); - return Strings.WeakIntern(resultBuffer.AsSpan(0, charsRead)); + var retval = Strings.WeakIntern(resultBuffer.AsSpan(0, charsRead)); + + return retval; } catch (Exception e) { Debug.Assert(false, e.ToString()); throw; } +#if !CLR2COMPATIBILITY + finally + { + // resultBuffer shall always be either Rented or null + if (resultBuffer != null) + { + ArrayPool.Shared.Return(resultBuffer); + } + } +#endif } /// /// A shared buffer to avoid extra allocations in InterningBinaryReader. /// + /// + /// The caller is responsible for managing the lifetime of the returned buffer and for passing it to . + /// internal static SharedReadBuffer CreateSharedBuffer() { return new Buffer(); } + /// + /// Gets a buffer from the pool or creates a new one. + /// + /// The . Should be returned to the pool after we're done with it. + private static Buffer GetPooledBuffer() + { + Buffer buffer = Interlocked.Exchange(ref s_bufferPool, null); + if (buffer != null) + { + return buffer; + } + return new Buffer(); + } + +#region IDisposable pattern + + /// + /// Returns our buffer to the pool if we were not passed one by the caller. + /// + protected override void Dispose(bool disposing) + { + if (_isPrivateBuffer) + { + // If we created this buffer then try to return it to the pool. If s_bufferPool is non-null we leave it alone, + // the idea being that it's more likely to have lived longer than our buffer. + Interlocked.CompareExchange(ref s_bufferPool, _buffer, null); + } + base.Dispose(disposing); + } + +#endregion + /// /// Create a BinaryReader. It will either be an interning reader or standard binary reader /// depending on whether the interning reader is possible given the buffer and stream. @@ -164,13 +233,11 @@ internal static SharedReadBuffer CreateSharedBuffer() internal static BinaryReader Create(Stream stream, SharedReadBuffer sharedBuffer) { Buffer buffer = (Buffer)sharedBuffer; - - if (buffer == null) + if (buffer != null) { - buffer = new Buffer(); + return new InterningBinaryReader(stream, buffer, false); } - - return new InterningBinaryReader(stream, buffer); + return new InterningBinaryReader(stream, GetPooledBuffer(), true); } /// @@ -178,13 +245,14 @@ internal static BinaryReader Create(Stream stream, SharedReadBuffer sharedBuffer /// private class Buffer : SharedReadBuffer { + private char[] _charBuffer; + private byte[] _byteBuffer; + /// /// Yes, we are constructing. /// internal Buffer() { - this.CharBuffer = new char[MaxCharsBuffer]; - this.ByteBuffer = new byte[Encoding.UTF8.GetMaxByteCount(MaxCharsBuffer)]; } /// @@ -192,8 +260,11 @@ internal Buffer() /// internal char[] CharBuffer { - get; - private set; + get + { + _charBuffer ??= new char[MaxCharsBuffer]; + return _charBuffer; + } } /// @@ -201,8 +272,11 @@ internal char[] CharBuffer /// internal byte[] ByteBuffer { - get; - private set; + get + { + _byteBuffer ??= new byte[Encoding.UTF8.GetMaxByteCount(MaxCharsBuffer)]; + return _byteBuffer; + } } } } diff --git a/src/Shared/MSBuildLoadContext.cs b/src/Shared/MSBuildLoadContext.cs index f080c2e05a9..1de3e06bced 100644 --- a/src/Shared/MSBuildLoadContext.cs +++ b/src/Shared/MSBuildLoadContext.cs @@ -29,9 +29,6 @@ internal class MSBuildLoadContext : AssemblyLoadContext "Microsoft.Build.Utilities.Core", }.ToImmutableHashSet(); - internal static readonly string[] Extensions = new[] { "ni.dll", "ni.exe", "dll", "exe" }; - - public MSBuildLoadContext(string assemblyPath) : base($"MSBuild plugin {assemblyPath}") { @@ -56,11 +53,9 @@ public MSBuildLoadContext(string assemblyPath) // bare search directory if that fails. : new[] { assemblyName.CultureName, string.Empty }) { - foreach (var extension in Extensions) - { var candidatePath = Path.Combine(_directory, cultureSubfolder, - $"{assemblyName.Name}.{extension}"); + $"{assemblyName.Name}.dll"); if (!FileSystems.Default.FileExists(candidatePath)) { @@ -74,7 +69,6 @@ public MSBuildLoadContext(string assemblyPath) } return LoadFromAssemblyPath(candidatePath); - } } // If the Assembly is provided via a file path, the following rules are used to load the assembly: @@ -83,7 +77,7 @@ public MSBuildLoadContext(string assemblyPath) // into the default ALC (so it's shared with other uses). var assemblyNameInExecutableDirectory = Path.Combine(BuildEnvironmentHelper.Instance.CurrentMSBuildToolsDirectory, - assemblyName.Name!); + $"{assemblyName.Name}.dll"); if (FileSystems.Default.FileExists(assemblyNameInExecutableDirectory)) { diff --git a/src/Shared/NativeMethodsShared.cs b/src/Shared/NativeMethodsShared.cs index 42e8a3ead07..09314174d36 100644 --- a/src/Shared/NativeMethodsShared.cs +++ b/src/Shared/NativeMethodsShared.cs @@ -514,9 +514,6 @@ public static int GetLogicalCoreCount() // https://github.com/dotnet/runtime/issues/29686 // so always double-check it. if (IsWindows -#if !CLR2COMPATIBILITY && !MICROSOFT_BUILD_ENGINE_OM_UNITTESTS - && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave16_8) -#endif #if NETFRAMEWORK // .NET Framework calls Windows APIs that have a core count limit (32/64 depending on process bitness). // So if we get a high core count on full framework, double-check it. @@ -635,6 +632,9 @@ internal static bool IsMaxPathLegacyWindows() } } + // CA1416 warns about code that can only run on Windows, but we verified we're running on Windows before this. + // This is the most reasonable way to resolve this part because other ways would require ifdef'ing on NET472. +#pragma warning disable CA1416 private static bool IsLongPathsEnabledRegistry() { using (RegistryKey fileSystemKey = Registry.LocalMachine.OpenSubKey(WINDOWS_FILE_SYSTEM_REGISTRY_KEY)) @@ -643,6 +643,7 @@ private static bool IsLongPathsEnabledRegistry() return fileSystemKey != null && Convert.ToInt32(longPathsEnabledValue) == 1; } } +#pragma warning restore CA1416 /// /// Cached value for IsUnixLike (this method is called frequently during evaluation). diff --git a/src/Shared/NodeEndpointOutOfProcBase.cs b/src/Shared/NodeEndpointOutOfProcBase.cs index c58bc449a1c..3d468e23e95 100644 --- a/src/Shared/NodeEndpointOutOfProcBase.cs +++ b/src/Shared/NodeEndpointOutOfProcBase.cs @@ -31,7 +31,7 @@ internal abstract class NodeEndpointOutOfProcBase : INodeEndpoint { #region Private Data -#if NETCOREAPP2_1 || MONO +#if NETCOREAPP2_1_OR_GREATER || MONO /// /// The amount of time to wait for the client to connect to the host. /// @@ -43,11 +43,6 @@ internal abstract class NodeEndpointOutOfProcBase : INodeEndpoint /// private const int PipeBufferSize = 131072; - /// - /// Flag indicating if we should debug communications or not. - /// - private bool _debugCommunications = false; - /// /// The current communication status of the node. /// @@ -193,8 +188,6 @@ internal void InternalConstruct(string pipeName) { ErrorUtilities.VerifyThrowArgumentLength(pipeName, nameof(pipeName)); - _debugCommunications = (Environment.GetEnvironmentVariable("MSBUILDDEBUGCOMM") == "1"); - _status = LinkStatus.Inactive; _asyncDataMonitor = new object(); _sharedReadBuffer = InterningBinaryReader.CreateSharedBuffer(); @@ -386,7 +379,7 @@ private void PacketPumpProc() for (int i = 0; i < handshakeComponents.Length; i++) { int handshakePart = _pipeServer.ReadIntForHandshake(i == 0 ? (byte?)CommunicationsUtilities.handshakeVersion : null /* this will disconnect a < 16.8 host; it expects leading 00 or F5 or 06. 0x00 is a wildcard */ -#if NETCOREAPP2_1 || MONO +#if NETCOREAPP2_1_OR_GREATER || MONO , ClientConnectTimeout /* wait a long time for the handshake from this side */ #endif ); @@ -403,7 +396,7 @@ private void PacketPumpProc() if (gotValidConnection) { // To ensure that our handshake and theirs have the same number of bytes, receive and send a magic number indicating EOS. -#if NETCOREAPP2_1 || MONO +#if NETCOREAPP2_1_OR_GREATER || MONO _pipeServer.ReadEndOfHandshakeSignal(false, ClientConnectTimeout); /* wait a long time for the handshake from this side */ #else _pipeServer.ReadEndOfHandshakeSignal(false); diff --git a/src/Shared/ResourceUtilities.cs b/src/Shared/ResourceUtilities.cs index 545d6241d18..1cd325959fc 100644 --- a/src/Shared/ResourceUtilities.cs +++ b/src/Shared/ResourceUtilities.cs @@ -231,12 +231,14 @@ internal static string FormatString(string unformatted, params object[] args) // FormatResourceString calls ToString() which returns the full name of the type! foreach (object param in args) { - // Check it has a real implementation of ToString() + // Check it has a real implementation of ToString() and the type is not actually System.String if (param != null) { - if (String.Equals(param.GetType().ToString(), param.ToString(), StringComparison.Ordinal)) + if (string.Equals(param.GetType().ToString(), param.ToString(), StringComparison.Ordinal) && + param.GetType() != typeof(string)) { - ErrorUtilities.ThrowInternalError("Invalid resource parameter type, was {0}", param.GetType().FullName); + ErrorUtilities.ThrowInternalError("Invalid resource parameter type, was {0}", + param.GetType().FullName); } } } diff --git a/src/Shared/Resources/xlf/Strings.shared.de.xlf b/src/Shared/Resources/xlf/Strings.shared.de.xlf index 95694a52226..28e73b49c1a 100644 --- a/src/Shared/Resources/xlf/Strings.shared.de.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.de.xlf @@ -122,7 +122,7 @@ MSB4077: The "{0}" task has been marked with the attribute LoadInSeparateAppDomain, but does not derive from MarshalByRefObject. Check that the task derives from MarshalByRefObject or AppDomainIsolatedTask. - MSB4077: Die Aufgabe "{0}" wurde mit dem LoadInSeparateAppDomain-Attribut markiert, ist jedoch nicht von MarshalByRefObject abgeleitet. Vergewissern Sie sich, dass die Aufgabe von MarshalByRefObject oder AppDomainIsolatedTask abgeleitet wird. + MSB4077: Die Aufgabe "{0}" wurde mit dem LoadInSeparateAppDomain-Attribut markiert, ist jedoch nicht von MarshalByRefObject abgeleitet. Stellen Sie sicher, dass die Aufgabe von MarshalByRefObject oder AppDomainIsolatedTask abgeleitet wird. {StrBegin="MSB4077: "}LOCALIZATION: <LoadInSeparateAppDomain>, <MarshalByRefObject>, <AppDomainIsolatedTask> should not be localized. diff --git a/src/Shared/Resources/xlf/Strings.shared.en.xlf b/src/Shared/Resources/xlf/Strings.shared.en.xlf deleted file mode 100644 index 62bf77f50b8..00000000000 --- a/src/Shared/Resources/xlf/Strings.shared.en.xlf +++ /dev/null @@ -1,313 +0,0 @@ - - - - - - MSB4188: Build was canceled. - MSB4188: Build was canceled. - {StrBegin="MSB4188: "} Error when the build stops suddenly for some reason. For example, because a child node died. - - - MSB5022: The MSBuild task host does not support running tasks that perform IBuildEngine callbacks. If you wish to perform these operations, please run your task in the core MSBuild process instead. A task will automatically execute in the task host if the UsingTask has been attributed with a "Runtime" or "Architecture" value, or the task invocation has been attributed with an "MSBuildRuntime" or "MSBuildArchitecture" value, that does not match the current runtime or architecture of MSBuild. - MSB5022: The MSBuild task host does not support running tasks that perform IBuildEngine callbacks. If you wish to perform these operations, please run your task in the core MSBuild process instead. A task will automatically execute in the task host if the UsingTask has been attributed with a "Runtime" or "Architecture" value, or the task invocation has been attributed with an "MSBuildRuntime" or "MSBuildArchitecture" value, that does not match the current runtime or architecture of MSBuild. - {StrBegin="MSB5022: "} "Runtime", "Architecture", "MSBuildRuntime", and "MSBuildArchitecture" are all attributes in the project file, and thus should not be localized. - - - Build started. - Build started. - - - - MSB4008: A conflicting assembly for the task assembly "{0}" has been found at "{1}". - MSB4008: A conflicting assembly for the task assembly "{0}" has been found at "{1}". - {StrBegin="MSB4008: "}UE: This message is shown when the type/class of a task cannot be resolved uniquely from a single assembly. - - - Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored. - Event type "{0}" was expected to be serializable using the .NET serializer. The event was not serializable and has been ignored. - - - - {0} ({1},{2}) - {0} ({1},{2}) - A file location to be embedded in a string. - - - Making the following modifications to the environment received from the parent node before applying it to the task host: - Making the following modifications to the environment received from the parent node before applying it to the task host: - Only ever used when MSBuild is run under a "secret" environment variable switch, MSBuildTaskHostUpdateEnvironmentAndLog=1 - - - Setting '{0}' to '{1}' rather than the parent environment's value, '{2}'. - Setting '{0}' to '{1}' rather than the parent environment's value, '{2}'. - Only ever used when MSBuild is run under a "secret" environment variable switch, MSBuildTaskHostUpdateEnvironmentAndLog=1 - - - MSB4025: The project file could not be loaded. {0} - MSB4025: The project file could not be loaded. {0} - {StrBegin="MSB4025: "}UE: This message is shown when the project file given to the engine cannot be loaded because the filename/path is - invalid, or due to lack of permissions, or incorrect XML. The project filename is not part of the message because it is - provided separately to loggers. - LOCALIZATION: {0} is a localized message from the CLR/FX explaining why the project is invalid. - - - MSB4103: "{0}" is not a valid logger verbosity level. - MSB4103: "{0}" is not a valid logger verbosity level. - {StrBegin="MSB4103: "} - - - MSB4233: There was an exception while reading the log file: {0} - MSB4233: There was an exception while reading the log file: {0} - {StrBegin="MSB4233: "}This is shown when the Binary Logger can't read the log file. - - - MSBuild is expecting a valid "{0}" object. - MSBuild is expecting a valid "{0}" object. - - - - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - Path: {0} exceeds the OS max path limit. The fully qualified file name must be less than {1} characters. - - - - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". - MSB5028: Solution filter file at "{0}" includes project "{1}" that is not in the solution file at "{2}". - {StrBegin="MSB5028: "}UE: The project filename is provided separately to loggers. - - - MSB5025: Json in solution filter file "{0}" is incorrectly formatted. - MSB5025: Json in solution filter file "{0}" is incorrectly formatted. - {StrBegin="MSB5025: "}UE: The solution filename is provided separately to loggers. - - - MSB5026: The solution filter file at "{0}" specifies there will be a solution file at "{1}", but that file does not exist. - MSB5026: The solution filter file at "{0}" specifies there will be a solution file at "{1}", but that file does not exist. - {StrBegin="MSB5026: "}UE: The solution filename is provided separately to loggers. - - - MSB5009: Error parsing the project "{0}" section with GUID: "{1}". It is nested under "{2}" but that project is not found in the solution. - MSB5009: Error parsing the project "{0}" section with GUID: "{1}". It is nested under "{2}" but that project is not found in the solution. - {StrBegin="MSB5009: "}UE: The solution filename is provided separately to loggers. - - - MSB4132: The tools version "{0}" is unrecognized. Available tools versions are {1}. - MSB4132: The tools version "{0}" is unrecognized. Available tools versions are {1}. - {StrBegin="MSB4132: "}LOCALIZATION: {1} contains a comma separated list. - - - MSB5016: The name "{0}" contains an invalid character "{1}". - MSB5016: The name "{0}" contains an invalid character "{1}". - {StrBegin="MSB5016: "} - - - "{0}" is a reserved item metadata, and cannot be modified or deleted. - "{0}" is a reserved item metadata, and cannot be modified or deleted. - UE: Tasks and OM users are not allowed to remove or change the value of the built-in metadata on items e.g. the meta-data "FullPath", "RelativeDir", etc. are reserved. - - - The string "{0}" cannot be converted to a boolean (true/false) value. - The string "{0}" cannot be converted to a boolean (true/false) value. - - - - MSB5003: Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} - MSB5003: Failed to create a temporary file. Temporary files folder is full or its path is incorrect. {0} - {StrBegin="MSB5003: "} - - - MSB5018: Failed to delete the temporary file "{0}". {1} - MSB5018: Failed to delete the temporary file "{0}". {1} - {StrBegin="MSB5018: "} - - - The item metadata "%({0})" cannot be applied to the path "{1}". {2} - The item metadata "%({0})" cannot be applied to the path "{1}". {2} - UE: This message is shown when the user tries to perform path manipulations using one of the built-in item metadata e.g. %(RootDir), on an item-spec that's not a valid path. LOCALIZATION: "{2}" is a localized message from a CLR/FX exception. - - - MSB4077: The "{0}" task has been marked with the attribute LoadInSeparateAppDomain, but does not derive from MarshalByRefObject. Check that the task derives from MarshalByRefObject or AppDomainIsolatedTask. - MSB4077: The "{0}" task has been marked with the attribute LoadInSeparateAppDomain, but does not derive from MarshalByRefObject. Check that the task derives from MarshalByRefObject or AppDomainIsolatedTask. - {StrBegin="MSB4077: "}LOCALIZATION: <LoadInSeparateAppDomain>, <MarshalByRefObject>, <AppDomainIsolatedTask> should not be localized. - - - .NET Framework version "{0}" is not supported. Please specify a value from the enumeration Microsoft.Build.Utilities.TargetDotNetFrameworkVersion. - .NET Framework version "{0}" is not supported. Please specify a value from the enumeration Microsoft.Build.Utilities.TargetDotNetFrameworkVersion. - - - - .NET Framework version "{0}" is not supported when explicitly targeting the Windows SDK, which is only supported on .NET 4.5 and later. Please specify a value from the enumeration Microsoft.Build.Utilities.TargetDotNetFrameworkVersion that is Version45 or above. - .NET Framework version "{0}" is not supported when explicitly targeting the Windows SDK, which is only supported on .NET 4.5 and later. Please specify a value from the enumeration Microsoft.Build.Utilities.TargetDotNetFrameworkVersion that is Version45 or above. - - - - Visual Studio version "{0}" is not supported. Please specify a value from the enumeration Microsoft.Build.Utilities.VisualStudioVersion. - Visual Studio version "{0}" is not supported. Please specify a value from the enumeration Microsoft.Build.Utilities.VisualStudioVersion. - - - - When attempting to generate a reference assembly path from the path "{0}" and the framework moniker "{1}" there was an error. {2} - When attempting to generate a reference assembly path from the path "{0}" and the framework moniker "{1}" there was an error. {2} - No Error code because this resource will be used in an exception. The error code is discarded if it is included - - - Could not find directory path: {0} - Could not find directory path: {0} - Directory must exist - - - You do not have access to: {0} - You do not have access to: {0} - Directory must have access - - - Schema validation - Schema validation - - UE: this fragment is used to describe errors that are caused by schema validation. For example, if a normal error is - displayed like this: "MSBUILD : error MSB0000: This is an error.", then an error from schema validation would look like this: - "MSBUILD : Schema validation error MSB0000: This is an error." - LOCALIZATION: This fragment needs to be localized. - - - - MSB5002: Terminating the task executable "{0}" because it did not finish within the specified limit of {1} milliseconds. - MSB5002: Terminating the task executable "{0}" because it did not finish within the specified limit of {1} milliseconds. - {StrBegin="MSB5002: "} - - - Parameter "{0}" cannot be null. - Parameter "{0}" cannot be null. - - - - Parameter "{0}" with assigned value "{1}" cannot have invalid path or invalid file characters. - Parameter "{0}" with assigned value "{1}" cannot have invalid path or invalid file characters. - - - - Parameter "{0}" cannot have zero length. - Parameter "{0}" cannot have zero length. - - - - Parameters "{0}" and "{1}" must have the same number of elements. - Parameters "{0}" and "{1}" must have the same number of elements. - - - - The resource string "{0}" for the "{1}" task cannot be found. Confirm that the resource name "{0}" is correctly spelled, and the resource exists in the task's assembly. - The resource string "{0}" for the "{1}" task cannot be found. Confirm that the resource name "{0}" is correctly spelled, and the resource exists in the task's assembly. - - - - The "{0}" task has not registered its resources. In order to use the "TaskLoggingHelper.FormatResourceString()" method this task needs to register its resources either during construction, or via the "TaskResources" property. - The "{0}" task has not registered its resources. In order to use the "TaskLoggingHelper.FormatResourceString()" method this task needs to register its resources either during construction, or via the "TaskResources" property. - LOCALIZATION: "TaskLoggingHelper.FormatResourceString()" and "TaskResources" should not be localized. - - - MSB5004: The solution file has two projects named "{0}". - MSB5004: The solution file has two projects named "{0}". - {StrBegin="MSB5004: "}UE: The solution filename is provided separately to loggers. - - - MSB5005: Error parsing project section for project "{0}". The project file name "{1}" contains invalid characters. - MSB5005: Error parsing project section for project "{0}". The project file name "{1}" contains invalid characters. - {StrBegin="MSB5005: "}UE: The solution filename is provided separately to loggers. - - - MSB5006: Error parsing project section for project "{0}". The project file name is empty. - MSB5006: Error parsing project section for project "{0}". The project file name is empty. - {StrBegin="MSB5006: "}UE: The solution filename is provided separately to loggers. - - - MSB5007: Error parsing the project configuration section in solution file. The entry "{0}" is invalid. - MSB5007: Error parsing the project configuration section in solution file. The entry "{0}" is invalid. - {StrBegin="MSB5007: "}UE: The solution filename is provided separately to loggers. - - - MSB5008: Error parsing the solution configuration section in solution file. The entry "{0}" is invalid. - MSB5008: Error parsing the solution configuration section in solution file. The entry "{0}" is invalid. - {StrBegin="MSB5008: "}UE: The solution filename is provided separately to loggers. - - - MSB5009: Error parsing the nested project section in solution file. - MSB5009: Error parsing the nested project section in solution file. - {StrBegin="MSB5009: "}UE: The solution filename is provided separately to loggers. - - - MSB5023: Error parsing the nested project section in solution file. A project with the GUID "{0}" is listed as being nested under project "{1}", but does not exist in the solution. - MSB5023: Error parsing the nested project section in solution file. A project with the GUID "{0}" is listed as being nested under project "{1}", but does not exist in the solution. - {StrBegin="MSB5023: "}UE: The solution filename is provided separately to loggers. - - - MSB5010: No file format header found. - MSB5010: No file format header found. - {StrBegin="MSB5010: "}UE: The solution filename is provided separately to loggers. - - - MSB5011: Parent project GUID not found in "{0}" project dependency section. - MSB5011: Parent project GUID not found in "{0}" project dependency section. - {StrBegin="MSB5011: "}UE: The solution filename is provided separately to loggers. - - - MSB5012: Unexpected end-of-file reached inside "{0}" project section. - MSB5012: Unexpected end-of-file reached inside "{0}" project section. - {StrBegin="MSB5012: "}UE: The solution filename is provided separately to loggers. - - - MSB5013: Error parsing a project section. - MSB5013: Error parsing a project section. - {StrBegin="MSB5013: "}UE: The solution filename is provided separately to loggers. - - - MSB5014: File format version is not recognized. MSBuild can only read solution files between versions {0}.0 and {1}.0, inclusive. - MSB5014: File format version is not recognized. MSBuild can only read solution files between versions {0}.0 and {1}.0, inclusive. - {StrBegin="MSB5014: "}UE: The solution filename is provided separately to loggers. - - - MSB5015: The properties could not be read from the WebsiteProperties section of the "{0}" project. - MSB5015: The properties could not be read from the WebsiteProperties section of the "{0}" project. - {StrBegin="MSB5015: "}UE: The solution filename is provided separately to loggers. - - - Unrecognized solution version "{0}", attempting to continue. - Unrecognized solution version "{0}", attempting to continue. - - - - Solution file - Solution file - UE: this fragment is used to describe errors found while parsing solution files. For example, if a normal error is - displayed like this: "MSBUILD : error MSB0000: This is an error.", then an error from solution parsing would look like this: - "MSBUILD : Solution file error MSB0000: This is an error." - LOCALIZATION: This fragment needs to be localized. - - - MSB5019: The project file is malformed: "{0}". {1} - MSB5019: The project file is malformed: "{0}". {1} - {StrBegin="MSB5019: "} - - - MSB5020: Could not load the project file: "{0}". {1} - MSB5020: Could not load the project file: "{0}". {1} - {StrBegin="MSB5020: "} - - - MSB5021: Terminating the task executable "{0}" and its child processes because the build was canceled. - MSB5021: Terminating the task executable "{0}" and its child processes because the build was canceled. - {StrBegin="MSB5021: "} - - - MSB5024: Could not determine a valid location to MSBuild. Try running this process from the Developer Command Prompt for Visual Studio. - MSB5024: Could not determine a valid location to MSBuild. Try running this process from the Developer Command Prompt for Visual Studio. - {StrBegin="MSB5024: "} - - - This collection is read-only. - This collection is read-only. - - - - - \ No newline at end of file diff --git a/src/Shared/Resources/xlf/Strings.shared.es.xlf b/src/Shared/Resources/xlf/Strings.shared.es.xlf index 4c2fef8096e..332d51f7961 100644 --- a/src/Shared/Resources/xlf/Strings.shared.es.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.es.xlf @@ -122,7 +122,7 @@ MSB4077: The "{0}" task has been marked with the attribute LoadInSeparateAppDomain, but does not derive from MarshalByRefObject. Check that the task derives from MarshalByRefObject or AppDomainIsolatedTask. - MSB4077: La tarea "{0}" se marcó con el atributo LoadInSeparateAppDomain, pero no deriva de MarshalByRefObject. Asegúrese de que la tarea deriva de MarshalByRefObject o de AppDomainIsolatedTask. + MSB4077: La tarea "{0}" se marcó con el atributo LoadInSeparateAppDomain, pero esta no deriva de MarshalByRefObject. Asegúrese de que la tarea deriva de MarshalByRefObject o de AppDomainIsolatedTask. {StrBegin="MSB4077: "}LOCALIZATION: <LoadInSeparateAppDomain>, <MarshalByRefObject>, <AppDomainIsolatedTask> should not be localized. diff --git a/src/Shared/Resources/xlf/Strings.shared.fr.xlf b/src/Shared/Resources/xlf/Strings.shared.fr.xlf index 62416e6a96a..98d0ecd2344 100644 --- a/src/Shared/Resources/xlf/Strings.shared.fr.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.fr.xlf @@ -122,7 +122,7 @@ MSB4077: The "{0}" task has been marked with the attribute LoadInSeparateAppDomain, but does not derive from MarshalByRefObject. Check that the task derives from MarshalByRefObject or AppDomainIsolatedTask. - MSB4077: La tâche "{0}" a été marquée avec l'attribut LoadInSeparateAppDomain, mais elle ne dérive pas de MarshalByRefObject. Vérifiez que la tâche dérive de MarshalByRefObject ou de AppDomainIsolatedTask. + MSB4077: la tâche "{0}" a été marquée avec l'attribut LoadInSeparateAppDomain, mais ne dérive pas de MarshalByRefObject. Vérifiez que la tâche dérive de MarshalByRefObject ou de AppDomainIsolatedTask. {StrBegin="MSB4077: "}LOCALIZATION: <LoadInSeparateAppDomain>, <MarshalByRefObject>, <AppDomainIsolatedTask> should not be localized. diff --git a/src/Shared/Resources/xlf/Strings.shared.ko.xlf b/src/Shared/Resources/xlf/Strings.shared.ko.xlf index dc190421451..7cd5d631a66 100644 --- a/src/Shared/Resources/xlf/Strings.shared.ko.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.ko.xlf @@ -122,7 +122,7 @@ MSB4077: The "{0}" task has been marked with the attribute LoadInSeparateAppDomain, but does not derive from MarshalByRefObject. Check that the task derives from MarshalByRefObject or AppDomainIsolatedTask. - MSB4077: "{0}" 작업이 LoadInSeparateAppDomain 특성으로 표시되었지만 MarshalByRefObject에서 파생되지 않습니다. 해당 작업이 MarshalByRefObject 또는 AppDomainIsolatedTask에서 파생되는지 확인하세요. + MSB4077: "{0}" 작업이 LoadInSeparateAppDomain 특성으로 표시되었지만 MarshalByRefObject에서 파생되지 않습니다. 해당 작업이 MarshalByRefObject 또는 AppDomainIsolatedTask에서 파생되는지 확인하십시오. {StrBegin="MSB4077: "}LOCALIZATION: <LoadInSeparateAppDomain>, <MarshalByRefObject>, <AppDomainIsolatedTask> should not be localized. diff --git a/src/Shared/Resources/xlf/Strings.shared.pt-BR.xlf b/src/Shared/Resources/xlf/Strings.shared.pt-BR.xlf index acf02be88fd..6fd1b77903b 100644 --- a/src/Shared/Resources/xlf/Strings.shared.pt-BR.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.pt-BR.xlf @@ -52,7 +52,7 @@ MSB4103: "{0}" is not a valid logger verbosity level. - MSB4103: "{0}" não é um nível válido de detalhamento de agente. + MSB4103: "{0}" não é um nível de detalhamento de agente de log válido. {StrBegin="MSB4103: "} diff --git a/src/Shared/Resources/xlf/Strings.shared.tr.xlf b/src/Shared/Resources/xlf/Strings.shared.tr.xlf index 73f58bb94c7..d6831788b90 100644 --- a/src/Shared/Resources/xlf/Strings.shared.tr.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.tr.xlf @@ -122,7 +122,7 @@ MSB4077: The "{0}" task has been marked with the attribute LoadInSeparateAppDomain, but does not derive from MarshalByRefObject. Check that the task derives from MarshalByRefObject or AppDomainIsolatedTask. - MSB4077: "{0}" görevi LoadInSeparateAppDomain özniteliğiyle işaretlenmiş, ancak MarshalByRefObject öğesinden türetilmiyor. Görevin MarshalByRefObject veya AppDomainIsolatedTask öğesinden türetilip türetilmediğini denetleyin. + MSB4077: "{0}" görevi LoadInSeparateAppDomain özniteliğiyle işaretlenmiş, ancak MarshalByRefObject öğesinden türetilmiyor. Görevin MarshalByRefObject veya AppDomainIsolatedTask öğesinden türetildiğini denetleyin. {StrBegin="MSB4077: "}LOCALIZATION: <LoadInSeparateAppDomain>, <MarshalByRefObject>, <AppDomainIsolatedTask> should not be localized. diff --git a/src/Shared/Resources/xlf/Strings.shared.zh-Hans.xlf b/src/Shared/Resources/xlf/Strings.shared.zh-Hans.xlf index 248af5346b3..b5468e75bcb 100644 --- a/src/Shared/Resources/xlf/Strings.shared.zh-Hans.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.zh-Hans.xlf @@ -1,4 +1,4 @@ - + @@ -122,7 +122,7 @@ MSB4077: The "{0}" task has been marked with the attribute LoadInSeparateAppDomain, but does not derive from MarshalByRefObject. Check that the task derives from MarshalByRefObject or AppDomainIsolatedTask. - MSB4077: “{0}”任务已标记为 LoadInSeparateAppDomain 特性,但并非派生自 MarshalByRefObject。请检查该任务是派生自 MarshalByRefObject 还是 AppDomainIsolatedTask。 + MSB4077: “{0}”任务已标记为 LoadInSeparateAppDomain 特性,但未派生自 MarshalByRefObject。请检查该任务是派生自 MarshalByRefObject 还是 AppDomainIsolatedTask。 {StrBegin="MSB4077: "}LOCALIZATION: <LoadInSeparateAppDomain>, <MarshalByRefObject>, <AppDomainIsolatedTask> should not be localized. diff --git a/src/Shared/Resources/xlf/Strings.shared.zh-Hant.xlf b/src/Shared/Resources/xlf/Strings.shared.zh-Hant.xlf index dccd588bfc1..893e286184a 100644 --- a/src/Shared/Resources/xlf/Strings.shared.zh-Hant.xlf +++ b/src/Shared/Resources/xlf/Strings.shared.zh-Hant.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Shared/TaskHostConfiguration.cs b/src/Shared/TaskHostConfiguration.cs index 1bcae1d7574..815eda222b3 100644 --- a/src/Shared/TaskHostConfiguration.cs +++ b/src/Shared/TaskHostConfiguration.cs @@ -78,6 +78,11 @@ internal class TaskHostConfiguration : INodePacket /// private string _taskLocation; + /// + /// Whether task inputs are logged. + /// + private bool _isTaskInputLoggingEnabled; + /// /// The set of parameters to apply to the task prior to execution. /// @@ -105,6 +110,7 @@ internal class TaskHostConfiguration : INodePacket /// Flag to continue with the build after a the task failed /// Name of the task. /// Location of the assembly the task is to be loaded from. + /// Whether task inputs are logged. /// Parameters to apply to the task. /// global properties for the current project. /// Warning codes to be treated as errors for the current project. @@ -124,6 +130,7 @@ internal class TaskHostConfiguration : INodePacket /// Flag to continue with the build after a the task failed /// Name of the task. /// Location of the assembly the task is to be loaded from. + /// Whether task inputs are logged. /// Parameters to apply to the task. /// global properties for the current project. /// Warning codes to be logged as errors for the current project. @@ -145,6 +152,7 @@ public TaskHostConfiguration bool continueOnError, string taskName, string taskLocation, + bool isTaskInputLoggingEnabled, IDictionary taskParameters, Dictionary globalParameters, ICollection warningsAsErrors, @@ -178,6 +186,7 @@ ICollection warningsAsMessages _continueOnError = continueOnError; _taskName = taskName; _taskLocation = taskLocation; + _isTaskInputLoggingEnabled = isTaskInputLoggingEnabled; _warningsAsErrors = warningsAsErrors; _warningsAsMessages = warningsAsMessages; @@ -324,6 +333,16 @@ public string TaskLocation { return _taskLocation; } } + /// + /// Returns if the build is configured to log all task inputs. + /// + public bool IsTaskInputLoggingEnabled + { + [DebuggerStepThrough] + get + { return _isTaskInputLoggingEnabled; } + } + /// /// Parameters to set on the instantiated task prior to execution. /// @@ -391,6 +410,7 @@ public void Translate(ITranslator translator) translator.Translate(ref _projectFileOfTask); translator.Translate(ref _taskName); translator.Translate(ref _taskLocation); + translator.Translate(ref _isTaskInputLoggingEnabled); translator.TranslateDictionary(ref _taskParameters, StringComparer.OrdinalIgnoreCase, TaskParameter.FactoryForDeserialization); translator.Translate(ref _continueOnError); translator.TranslateDictionary(ref _globalParameters, StringComparer.OrdinalIgnoreCase); diff --git a/src/Shared/TaskLoggingHelper.cs b/src/Shared/TaskLoggingHelper.cs index 9d9fdc1c1b4..4ee21110651 100644 --- a/src/Shared/TaskLoggingHelper.cs +++ b/src/Shared/TaskLoggingHelper.cs @@ -156,9 +156,9 @@ protected IBuildEngine BuildEngine /// public bool HasLoggedErrors { get; private set; } -#endregion + #endregion -#region Utility methods + #region Utility methods /// /// Extracts the message code (if any) prefixed to the given message string. Message code prefixes must match the @@ -235,9 +235,26 @@ public virtual string GetResourceMessage(string resourceName) string resourceString = FormatResourceString(resourceName, null); return resourceString; } -#endregion + #endregion + + #region Message logging methods + + /// + /// Returns if the build is configured to log all task inputs. + /// + public bool IsTaskInputLoggingEnabled => + BuildEngine is IBuildEngine10 buildEngine10 && buildEngine10.EngineServices.IsTaskInputLoggingEnabled; -#region Message logging methods + /// + /// Returns true if a message of given importance should be logged because it is possible that a logger consuming it exists. + /// + /// The importance to check. + /// True if messages of the given importance should be logged, false if it's guaranteed that such messages would be ignored. + public bool LogsMessagesOfImportance(MessageImportance importance) + { + return BuildEngine is not IBuildEngine10 buildEngine10 + || buildEngine10.EngineServices.LogsMessagesOfImportance(importance); + } /// /// Logs a message using the specified string. @@ -279,6 +296,10 @@ public void LogMessage(MessageImportance importance, string message, params obje ResourceUtilities.FormatString(message, messageArgs); } #endif + if (!LogsMessagesOfImportance(importance)) + { + return; + } BuildMessageEventArgs e = new BuildMessageEventArgs ( @@ -343,6 +364,11 @@ params object[] messageArgs // No lock needed, as BuildEngine methods from v4.5 onwards are thread safe. ErrorUtilities.VerifyThrowArgumentNull(message, nameof(message)); + if (!LogsMessagesOfImportance(importance)) + { + return; + } + // If BuildEngine is null, task attempted to log before it was set on it, // presumably in its constructor. This is not allowed, and all // we can do is throw. @@ -470,6 +496,11 @@ public void LogMessageFromResources(MessageImportance importance, string message // global state. ErrorUtilities.VerifyThrowArgumentNull(messageResourceName, nameof(messageResourceName)); + if (!LogsMessagesOfImportance(importance)) + { + return; + } + LogMessage(importance, GetResourceMessage(messageResourceName), messageArgs); #if DEBUG // Assert that the message does not contain an error code. Only errors and warnings @@ -552,6 +583,11 @@ public void LogCommandLine(MessageImportance importance, string commandLine) // No lock needed, as BuildEngine methods from v4.5 onwards are thread safe. ErrorUtilities.VerifyThrowArgumentNull(commandLine, nameof(commandLine)); + if (!LogsMessagesOfImportance(importance)) + { + return; + } + var e = new TaskCommandLineEventArgs(commandLine, TaskName, importance); // If BuildEngine is null, the task attempted to log before it was set on it, diff --git a/src/Shared/ToolsetElement.cs b/src/Shared/ToolsetElement.cs index 92dc5b6995a..8a3c7e8f192 100644 --- a/src/Shared/ToolsetElement.cs +++ b/src/Shared/ToolsetElement.cs @@ -7,6 +7,7 @@ using System.IO; using Microsoft.Build.Collections; using Microsoft.Build.Shared; +using Microsoft.Build.Utilities; namespace Microsoft.Build.Evaluation { @@ -15,11 +16,53 @@ namespace Microsoft.Build.Evaluation /// internal static class ToolsetConfigurationReaderHelpers { + /// + /// Lock for process wide ToolsetConfigurationSection section cache + /// + private static readonly object s_syncLock = new(); + + /// + /// Process wide ToolsetConfigurationSection section cache + /// + private static ToolsetConfigurationSection s_toolsetConfigurationSectionCache; + private static Configuration s_configurationOfCachedSection; + internal static ToolsetConfigurationSection ReadToolsetConfigurationSection(Configuration configuration) + { + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0)) + { + if (configuration == null) + { + return null; + } + + lock (s_syncLock) + { + // Cache 1st requested configuration section. In unit tests, different Configuration is provided for particular test cases. + // During runtime, however, only MSBuild exe configuration file is provided to read toolset configuration from, + // and modifying MSBuild exe configuration during lifetime of msbuild nodes is neither expected nor supported. + if (s_toolsetConfigurationSectionCache == null) + { + s_toolsetConfigurationSectionCache = GetToolsetConfigurationSection(configuration); + s_configurationOfCachedSection = configuration; + } + + return s_configurationOfCachedSection == configuration ? + s_toolsetConfigurationSectionCache : + GetToolsetConfigurationSection(configuration); + } + } + else + { + return GetToolsetConfigurationSection(configuration); + } + } + + private static ToolsetConfigurationSection GetToolsetConfigurationSection(Configuration configuration) { ToolsetConfigurationSection configurationSection = null; - // This will be null if the application config file does not have the following section + // This will be null if the application config file does not have the following section // definition for the msbuildToolsets section as the first child element. // //
diff --git a/src/Shared/Traits.cs b/src/Shared/Traits.cs index 09cca604107..d1fc45ffe12 100644 --- a/src/Shared/Traits.cs +++ b/src/Shared/Traits.cs @@ -27,11 +27,14 @@ public static Traits Instance public Traits() { EscapeHatches = new EscapeHatches(); + + DebugScheduler = DebugEngine || !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDDEBUGSCHEDULER")); + DebugNodeCommunication = DebugEngine || !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBUILDDEBUGCOMM")); } public EscapeHatches EscapeHatches { get; } - internal readonly string MSBuildDisableFeaturesFromVersion = Environment.GetEnvironmentVariable("MSBUILDDISABLEFEATURESFROMVERSION"); + internal readonly string MSBuildDisableFeaturesFromVersion = Environment.GetEnvironmentVariable("MSBUILDDISABLEFEATURESFROMVERSION"); /// /// Do not expand wildcards that match a certain pattern @@ -86,6 +89,15 @@ public Traits() /// public readonly int LogPropertyTracking = ParseIntFromEnvironmentVariableOrDefault("MsBuildLogPropertyTracking", 0); // Default to logging nothing via the property tracker. + /// + /// When evaluating items, this is the minimum number of items on the running list to use a dictionary-based remove optimization. + /// + public readonly int DictionaryBasedItemRemoveThreshold = ParseIntFromEnvironmentVariableOrDefault("MSBUILDDICTIONARYBASEDITEMREMOVETHRESHOLD", 100); + + public readonly bool DebugEngine = !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MSBuildDebugEngine")); + public readonly bool DebugScheduler; + public readonly bool DebugNodeCommunication; + private static int ParseIntFromEnvironmentVariableOrDefault(string environmentVariable, int defaultValue) { return int.TryParse(Environment.GetEnvironmentVariable(environmentVariable), out int result) diff --git a/src/Shared/TranslatorHelpers.cs b/src/Shared/TranslatorHelpers.cs index 9cab3485c97..c3e7d12b6ad 100644 --- a/src/Shared/TranslatorHelpers.cs +++ b/src/Shared/TranslatorHelpers.cs @@ -261,9 +261,6 @@ public static void Translate(this ITranslator translator, ref AssemblyName assem HashAlgorithm = hashAlgorithm, VersionCompatibility = versionCompatibility, CodeBase = codeBase, - // AssemblyName.KeyPair is not used anywhere, additionally StrongNameKeyPair is not supported in .net core 5- - // and throws platform not supported exception when serialized or deserialized - KeyPair = null, }; assemblyName.SetPublicKey(publicKey); diff --git a/src/Shared/UnitTests/FileMatcher_Tests.cs b/src/Shared/UnitTests/FileMatcher_Tests.cs index c3d200829e3..d8cf2eeed4c 100644 --- a/src/Shared/UnitTests/FileMatcher_Tests.cs +++ b/src/Shared/UnitTests/FileMatcher_Tests.cs @@ -74,7 +74,7 @@ public void GetFilesComplexGlobbingMatching(GetFilesComplexGlobbingMatchingInfo foreach (string fullPath in GetFilesComplexGlobbingMatchingInfo.FilesToCreate.Select(i => Path.Combine(testFolder.Path, i.ToPlatformSlash()))) { Directory.CreateDirectory(Path.GetDirectoryName(fullPath)); - + File.WriteAllBytes(fullPath, new byte[1]); } @@ -154,6 +154,7 @@ public class GetFilesComplexGlobbingMatchingInfo @"src\bar.cs", @"src\baz.cs", @"src\foo\foo.cs", + @"src\foo\licence", @"src\bar\bar.cs", @"src\baz\baz.cs", @"src\foo\inner\foo.cs", @@ -292,7 +293,7 @@ public static IEnumerable GetTestData() ExpectNoMatches = NativeMethodsShared.IsLinux, } }; - + #if !MONO // https://github.com/mono/mono/issues/8441 yield return new object[] { @@ -368,7 +369,8 @@ public static IEnumerable GetTestData() ExpectedMatches = new[] { @"readme.txt", - @"licence" + @"licence", + @"src\foo\licence", } } }; @@ -422,6 +424,30 @@ public static IEnumerable GetTestData() } }; + // Regression test for https://github.com/Microsoft/msbuild/issues/6502 + yield return new object[] + { + new GetFilesComplexGlobbingMatchingInfo + { + Include = @"src\**", + Excludes = new[] + { + @"**\foo\**", + }, + ExpectedMatches = new[] + { + @"src\foo.cs", + @"src\bar.cs", + @"src\baz.cs", + @"src\bar\bar.cs", + @"src\baz\baz.cs", + @"src\bar\inner\baz.cs", + @"src\bar\inner\baz\baz.cs", + }, + ExpectNoMatches = NativeMethodsShared.IsLinux, + } + }; + // Hits the early elimination of exclude file patterns that do not intersect with the include. // The exclude is redundant and can be eliminated before starting the file system walk. yield return new object[] @@ -1627,7 +1653,7 @@ public void ExcludeComplexPattern(string include, string[] exclude, string[] mat "", "", "", - "^(?)(?)(?)$", + "^(?)(?)$", false, true )] @@ -1697,7 +1723,7 @@ public void ExcludeComplexPattern(string include, string[] exclude, string[] mat "", @"*fo?ba?\", "*fo?ba?", - @"^(?)(?[^/\\]*fo.ba.[/\\]+)(?[^/\\]*fo.ba.)$", + @"^(?[^/\\]*fo.ba.[/\\]+)(?[^/\\]*fo.ba.)$", true, true )] @@ -1707,7 +1733,7 @@ public void ExcludeComplexPattern(string include, string[] exclude, string[] mat "", "", "?oo*.", - @"^(?)(?)(?[^\.].oo[^\.]*)$", + @"^(?)(?[^\.].oo[^\.]*)$", false, true )] @@ -1717,7 +1743,7 @@ public void ExcludeComplexPattern(string include, string[] exclude, string[] mat "", "", "*.*foo*.*", - @"^(?)(?)(?[^/\\]*foo[^/\\]*)$", + @"^(?)(?[^/\\]*foo[^/\\]*)$", false, true )] @@ -1727,7 +1753,7 @@ public void ExcludeComplexPattern(string include, string[] exclude, string[] mat @"\foo///bar\\\", @"?foo///bar\\\", "foo", - @"^(?[/\\]+foo[/\\]+bar[/\\]+)(?.foo[/\\]+bar[/\\]+)(?foo)$", + @"^[/\\]+foo[/\\]+bar[/\\]+(?.foo[/\\]+bar[/\\]+)(?foo)$", true, true )] @@ -1737,7 +1763,7 @@ public void ExcludeComplexPattern(string include, string[] exclude, string[] mat @"\./.\foo/.\./bar\./.\", @"?foo/.\./bar\./.\", "foo", - @"^(?[/\\]+foo[/\\]+bar[/\\]+)(?.foo[/\\]+bar[/\\]+)(?foo)$", + @"^[/\\]+foo[/\\]+bar[/\\]+(?.foo[/\\]+bar[/\\]+)(?foo)$", true, true )] @@ -1747,7 +1773,7 @@ public void ExcludeComplexPattern(string include, string[] exclude, string[] mat @"foo\", @"**/**\bar/**\**/foo\**/**\", "bar", - @"^(?foo[/\\]+)(?((.*/)|(.*\\)|())bar((/)|(\\)|(/.*/)|(/.*\\)|(\\.*\\)|(\\.*/))foo((/)|(\\)|(/.*/)|(/.*\\)|(\\.*\\)|(\\.*/)))(?bar)$", + @"^foo[/\\]+(?((.*/)|(.*\\)|())bar((/)|(\\)|(/.*/)|(/.*\\)|(\\.*\\)|(\\.*/))foo((/)|(\\)|(/.*/)|(/.*\\)|(\\.*\\)|(\\.*/)))(?bar)$", true, true )] @@ -1757,7 +1783,7 @@ public void ExcludeComplexPattern(string include, string[] exclude, string[] mat @"foo\\\.///", @"**\\\.///**\\\.///bar\\\.///**\\\.///**\\\.///foo\\\.///**\\\.///**\\\.///", "bar", - @"^(?foo[/\\]+)(?((.*/)|(.*\\)|())bar((/)|(\\)|(/.*/)|(/.*\\)|(\\.*\\)|(\\.*/))foo((/)|(\\)|(/.*/)|(/.*\\)|(\\.*\\)|(\\.*/)))(?bar)$", + @"^foo[/\\]+(?((.*/)|(.*\\)|())bar((/)|(\\)|(/.*/)|(/.*\\)|(\\.*\\)|(\\.*/))foo((/)|(\\)|(/.*/)|(/.*\\)|(\\.*\\)|(\\.*/)))(?bar)$", true, true )] @@ -1795,7 +1821,7 @@ bool expectedIsLegalFileSpec @"$()+.[^{\", @"?$()+.[^{\", "$()+.[^{", - @"^(?\$\(\)\+\.\[\^\{[/\\]+)(?.\$\(\)\+\.\[\^\{[/\\]+)(?\$\(\)\+\.\[\^\{)$", + @"^\$\(\)\+\.\[\^\{[/\\]+(?.\$\(\)\+\.\[\^\{[/\\]+)(?\$\(\)\+\.\[\^\{)$", true, true )] @@ -1805,7 +1831,7 @@ bool expectedIsLegalFileSpec @"\\\.\foo/", "", "bar", - @"^(?\\\\foo[/\\]+)(?)(?bar)$", + @"^\\\\foo[/\\]+(?)(?bar)$", false, true )] @@ -1838,7 +1864,7 @@ bool expectedIsLegalFileSpec @"$()+.[^{|/", @"?$()+.[^{|/", "$()+.[^{|", - @"^(?\$\(\)\+\.\[\^\{\|[/\\]+)(?.\$\(\)\+\.\[\^\{\|[/\\]+)(?\$\(\)\+\.\[\^\{\|)$", + @"^\$\(\)\+\.\[\^\{\|[/\\]+(?.\$\(\)\+\.\[\^\{\|[/\\]+)(?\$\(\)\+\.\[\^\{\|)$", true, true )] @@ -1848,7 +1874,7 @@ bool expectedIsLegalFileSpec @"///./foo/", "", "bar", - @"^(?[/\\]+foo[/\\]+)(?)(?bar)$", + @"^[/\\]+foo[/\\]+(?)(?bar)$", false, true )] @@ -2086,8 +2112,7 @@ private void GetMatchingDirectories(string[] candidates, string path, string pat int nextSlash = normalizedCandidate.IndexOfAny(FileMatcher.directorySeparatorCharacters, path.Length + 1); if (nextSlash != -1) { - - //UNC paths start with a \\ fragment. Match against \\ when path is empty (i.e., inside the current working directory) + // UNC paths start with a \\ fragment. Match against \\ when path is empty (i.e., inside the current working directory) string match = normalizedCandidate.StartsWith(@"\\") && string.IsNullOrEmpty(path) ? @"\\" : normalizedCandidate.Substring(0, nextSlash); @@ -2103,7 +2128,7 @@ private void GetMatchingDirectories(string[] candidates, string path, string pat directories.Add(FileMatcher.Normalize(match)); } else if // Match patterns like ?emp - ( + ( pattern.Substring(0, 1) == "?" && pattern.Length == baseMatch.Length ) @@ -2633,15 +2658,10 @@ public bool FileExists(string path) return FileSystems.Default.FileExists(path); } - public bool DirectoryEntryExists(string path) + public bool FileOrDirectoryExists(string path) { - return FileSystems.Default.DirectoryEntryExists(path); + return FileSystems.Default.FileOrDirectoryExists(path); } } } } - - - - - diff --git a/src/Shared/UnitTests/FileUtilities_Tests.cs b/src/Shared/UnitTests/FileUtilities_Tests.cs index 7080a2e7ccc..fa6087dffe9 100644 --- a/src/Shared/UnitTests/FileUtilities_Tests.cs +++ b/src/Shared/UnitTests/FileUtilities_Tests.cs @@ -97,6 +97,24 @@ public void MakeRelativeTests() Assert.Equal(@"\\host\path\file", FileUtilities.MakeRelative(@"c:\abc\def", @"\\host\path\file")); Assert.Equal(@"\\host\d$\file", FileUtilities.MakeRelative(@"c:\abc\def", @"\\host\d$\file")); Assert.Equal(@"..\fff\ggg.hh", FileUtilities.MakeRelative(@"c:\foo\bar\..\abc\cde", @"c:\foo\bar\..\abc\fff\ggg.hh")); + + /* Directories */ + Assert.Equal(@"def\", FileUtilities.MakeRelative(@"c:\abc\", @"c:\abc\def\")); + Assert.Equal(@"..\", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\")); + Assert.Equal(@"..\ttt\", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\ttt\")); + Assert.Equal(@".", FileUtilities.MakeRelative(@"c:\abc\def\", @"c:\abc\def\")); + + /* Directory + File */ + Assert.Equal(@"def", FileUtilities.MakeRelative(@"c:\abc\", @"c:\abc\def")); + Assert.Equal(@"..\..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\ghi")); + Assert.Equal(@"..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\xyz\", @"c:\abc\def\ghi")); + Assert.Equal(@"..\ghi", FileUtilities.MakeRelative(@"c:\abc\def\", @"c:\abc\ghi")); + + /* File + Directory */ + Assert.Equal(@"def\", FileUtilities.MakeRelative(@"c:\abc", @"c:\abc\def\")); + Assert.Equal(@"..\", FileUtilities.MakeRelative(@"c:\abc\def\xyz", @"c:\abc\def\")); + Assert.Equal(@"..\ghi\", FileUtilities.MakeRelative(@"c:\abc\def\xyz", @"c:\abc\def\ghi\")); + Assert.Equal(@".", FileUtilities.MakeRelative(@"c:\abc\def", @"c:\abc\def\")); } else { @@ -106,6 +124,25 @@ public void MakeRelativeTests() Assert.Equal(@"../ttt/foo.cpp", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ttt/foo.cpp")); Assert.Equal(@"foo.cpp", FileUtilities.MakeRelative(@"/abc/def", @"foo.cpp")); Assert.Equal(@"../fff/ggg.hh", FileUtilities.MakeRelative(@"/foo/bar/../abc/cde", @"/foo/bar/../abc/fff/ggg.hh")); + + /* Directories */ + Assert.Equal(@"def/", FileUtilities.MakeRelative(@"/abc/", @"/abc/def/")); + Assert.Equal(@"../", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/")); + Assert.Equal(@"../ttt/", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ttt/")); + Assert.Equal(@".", FileUtilities.MakeRelative(@"/abc/def/", @"/abc/def/")); + + /* Directory + File */ + Assert.Equal(@"def", FileUtilities.MakeRelative(@"/abc/", @"/abc/def")); + Assert.Equal(@"../../ghi", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/ghi")); + Assert.Equal(@"../ghi", FileUtilities.MakeRelative(@"/abc/def/xyz/", @"/abc/def/ghi")); + Assert.Equal(@"../ghi", FileUtilities.MakeRelative(@"/abc/def/", @"/abc/ghi")); + + /* File + Directory */ + Assert.Equal(@"def/", FileUtilities.MakeRelative(@"/abc", @"/abc/def/")); + Assert.Equal(@"../", FileUtilities.MakeRelative(@"/abc/def/xyz", @"/abc/def/")); + Assert.Equal(@"../ghi/", FileUtilities.MakeRelative(@"/abc/def/xyz", @"/abc/def/ghi/")); + Assert.Equal(@".", FileUtilities.MakeRelative(@"/abc/def", @"/abc/def/")); + } } diff --git a/src/Shared/UnitTests/ObjectModelHelpers.cs b/src/Shared/UnitTests/ObjectModelHelpers.cs index a9bdd429d16..9261b45785c 100644 --- a/src/Shared/UnitTests/ObjectModelHelpers.cs +++ b/src/Shared/UnitTests/ObjectModelHelpers.cs @@ -10,6 +10,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Threading; +using System.Threading.Tasks; using System.Xml; using Microsoft.Build.Construction; @@ -154,6 +155,40 @@ internal static void AssertItemEvaluationFromGenericItemEvaluator(Func globalProperties = null) { - var buildResult = _buildManager.BuildRequest( - new BuildRequestData(projectFile, - globalProperties ?? new Dictionary(), - MSBuildConstants.CurrentToolsVersion, - entryTargets ?? new string[0], - null)); + var buildTask = BuildProjectFileAsync(projectFile, entryTargets, globalProperties); + return buildTask.Result; + } + + public async Task BuildProjectFileAsync( + string projectFile, + string[] entryTargets = null, + Dictionary globalProperties = null) + { + var buildRequestData = new BuildRequestData(projectFile, + globalProperties ?? new Dictionary(), + MSBuildConstants.CurrentToolsVersion, + entryTargets ?? new string[0], + null); + + var completion = new TaskCompletionSource(); + + _buildManager.PendBuildRequest(buildRequestData).ExecuteAsync(submission => + { + completion.SetResult(submission.BuildResult); + }, null); - return buildResult; + return await completion.Task; } public GraphBuildResult BuildGraphSubmission(GraphBuildRequestData requestData) @@ -1971,44 +2021,38 @@ public void Dispose() internal class LoggingFileSystem : MSBuildFileSystemBase { - private readonly IFileSystem _wrappingFileSystem; private int _fileSystemCalls; public int FileSystemCalls => _fileSystemCalls; public ConcurrentDictionary ExistenceChecks { get; } = new ConcurrentDictionary(); - public LoggingFileSystem(IFileSystem wrappingFileSystem = null) - { - _wrappingFileSystem = wrappingFileSystem ?? FileSystems.Default; - } - public override TextReader ReadFile(string path) { IncrementCalls(ref _fileSystemCalls); - return _wrappingFileSystem.ReadFile(path); + return base.ReadFile(path); } public override Stream GetFileStream(string path, FileMode mode, FileAccess access, FileShare share) { IncrementCalls(ref _fileSystemCalls); - return _wrappingFileSystem.GetFileStream(path, mode, access, share); + return base.GetFileStream(path, mode, access, share); } public override string ReadFileAllText(string path) { IncrementCalls(ref _fileSystemCalls); - return _wrappingFileSystem.ReadFileAllText(path); + return base.ReadFileAllText(path); } public override byte[] ReadFileAllBytes(string path) { IncrementCalls(ref _fileSystemCalls); - return _wrappingFileSystem.ReadFileAllBytes(path); + return base.ReadFileAllBytes(path); } public override IEnumerable EnumerateFiles( @@ -2019,7 +2063,7 @@ public override IEnumerable EnumerateFiles( { IncrementCalls(ref _fileSystemCalls); - return _wrappingFileSystem.EnumerateFiles(path, searchPattern, searchOption); + return base.EnumerateFiles(path, searchPattern, searchOption); } public override IEnumerable EnumerateDirectories( @@ -2030,7 +2074,7 @@ public override IEnumerable EnumerateDirectories( { IncrementCalls(ref _fileSystemCalls); - return _wrappingFileSystem.EnumerateDirectories(path, searchPattern, searchOption); + return base.EnumerateDirectories(path, searchPattern, searchOption); } public override IEnumerable EnumerateFileSystemEntries( @@ -2041,21 +2085,21 @@ public override IEnumerable EnumerateFileSystemEntries( { IncrementCalls(ref _fileSystemCalls); - return _wrappingFileSystem.EnumerateFileSystemEntries(path, searchPattern, searchOption); + return base.EnumerateFileSystemEntries(path, searchPattern, searchOption); } public override FileAttributes GetAttributes(string path) { IncrementCalls(ref _fileSystemCalls); - return _wrappingFileSystem.GetAttributes(path); + return base.GetAttributes(path); } public override DateTime GetLastWriteTimeUtc(string path) { IncrementCalls(ref _fileSystemCalls); - return _wrappingFileSystem.GetLastWriteTimeUtc(path); + return base.GetLastWriteTimeUtc(path); } public override bool DirectoryExists(string path) @@ -2063,7 +2107,7 @@ public override bool DirectoryExists(string path) IncrementCalls(ref _fileSystemCalls); IncrementExistenceChecks(path); - return _wrappingFileSystem.DirectoryExists(path); + return base.DirectoryExists(path); } public override bool FileExists(string path) @@ -2071,19 +2115,19 @@ public override bool FileExists(string path) IncrementCalls(ref _fileSystemCalls); IncrementExistenceChecks(path); - return _wrappingFileSystem.FileExists(path); + return base.FileExists(path); } - private int _directoryEntryExistsCalls; - public int DirectoryEntryExistsCalls => _directoryEntryExistsCalls; + private int _fileOrDirectoryExistsCalls; + public int FileOrDirectoryExistsCalls => _fileOrDirectoryExistsCalls; public override bool FileOrDirectoryExists(string path) { IncrementCalls(ref _fileSystemCalls); - IncrementCalls(ref _directoryEntryExistsCalls); + IncrementCalls(ref _fileOrDirectoryExistsCalls); IncrementExistenceChecks(path); - return _wrappingFileSystem.DirectoryEntryExists(path); + return base.FileOrDirectoryExists(path); } private void IncrementCalls(ref int incremented) diff --git a/src/Shared/UnitTests/TestAssemblyInfo.cs b/src/Shared/UnitTests/TestAssemblyInfo.cs index e1e7ef66d5a..627aa0d465e 100644 --- a/src/Shared/UnitTests/TestAssemblyInfo.cs +++ b/src/Shared/UnitTests/TestAssemblyInfo.cs @@ -35,6 +35,8 @@ public MSBuildTestAssemblyFixture() _testEnvironment = TestEnvironment.Create(); + _testEnvironment.DoNotLaunchDebugger(); + // Reset the VisualStudioVersion environment variable. This will be set if tests are run from a VS command prompt. However, // if the environment variable is set, it will interfere with tests which set the SubToolsetVersion // (VerifySubToolsetVersionSetByConstructorOverridable), as the environment variable would take precedence. diff --git a/src/StringTools.UnitTests/SpanBasedStringBuilder_Tests.cs b/src/StringTools.UnitTests/SpanBasedStringBuilder_Tests.cs index 9be63b4b714..086d684231b 100644 --- a/src/StringTools.UnitTests/SpanBasedStringBuilder_Tests.cs +++ b/src/StringTools.UnitTests/SpanBasedStringBuilder_Tests.cs @@ -96,6 +96,28 @@ public void ReferenceEqualsReturnsExpectedValue() internableString.ReferenceEquals(str).ShouldBeFalse(); } + [Theory] + [InlineData("012345678")] // odd number of characters + [InlineData("0123456789")] // even number of characters + public void GetHashCodeIsStableRegardlessOfSpanLength(string testString) + { + int hashCode = new InternableString(testString).GetHashCode(); + + // Chop the string into 2-3 parts and verify that the hash code is unchanged. + for (int i = 0; i < testString.Length - 1; i++) + { + for (int j = i + 1; j < testString.Length; j++) + { + SpanBasedStringBuilder stringBuilder = new SpanBasedStringBuilder(); + stringBuilder.Append(testString.Substring(0, i)); + stringBuilder.Append(testString.Substring(i, j - i)); + stringBuilder.Append(testString.Substring(j)); + InternableString internableString = new InternableString(stringBuilder); + internableString.GetHashCode().ShouldBe(hashCode); + } + } + } + [Theory] [MemberData(nameof(TestData))] public void AppendAppendsString(InterningTestData.TestDatum datum) diff --git a/src/StringTools.UnitTests/StringTools.UnitTests.csproj b/src/StringTools.UnitTests/StringTools.UnitTests.csproj index e11fc1d60ce..b48cd46cb93 100644 --- a/src/StringTools.UnitTests/StringTools.UnitTests.csproj +++ b/src/StringTools.UnitTests/StringTools.UnitTests.csproj @@ -1,4 +1,4 @@ - + $(RuntimeOutputTargetFrameworks) $(RuntimeOutputPlatformTarget) @@ -12,6 +12,7 @@ + diff --git a/src/StringTools.UnitTests/WeakStringCache_Tests.cs b/src/StringTools.UnitTests/WeakStringCache_Tests.cs index bddfc60917b..41d4d931c95 100644 --- a/src/StringTools.UnitTests/WeakStringCache_Tests.cs +++ b/src/StringTools.UnitTests/WeakStringCache_Tests.cs @@ -84,8 +84,8 @@ private void AddStringsWithSameHashCode(int numberOfStrings) for (int i = 0; i < numberOfStrings; i++) { - string strPart2 = "1" + String.Concat(Enumerable.Repeat("4428939786", i)); - hashCodes[i] = AddString("Random string ", strPart2, (string cachedString) => + string strPart2 = string.Concat(Enumerable.Repeat("100570862200", i + 2)); + hashCodes[i] = AddString(string.Empty, strPart2, (string cachedString) => { _cache.GetDebugInfo().ShouldBe(new WeakStringCache.DebugInfo() { diff --git a/src/StringTools/InternableString.cs b/src/StringTools/InternableString.cs index ca8fa75ef48..15508eb6fea 100644 --- a/src/StringTools/InternableString.cs +++ b/src/StringTools/InternableString.cs @@ -265,6 +265,15 @@ public unsafe string ExpensiveConvertToString() } } } + + // The invariant that Length is the sum of span lengths is critical in this unsafe method. + // Violating it may lead to memory corruption and, since this code tends to run under a lock, + // to hangs caused by the lock getting orphaned. Attempt to detect that and throw now, + // before the corruption causes further problems. + if (destPtr != resultPtr + Length) + { + throw new InvalidOperationException($"Length of {Length} does not match the sum of span lengths of {destPtr - resultPtr}."); + } } return result; } @@ -299,18 +308,23 @@ public override string ToString() } /// - /// Implements the simple yet very decently performing djb2 hash function (xor version). + /// Implements the simple yet very decently performing djb2-like hash function (xor version) as inspired by + /// https://github.com/dotnet/runtime/blob/6262ae8e6a33abac569ab6086cdccc470c810ea4/src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs#L810-L840 /// /// A stable hashcode of the string represented by this instance. + /// + /// Unlike the BCL method, this implementation works only on two characters at a time to cut on the complexity with + /// characters that feed into the same operation but straddle multiple spans. Note that it must return the same value for + /// a given string regardless of how it's split into spans (e.g. { "AB" } and { "A", "B" } have the same hash code). + /// public override unsafe int GetHashCode() { - int hashCode = 5381; + uint hash = (5381 << 16) + 5381; + bool hashedOddNumberOfCharacters = false; + fixed (char* charPtr = _inlineSpan) { - for (int i = 0; i < _inlineSpan.Length; i++) - { - hashCode = unchecked(hashCode * 33 ^ charPtr[i]); - } + hash = GetHashCodeHelper(charPtr, _inlineSpan.Length, hash, ref hashedOddNumberOfCharacters); } if (_spans != null) { @@ -318,14 +332,68 @@ public override unsafe int GetHashCode() { fixed (char* charPtr = span.Span) { - for (int i = 0; i < span.Length; i++) - { - hashCode = unchecked(hashCode * 33 ^ charPtr[i]); - } + hash = GetHashCodeHelper(charPtr, span.Length, hash, ref hashedOddNumberOfCharacters); } } } - return hashCode; + return (int)(hash); + } + + /// + /// Hashes a memory block specified by a pointer and length. + /// + /// Pointer to the first character. + /// Number of characters at . + /// The running hash code. + /// True if the incoming was calculated from an odd number of characters. + /// The updated running hash code (not passed as a ref parameter to play nicely with JIT optimizations). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint GetHashCodeHelper(char* charPtr, int length, uint hash, ref bool hashedOddNumberOfCharacters) + { + if (hashedOddNumberOfCharacters && length > 0) + { + // If the number of characters hashed so far is odd, the first character of the current block completes + // the calculation done with the last character of the previous block. + hash ^= BitConverter.IsLittleEndian ? ((uint)*charPtr << 16) : *charPtr; + length--; + charPtr++; + hashedOddNumberOfCharacters = false; + } + + // The loop hashes two characters at a time. + uint* ptr = (uint*)charPtr; + while (length >= 2) + { + length -= 2; + hash = (RotateLeft(hash, 5) + hash) ^ *ptr; + ptr += 1; + } + + if (length > 0) + { + hash = (RotateLeft(hash, 5) + hash) ^ (BitConverter.IsLittleEndian ? *((char*)ptr) : ((uint)*((char*)ptr) << 16)); + hashedOddNumberOfCharacters = true; + } + + return hash; + } + + /// + /// Rotates an integer by the specified number of bits. + /// + /// The value to rotate. + /// The number of bits. + /// The rotated value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint RotateLeft(uint value, int offset) + { +#if NETCOREAPP + return System.Numerics.BitOperations.RotateLeft(value, offset); +#else + // Copied from System\Numerics\BitOperations.cs in dotnet/runtime as the routine is not available on .NET Framework. + // The JIT recognized the pattern and generates efficient code, e.g. the rol instruction on x86/x64. + return (value << offset) | (value >> (32 - offset)); +#endif } } } diff --git a/src/StringTools/StringTools.csproj b/src/StringTools/StringTools.csproj index e7713cb359a..fef9909ce6f 100644 --- a/src/StringTools/StringTools.csproj +++ b/src/StringTools/StringTools.csproj @@ -15,6 +15,7 @@ true Microsoft.NET.StringTools + This package contains the $(AssemblyName) assembly which implements common string-related functionality such as weak interning. diff --git a/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceCacheSerialization.cs b/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceCacheSerialization.cs index 77a9bf0452a..7f55c5bcbd1 100644 --- a/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceCacheSerialization.cs +++ b/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceCacheSerialization.cs @@ -1,6 +1,9 @@ +// Copyright (c) Microsoft. 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.IO; -using System.Reflection; using System.Runtime.Versioning; using Microsoft.Build.Shared; using Microsoft.Build.Tasks; @@ -12,10 +15,6 @@ namespace Microsoft.Build.UnitTests.ResolveAssemblyReference_Tests { public class ResolveAssemblyReferenceCacheSerialization : IDisposable { - // Maintain this two in sync with the constant in SystemState - private static readonly byte[] TranslateContractSignature = { (byte)'M', (byte)'B', (byte)'R', (byte)'S', (byte)'C' }; // Microsoft Build RAR State Cache - private static readonly byte TranslateContractVersion = 0x01; - private readonly string _rarCacheFile; private readonly TaskLoggingHelper _taskLoggingHelper; @@ -42,178 +41,81 @@ public void RoundTripEmptyState() { SystemState systemState = new(); - systemState.SerializeCacheByTranslator(_rarCacheFile, _taskLoggingHelper); + systemState.SerializeCache(_rarCacheFile, _taskLoggingHelper); - var deserialized = SystemState.DeserializeCacheByTranslator(_rarCacheFile, _taskLoggingHelper); + var deserialized = SystemState.DeserializeCache(_rarCacheFile, _taskLoggingHelper, typeof(SystemState)); deserialized.ShouldNotBeNull(); } [Fact] - public void WrongFileSignature() - { - SystemState systemState = new(); - - for (int i = 0; i < TranslateContractSignature.Length; i++) - { - systemState.SerializeCacheByTranslator(_rarCacheFile, _taskLoggingHelper); - using (var cacheStream = new FileStream(_rarCacheFile, FileMode.Open, FileAccess.ReadWrite)) - { - cacheStream.Seek(i, SeekOrigin.Begin); - cacheStream.WriteByte(0); - cacheStream.Close(); - } - - var deserialized = SystemState.DeserializeCacheByTranslator(_rarCacheFile, _taskLoggingHelper); - - deserialized.ShouldBeNull(); - } - } - - [Fact] - public void WrongFileVersion() + public void CorrectFileVersion() { SystemState systemState = new(); - systemState.SerializeCacheByTranslator(_rarCacheFile, _taskLoggingHelper); + systemState.SerializeCache(_rarCacheFile, _taskLoggingHelper); using (var cacheStream = new FileStream(_rarCacheFile, FileMode.Open, FileAccess.ReadWrite)) { - cacheStream.Seek(TranslateContractSignature.Length, SeekOrigin.Begin); - cacheStream.WriteByte((byte) (TranslateContractVersion + 1)); + cacheStream.Seek(0, SeekOrigin.Begin); + cacheStream.WriteByte(StateFileBase.CurrentSerializationVersion); cacheStream.Close(); } - var deserialized = SystemState.DeserializeCacheByTranslator(_rarCacheFile, _taskLoggingHelper); - - deserialized.ShouldBeNull(); - } - - [Fact] - public void CorrectFileSignature() - { - SystemState systemState = new(); - - for (int i = 0; i < TranslateContractSignature.Length; i++) - { - systemState.SerializeCacheByTranslator(_rarCacheFile, _taskLoggingHelper); - using (var cacheStream = new FileStream(_rarCacheFile, FileMode.Open, FileAccess.ReadWrite)) - { - cacheStream.Seek(i, SeekOrigin.Begin); - cacheStream.WriteByte(TranslateContractSignature[i]); - cacheStream.Close(); - } - - var deserialized = SystemState.DeserializeCacheByTranslator(_rarCacheFile, _taskLoggingHelper); + var deserialized = SystemState.DeserializeCache(_rarCacheFile, _taskLoggingHelper, typeof(SystemState)); - deserialized.ShouldNotBeNull(); - } + deserialized.ShouldNotBeNull(); } [Fact] - public void CorrectFileVersion() + public void WrongFileVersion() { SystemState systemState = new(); - systemState.SerializeCacheByTranslator(_rarCacheFile, _taskLoggingHelper); + systemState.SerializeCache(_rarCacheFile, _taskLoggingHelper); using (var cacheStream = new FileStream(_rarCacheFile, FileMode.Open, FileAccess.ReadWrite)) { - cacheStream.Seek(TranslateContractSignature.Length, SeekOrigin.Begin); - cacheStream.WriteByte(TranslateContractVersion); + cacheStream.Seek(0, SeekOrigin.Begin); + cacheStream.WriteByte(StateFileBase.CurrentSerializationVersion - 1); cacheStream.Close(); } - var deserialized = SystemState.DeserializeCacheByTranslator(_rarCacheFile, _taskLoggingHelper); + var deserialized = SystemState.DeserializeCache(_rarCacheFile, _taskLoggingHelper, typeof(SystemState)); - deserialized.ShouldNotBeNull(); + deserialized.ShouldBeNull(); } [Fact] - public void VerifySampleStateDeserialization() + public void ValidateSerializationAndDeserialization() { - // This test might also fail when binary format is modified. - // Any change in SystemState and child class ITranslatable implementation will most probably make this fail. - // To fix it, file referred by 'sampleName' needs to be recaptured and constant bellow modified to reflect - // the content of that cache. - // This sample was captured by compiling https://github.com/dotnet/roslyn/commit/f8107de2a94a01e96ac3d7c1f225acbb61e18830 - const string sampleName = "Microsoft.VisualStudio.LanguageServices.Implementation.csprojAssemblyReference.cache"; - const string expectedAssemblyPath = @"C:\Users\rokon\.nuget\packages\microsoft.visualstudio.codeanalysis.sdk.ui\15.8.27812-alpha\lib\net46\Microsoft.VisualStudio.CodeAnalysis.Sdk.UI.dll"; - const long expectedAssemblyLastWriteTimeTicks = 636644382480000000; - const string expectedAssemblyName = "Microsoft.VisualStudio.CodeAnalysis.Sdk.UI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; - const string expectedFrameworkName = ".NETFramework,Version=v4.5"; - var expectedDependencies = new[] + Dictionary cache = new() { + { "path1", new SystemState.FileState(DateTime.Now) }, + { "path2", new SystemState.FileState(DateTime.Now) { Assembly = new AssemblyNameExtension("hi") } }, + { "dllName", new SystemState.FileState(DateTime.Now.AddSeconds(-10)) { + Assembly = null, + RuntimeVersion = "v4.0.30319", + FrameworkNameAttribute = new FrameworkName(".NETFramework", Version.Parse("4.7.2"), "Profile"), + scatterFiles = new string[] { "first", "second" } } } }; + SystemState sysState = new(); + sysState.instanceLocalFileStateCache = cache; + SystemState sysState2 = null; + using (TestEnvironment env = TestEnvironment.Create()) { - "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", - "System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", - "System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.CodeAnalysis, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.DeveloperTools, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", - "Microsoft.VisualStudio.Shell.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "EnvDTE, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.CodeAnalysis.Sdk, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.Build.Framework, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.Text.Logic, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.Text.UI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.Text.Data, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.Text.UI.Wpf, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.ComponentModelHost, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.VSHelp, Version=7.0.3300.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.Shell.Interop.11.0, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.VCProjectEngine, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.Shell.15.0, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.OLE.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "System.Xml, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", - "Microsoft.VisualStudio.TextManager.Interop, Version=7.1.40304.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "EnvDTE80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", - "Microsoft.VisualStudio.VirtualTreeGrid, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.Shell.Interop.8.0, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "Microsoft.VisualStudio.Editor, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - }; - - - CopyResourceSampleFileIntoRarCacheFile($@"AssemblyDependency\CacheFileSamples\{sampleName}"); - - var deserializedByTranslator = SystemState.DeserializeCacheByTranslator(_rarCacheFile, _taskLoggingHelper); - deserializedByTranslator.ShouldNotBeNull(); - - deserializedByTranslator.SetGetLastWriteTime(path => - { - if (path != expectedAssemblyPath) - throw new InvalidOperationException("Unexpected file name for this test case"); - - return new DateTime(expectedAssemblyLastWriteTimeTicks, DateTimeKind.Utc); - }); - - GetAssemblyName getAssemblyName = deserializedByTranslator.CacheDelegate((GetAssemblyName)null); - GetAssemblyMetadata getAssemblyMetadata = deserializedByTranslator.CacheDelegate((GetAssemblyMetadata)null); - - var assemblyName = getAssemblyName(expectedAssemblyPath); - getAssemblyMetadata(expectedAssemblyPath, null, - out AssemblyNameExtension[] dependencies, - out string[] scatterFiles, - out FrameworkName frameworkNameAttribute); - - - assemblyName.ShouldNotBeNull(); - assemblyName.ShouldBe(new AssemblyNameExtension(expectedAssemblyName, false)); - scatterFiles.ShouldBeEmpty(); - frameworkNameAttribute.ShouldBe(new FrameworkName(expectedFrameworkName)); - dependencies.ShouldNotBeNull(); - expectedDependencies.ShouldBe(expectedDependencies, ignoreOrder: true); - } - - private void CopyResourceSampleFileIntoRarCacheFile(string name) - { - Assembly asm = this.GetType().Assembly; - var resource = string.Format($"{asm.GetName().Name}.{name.Replace("\\", ".")}"); - using Stream resourceStream = asm.GetManifestResourceStream(resource); - if (resourceStream == null) - throw new InvalidOperationException($"Resource '{resource}' has not been found."); - - using FileStream rarCacheFile = new FileStream(_rarCacheFile, FileMode.CreateNew); + TransientTestFile file = env.CreateFile(); + sysState.SerializeCache(file.Path, null); + sysState2 = SystemState.DeserializeCache(file.Path, null, typeof(SystemState)) as SystemState; + } - resourceStream.CopyTo(rarCacheFile); + Dictionary cache2 = sysState2.instanceLocalFileStateCache; + cache2.Count.ShouldBe(cache.Count); + cache2["path2"].Assembly.Name.ShouldBe(cache["path2"].Assembly.Name); + SystemState.FileState dll = cache["dllName"]; + SystemState.FileState dll2 = cache2["dllName"]; + dll2.Assembly.ShouldBe(dll.Assembly); + dll2.FrameworkNameAttribute.FullName.ShouldBe(dll.FrameworkNameAttribute.FullName); + dll2.LastModified.ShouldBe(dll.LastModified); + dll2.RuntimeVersion.ShouldBe(dll.RuntimeVersion); + dll2.scatterFiles.Length.ShouldBe(dll.scatterFiles.Length); + dll2.scatterFiles[1].ShouldBe(dll.scatterFiles[1]); } } } diff --git a/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs b/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs index 373805aa30f..e932aba587b 100644 --- a/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs +++ b/src/Tasks.UnitTests/AssemblyDependency/ResolveAssemblyReferenceTestFixture.cs @@ -2493,6 +2493,7 @@ internal static AssemblyNameExtension[] GetDependencies(string path) }; } +#pragma warning disable CA1416 /// /// Registry access delegate. Given a hive and a view, return the registry base key. /// @@ -2901,6 +2902,7 @@ private static string GetRegistrySubKeyDefaultValue(RegistryKey baseKey, string Assert.True(false, $"New GetRegistrySubKeyDefaultValue parameters encountered, need to add unittesting support for subKey={subKey}"); return null; } +#pragma warning restore CA1416 /// /// Delegate for System.IO.File.GetLastWriteTime diff --git a/src/Tasks.UnitTests/AssemblyRegistrationCache_Tests.cs b/src/Tasks.UnitTests/AssemblyRegistrationCache_Tests.cs index d496e33ecf1..fc94fbb510c 100644 --- a/src/Tasks.UnitTests/AssemblyRegistrationCache_Tests.cs +++ b/src/Tasks.UnitTests/AssemblyRegistrationCache_Tests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Tasks; +using Shouldly; using Xunit; namespace Microsoft.Build.UnitTests @@ -26,5 +27,24 @@ public void ExerciseCache() Assert.Equal("foo", assembly); Assert.Equal("bar", tlb); } + + [Fact] + public void ExerciseCacheSerialization() + { + AssemblyRegistrationCache arc = new(); + arc.AddEntry("foo", "bar"); + AssemblyRegistrationCache arc2 = null; + using (TestEnvironment env = TestEnvironment.Create()) + { + TransientTestFile file = env.CreateFile(); + arc.SerializeCache(file.Path, null); + arc2 = StateFileBase.DeserializeCache(file.Path, null, typeof(AssemblyRegistrationCache)) as AssemblyRegistrationCache; + } + + arc2._assemblies.Count.ShouldBe(arc._assemblies.Count); + arc2._assemblies[0].ShouldBe(arc._assemblies[0]); + arc2._typeLibraries.Count.ShouldBe(arc._typeLibraries.Count); + arc2._typeLibraries[0].ShouldBe(arc._typeLibraries[0]); + } } } diff --git a/src/Tasks.UnitTests/CSharpParserUtilitites_Tests.cs b/src/Tasks.UnitTests/CSharpParserUtilitites_Tests.cs index 8d438b418fc..7dbd018acc2 100644 --- a/src/Tasks.UnitTests/CSharpParserUtilitites_Tests.cs +++ b/src/Tasks.UnitTests/CSharpParserUtilitites_Tests.cs @@ -17,22 +17,28 @@ public void EmptyFile() // Simplest case of getting a fully-qualified class name from // a c# file. - [Fact] - public void Simple() + [Theory] + [InlineData("namespace MyNamespace { class MyClass {} }")] + [InlineData("namespace MyNamespace ; class MyClass {} ")] // file-scoped namespaces + public void Simple(string fileContents) { - AssertParse("namespace MyNamespace { class MyClass {} }", "MyNamespace.MyClass"); + AssertParse(fileContents, "MyNamespace.MyClass"); } - [Fact] - public void EmbeddedComment() + [Theory] + [InlineData("namespace /**/ MyNamespace /**/ { /**/ class /**/ MyClass/**/{}} //")] + [InlineData("namespace /**/ MyNamespace /**/ ; /**/ class /**/ MyClass/**/{} //")] // file-scoped namespaces + public void EmbeddedComment(string fileContents) { - AssertParse("namespace /**/ MyNamespace /**/ { /**/ class /**/ MyClass/**/{}} //", "MyNamespace.MyClass"); + AssertParse(fileContents, "MyNamespace.MyClass"); } - [Fact] - public void MinSpace() + [Theory] + [InlineData("namespace MyNamespace{class MyClass{}}")] + [InlineData("namespace MyNamespace;class MyClass{}")] // file-scoped namespaces + public void MinSpace(string fileContents) { - AssertParse("namespace MyNamespace{class MyClass{}}", "MyNamespace.MyClass"); + AssertParse(fileContents, "MyNamespace.MyClass"); } [Fact] @@ -41,16 +47,20 @@ public void NoNamespace() AssertParse("class MyClass{}", "MyClass"); } - [Fact] - public void SneakyComment() + [Theory] + [InlineData("/*namespace MyNamespace { */ class MyClass {} /* } */")] + [InlineData("/*namespace MyNamespace ; */ class MyClass {}")] // file-scoped namespaces + public void SneakyComment(string fileContents) { - AssertParse("/*namespace MyNamespace { */ class MyClass {} /* } */", "MyClass"); + AssertParse(fileContents, "MyClass"); } - [Fact] - public void CompoundNamespace() + [Theory] + [InlineData("namespace MyNamespace.Feline { class MyClass {} }")] + [InlineData("namespace MyNamespace.Feline ; class MyClass {} ")] // file-scoped namespaces + public void CompoundNamespace(string fileContents) { - AssertParse("namespace MyNamespace.Feline { class MyClass {} }", "MyNamespace.Feline.MyClass"); + AssertParse(fileContents, "MyNamespace.Feline.MyClass"); } [Fact] @@ -71,16 +81,20 @@ public void NestedCompoundNamespace() AssertParse("namespace MyNamespace/**/.A{ namespace Feline . B {namespace Bovine.C {sealed class MyClass {} }} }", "MyNamespace.A.Feline.B.Bovine.C.MyClass"); } - [Fact] - public void DoubleClass() + [Theory] + [InlineData("namespace MyNamespace{class Feline{}class Bovine}")] + [InlineData("namespace MyNamespace;class Feline{}class Bovine")] // file-scoped namespaces + public void DoubleClass(string fileContents) { - AssertParse("namespace MyNamespace{class Feline{}class Bovine}", "MyNamespace.Feline"); + AssertParse(fileContents, "MyNamespace.Feline"); } - [Fact] - public void EscapedKeywordClass() + [Theory] + [InlineData("namespace MyNamespace{class @class{}}")] + [InlineData("namespace MyNamespace;class @class{}")] // file-scoped namespaces + public void EscapedKeywordClass(string fileContents) { - AssertParse("namespace MyNamespace{class @class{}}", "MyNamespace.class"); + AssertParse(fileContents, "MyNamespace.class"); } [Fact] @@ -90,7 +104,7 @@ public void LeadingUnderscore() } [Fact] - public void SkipInterveningNamespaces() + public void InterveningNamespaces() { AssertParse("namespace MyNamespace { namespace XXX {} class MyClass {} }", "MyNamespace.MyClass"); } @@ -174,10 +188,12 @@ public void AssemblyAttributeBool() AssertParse("[assembly :AssemblyDelaySign(false)] namespace i { class a { } }", "i.a"); } - [Fact] - public void AssemblyAttributeString() + [Theory] + [InlineData("[assembly :MyString(\"namespace\")] namespace i { class a { } }")] + [InlineData("[assembly :MyString(\"namespace\")] namespace i; class a { }")] + public void AssemblyAttributeString(string fileContents) { - AssertParse("[assembly :MyString(\"namespace\")] namespace i { class a { } }", "i.a"); + AssertParse(fileContents, "i.a"); } [Fact] @@ -253,19 +269,20 @@ public void ClassAttributeStringIsCloseScope() AssertParse("namespace i { [MyString(\"}\")] class a { } }", "i.a"); } - [Fact] - public void NameSpaceStructEnum() + [Theory] + [InlineData("namespace n { public struct s { enum e {} } class c {} }")] + [InlineData("namespace n; public struct s { enum e {} } class c {}")] // file-scoped namespace + public void NameSpaceStructEnum(string fileContents) { - AssertParse("namespace n { public struct s { enum e {} } class c {} }", "n.c"); + AssertParse(fileContents, "n.c"); } [Fact] public void PreprocessorControllingTwoNamespaces() { // This works by coincidence since preprocessor directives are currently ignored. - AssertParse - ( - @" + // Note: If the condition were #if (true), the result would still be n1.c + AssertParse(@" #if (false) namespace n1 #else @@ -275,29 +292,57 @@ namespace n2 ", "n2.c"); } - [Fact] - public void PreprocessorControllingTwoNamespacesWithInterveningKeyword() + /// + /// The test "PreprocessorControllingTwoNamespaces" reveals that preprocessor directives are ignored. + /// This means that in the case of many namespaces before curly braces (despite that being invalid C#) + /// the last namespace would win. This test explicitly tests that. + /// + [Theory] + [InlineData(@" +namespace n1 + namespace n2 + namespace n3 + namespace n4 + { class c { } }", "n4.c")] + [InlineData(@" +namespace n1; +namespace n2; +namespace n3; +namespace n4; +class c {} ", "n1.n2.n3.n4.c")] + public void MultipleNamespaces_InvalidCSharp(string fileContents, string expected) { // This works by coincidence since preprocessor directives are currently ignored. - AssertParse - ( - @" + AssertParse(fileContents, expected); + } + + /// + /// Note: Preprocessor conditions are not implemented + /// + [Theory] + [InlineData(@" #if (false) namespace n1 #else using a=b; namespace n2 #endif -{ class c {} } - ", "n2.c"); +{ class c {} }", "n2.c")] + [InlineData(@" +#if (false) +namespace n1; +#else +using a=b; +namespace n2; +#endif +{ class c {} }", "n1.n2.c")] + public void PreprocessorControllingTwoNamespacesWithInterveningKeyword(string fileContents, string expected) + { + AssertParse(fileContents, expected); } - [Fact] - public void Preprocessor() - { - AssertParse - ( - @" + [Theory] + [InlineData(@" #if MY_CONSTANT namespace i { @@ -307,8 +352,19 @@ class a } #endregion } -#endif // MY_CONSTANT - ", "i.a"); +#endif // MY_CONSTANT ")] + [InlineData(@" +#if MY_CONSTANT +namespace i; + #region Put the class in a region + class a + { + } + #endregion +#endif // MY_CONSTANT")] + public void Preprocessor(string fileContents) + { + AssertParse(fileContents, "i.a"); } [Fact(Skip = "Preprocessor is not yet implemented.")] @@ -333,16 +389,18 @@ namespace i - [Fact] - public void Regress_Mutation_SingleLineCommentsShouldBeIgnored() - { - AssertParse - ( - @" + [Theory] + [InlineData(@" namespace n2 // namespace n1 -{ class c {} } - ", "n2.c"); +{ class c {} }")] + [InlineData(@" +namespace n2; +// namespace n1 +class c {}")] + public void Regress_Mutation_SingleLineCommentsShouldBeIgnored(string fileContents) + { + AssertParse(fileContents, "n2.c"); } /* diff --git a/src/Tasks.UnitTests/GetCompatiblePlatform_Tests.cs b/src/Tasks.UnitTests/GetCompatiblePlatform_Tests.cs new file mode 100644 index 00000000000..91bbc2269b3 --- /dev/null +++ b/src/Tasks.UnitTests/GetCompatiblePlatform_Tests.cs @@ -0,0 +1,203 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.Build.UnitTests; +using Microsoft.Build.Utilities; +using Shouldly; +using Xunit; +using Xunit.Abstractions; + +namespace Microsoft.Build.Tasks.UnitTests +{ + sealed public class GetCompatiblePlatform_Tests + { + private readonly ITestOutputHelper _output; + + public GetCompatiblePlatform_Tests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void ResolvesViaPlatformLookupTable() + { + // PlatformLookupTable always takes priority. It is typically user-defined. + TaskItem projectReference = new TaskItem("foo.bar"); + projectReference.SetMetadata("Platforms", "x64;x86;AnyCPU"); + + GetCompatiblePlatform task = new GetCompatiblePlatform() + { + BuildEngine = new MockEngine(_output), + CurrentProjectPlatform = "win32", + PlatformLookupTable = "win32=x64", + AnnotatedProjects = new TaskItem[] { projectReference } + }; + + task.Execute().ShouldBeTrue(); + + task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe("x64"); + } + + [Fact] + public void ResolvesViaProjectReferencesPlatformLookupTable() + { + // A ProjectReference's PlatformLookupTable takes priority over the current project's table. + // This allows overrides on a per-ProjectItem basis. + TaskItem projectReference = new TaskItem("foo.bar"); + projectReference.SetMetadata("Platforms", "x64;x86;AnyCPU"); + + // ProjectReference will be assigned x86 because its table takes priority + projectReference.SetMetadata("PlatformLookupTable", "win32=x86"); + + GetCompatiblePlatform task = new GetCompatiblePlatform() + { + BuildEngine = new MockEngine(_output), + CurrentProjectPlatform = "win32", + PlatformLookupTable = "win32=x64", + AnnotatedProjects = new TaskItem[] { projectReference } + }; + + task.Execute().ShouldBeTrue(); + + task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe("x86"); + } + + [Fact] + public void ResolvesViaAnyCPUDefault() + { + // No valid mapping via the lookup table, should default to AnyCPU when the current project + // and ProjectReference platforms don't match. + TaskItem projectReference = new TaskItem("foo.bar"); + projectReference.SetMetadata("Platforms", "x64;AnyCPU"); + + GetCompatiblePlatform task = new GetCompatiblePlatform() + { + BuildEngine = new MockEngine(_output), + CurrentProjectPlatform = "x86", + PlatformLookupTable = "AnyCPU=x64", + AnnotatedProjects = new TaskItem[] { projectReference } + }; + + task.Execute().ShouldBeTrue(); + + task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe("AnyCPU"); + } + + [Fact] + public void ResolvesViaSamePlatform() + { + // No valid mapping via the lookup table. If the ProjectReference's platform + // matches the current project's platform, it takes priority over AnyCPU default. + TaskItem projectReference = new TaskItem("foo.bar"); + projectReference.SetMetadata("Platforms", "x86;x64;AnyCPU"); + projectReference.SetMetadata("PlatformLookupTable", "x86=AnyCPU"); // matching platform takes priority over lookup tables + + GetCompatiblePlatform task = new GetCompatiblePlatform() + { + BuildEngine = new MockEngine(_output), + CurrentProjectPlatform = "x86", + PlatformLookupTable = "x86=AnyCPU", + AnnotatedProjects = new TaskItem[] { projectReference } + }; + + task.Execute().ShouldBeTrue(); + + task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe("x86"); + } + + [Fact] + public void FailsToResolve() + { + // No valid mapping via the lookup table, ProjectReference can't default to AnyCPU, + // it also can't match with current project, log a warning. + TaskItem projectReference = new TaskItem("foo.bar"); + projectReference.SetMetadata("Platforms", "x64"); + + GetCompatiblePlatform task = new GetCompatiblePlatform() + { + BuildEngine = new MockEngine(_output), + CurrentProjectPlatform = "x86", + PlatformLookupTable = "AnyCPU=x64", + AnnotatedProjects = new TaskItem[] { projectReference }, + }; + + task.Execute().ShouldBeTrue(); + // When the task logs a warning, it does not set NearestPlatform + task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe(string.Empty); + ((MockEngine)task.BuildEngine).AssertLogContains("MSB3981"); + } + + [Fact] + public void WarnsWhenProjectReferenceHasNoPlatformOptions() + { + // Task should log a warning when a ProjectReference has no options to build as. + // It will continue and have no NearestPlatform metadata. + TaskItem projectReference = new TaskItem("foo.bar"); + projectReference.SetMetadata("Platforms", string.Empty); + + GetCompatiblePlatform task = new GetCompatiblePlatform() + { + BuildEngine = new MockEngine(_output), + CurrentProjectPlatform = "x86", + PlatformLookupTable = "AnyCPU=x64", + AnnotatedProjects = new TaskItem[] { projectReference }, + }; + + task.Execute().ShouldBeTrue(); + // When the task logs a warning, it does not set NearestPlatform + task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe(string.Empty); + ((MockEngine)task.BuildEngine).AssertLogContains("MSB3982"); + } + + /// + /// Invalid format on PlatformLookupTable results in an exception being thrown. + /// + [Fact] + public void WarnsOnInvalidFormatLookupTable() + { + TaskItem projectReference = new TaskItem("foo.bar"); + projectReference.SetMetadata("Platforms", "x64"); + + GetCompatiblePlatform task = new GetCompatiblePlatform() + { + BuildEngine = new MockEngine(_output), + CurrentProjectPlatform = "AnyCPU", + PlatformLookupTable = "AnyCPU=;A=B", // invalid format + AnnotatedProjects = new TaskItem[] { projectReference }, + }; + + task.Execute().ShouldBeTrue(); + // When the platformlookuptable is in an invalid format, it is discarded. + // There shouldn't have been a translation found from AnyCPU to anything. + // Meaning the projectreference would not have NearestPlatform set. + task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe(string.Empty); + ((MockEngine)task.BuildEngine).AssertLogContains("MSB3983"); + } + + /// + /// Invalid format on PlatformLookupTable from the projectreference results in an exception being thrown. + /// + [Fact] + public void WarnsOnInvalidFormatProjectReferenceLookupTable() + { + TaskItem projectReference = new TaskItem("foo.bar"); + projectReference.SetMetadata("Platforms", "x64;x86"); + projectReference.SetMetadata("PlatformLookupTable", "x86=;b=d"); + + GetCompatiblePlatform task = new GetCompatiblePlatform() + { + BuildEngine = new MockEngine(_output), + CurrentProjectPlatform = "AnyCPU", + PlatformLookupTable = "AnyCPU=x86;A=B", // invalid format + AnnotatedProjects = new TaskItem[] { projectReference }, + }; + + task.Execute().ShouldBeTrue(); + + // A ProjectReference PlatformLookupTable should take priority, but is thrown away when + // it has an invalid format. The current project's PLT should be the next priority. + task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe("x86"); + ((MockEngine)task.BuildEngine).AssertLogContains("MSB3983"); + } + } +} diff --git a/src/Tasks.UnitTests/GetSDKReference_Tests.cs b/src/Tasks.UnitTests/GetSDKReference_Tests.cs index 7af8f468716..6437663437c 100644 --- a/src/Tasks.UnitTests/GetSDKReference_Tests.cs +++ b/src/Tasks.UnitTests/GetSDKReference_Tests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; @@ -259,6 +260,38 @@ public void GetSDKReferenceFolders() VerifySDKFolders(getReferenceFolders, getReferenceFolders2, "References", _sdkDirectory); } + [Fact] + public void VerifyGetSdkReferenceTranslator() + { + Dictionary pathToReferenceMetadata = new(); + pathToReferenceMetadata.Add("first", new("dat", "dat2", true, false)); + pathToReferenceMetadata.Add("second", new("inf", "inf2", false, false)); + Dictionary> directoryToFileList = new(); + directoryToFileList.Add("third", new List() { "a", "b", "c" }); + directoryToFileList.Add("fourth", new List() { "1", "2", "3" }); + GetSDKReferenceFiles.SDKInfo writeInfo = new(pathToReferenceMetadata, directoryToFileList, 47); + GetSDKReferenceFiles.SaveContext contextWriter = new("d", "n", writeInfo); + GetSDKReferenceFiles.SDKInfo readInfo = null; + using (TestEnvironment env = TestEnvironment.Create()) + { + TransientTestFolder folder = env.CreateFolder(); + GetSDKReferenceFiles.SDKFilesCache cache = new(null, folder.Path, null, null, null); + cache.SaveAssemblyListToCacheFile(contextWriter); + GetSDKReferenceFiles.SDKFilesCache cache2 = new(null, folder.Path, null, null, null); + readInfo = cache2.LoadAssemblyListFromCacheFile("d", "n"); + } + readInfo.DirectoryToFileList.Count.ShouldBe(2); + readInfo.DirectoryToFileList["fourth"].Count.ShouldBe(3); + readInfo.DirectoryToFileList["fourth"][1].ShouldBe("2"); + readInfo.DirectoryToFileList["third"][0].ShouldBe("a"); + readInfo.Hash.ShouldBe(47); + readInfo.PathToReferenceMetadata.Count.ShouldBe(2); + readInfo.PathToReferenceMetadata["first"].FusionName.ShouldBe("dat"); + readInfo.PathToReferenceMetadata["first"].IsManagedWinmd.ShouldBeFalse(); + readInfo.PathToReferenceMetadata["first"].IsWinMD.ShouldBeTrue(); + readInfo.PathToReferenceMetadata["second"].ImageRuntime.ShouldBe("inf2"); + } + private static void VerifySDKFolders(GetSDKFolders singleParamDelegate, GetSDKFolders2 multiParamDelegate, string folderName, string sdkDirectory) { IList sdkFolders = singleParamDelegate(sdkDirectory); diff --git a/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj b/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj index b782457b344..98ae00b9da8 100644 --- a/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj +++ b/src/Tasks.UnitTests/Microsoft.Build.Tasks.UnitTests.csproj @@ -18,6 +18,7 @@ + @@ -25,14 +26,13 @@ - + - diff --git a/src/Tasks.UnitTests/ResolveComReference_Tests.cs b/src/Tasks.UnitTests/ResolveComReference_Tests.cs index f8c1c4855f7..4e695b86a73 100644 --- a/src/Tasks.UnitTests/ResolveComReference_Tests.cs +++ b/src/Tasks.UnitTests/ResolveComReference_Tests.cs @@ -14,6 +14,9 @@ using Microsoft.Build.Tasks; using Xunit; using Microsoft.Build.Shared; +using System.IO; +using Microsoft.Build.BackEnd; +using Shouldly; namespace Microsoft.Build.UnitTests { @@ -57,6 +60,29 @@ public void GetResolvedASsemblyReferenceSpecNotNull() Assert.NotNull(task.GetResolvedAssemblyReferenceItemSpecs()); } + [Fact] + public void TestSerializationAndDeserialization() + { + ResolveComReferenceCache cache = new("path1", "path2"); + cache.componentTimestamps = new() + { + { "first", DateTime.Now }, + { "second", DateTime.FromBinary(10000) }, + }; + ResolveComReferenceCache cache2 = null; + using (TestEnvironment env = TestEnvironment.Create()) + { + TransientTestFile file = env.CreateFile(); + cache.SerializeCache(file.Path, null); + cache2 = StateFileBase.DeserializeCache(file.Path, null, typeof(ResolveComReferenceCache)) as ResolveComReferenceCache; + } + + cache2.tlbImpLocation.ShouldBe(cache.tlbImpLocation); + cache2.axImpLocation.ShouldBe(cache.axImpLocation); + cache2.componentTimestamps.Count.ShouldBe(cache.componentTimestamps.Count); + cache2.componentTimestamps["second"].ShouldBe(cache.componentTimestamps["second"]); + } + /* * Method: CheckComReferenceAttributeVerificationForNameItems * diff --git a/src/Tasks.UnitTests/ResourceHandling/GenerateResource_Tests.cs b/src/Tasks.UnitTests/ResourceHandling/GenerateResource_Tests.cs index 7a9c3a3fac7..52fcfff9d56 100644 --- a/src/Tasks.UnitTests/ResourceHandling/GenerateResource_Tests.cs +++ b/src/Tasks.UnitTests/ResourceHandling/GenerateResource_Tests.cs @@ -2718,7 +2718,7 @@ public References(ITestOutputHelper output) [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "Linked resources not supported on Core: https://github.com/microsoft/msbuild/issues/4094")] - [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "https://github.com/Microsoft/msbuild/issues/677")] + [SkipOnMono("https://github.com/Microsoft/msbuild/issues/677")] public void DontLockP2PReferenceWhenResolvingSystemTypes() { // This WriteLine is a hack. On a slow machine, the Tasks unittest fails because remoting @@ -2896,7 +2896,7 @@ public class Class1 /// [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "Linked resources not supported on Core: https://github.com/microsoft/msbuild/issues/4094")] - [SkipOnTargetFramework(TargetFrameworkMonikers.Mono, "https://github.com/Microsoft/msbuild/issues/677")] + [SkipOnMono("https://github.com/Microsoft/msbuild/issues/677")] public void ReferencedAssemblySpecifiedUsingRelativePath() { // This WriteLine is a hack. On a slow machine, the Tasks unittest fails because remoting diff --git a/src/Tasks.UnitTests/ResourceHandling/ResGenDependencies_Tests.cs b/src/Tasks.UnitTests/ResourceHandling/ResGenDependencies_Tests.cs index a48675078f4..fa4c5af5675 100644 --- a/src/Tasks.UnitTests/ResourceHandling/ResGenDependencies_Tests.cs +++ b/src/Tasks.UnitTests/ResourceHandling/ResGenDependencies_Tests.cs @@ -6,6 +6,8 @@ using Microsoft.Build.Tasks; using Microsoft.Build.Shared; using Xunit; +using Shouldly; +using System; namespace Microsoft.Build.UnitTests { @@ -16,35 +18,64 @@ sealed public class ResGenDependencies_Tests public void DirtyCleanScenario(bool useMSBuildResXReader) { - ResGenDependencies cache = new ResGenDependencies(); - + ResGenDependencies cache = new(); string resx = CreateSampleResx(); string stateFile = FileUtilities.GetTemporaryFile(); try { // A newly created cache is not dirty. - Assert.False(cache.IsDirty); + cache.IsDirty.ShouldBeFalse(); + + ResGenDependencies.PortableLibraryFile libFile = new("otherFileName"); + libFile.outputFiles = new string[] { "first", "second" }; + libFile.assemblySimpleName = "simpleName"; + libFile.lastModified = DateTime.Now.Subtract(TimeSpan.FromSeconds(10)); + cache.portableLibraries.Add("fileName", libFile); + + // Writing the file to disk should make the cache clean. + cache.SerializeCache(stateFile, /* Log */ null); + cache.IsDirty.ShouldBeFalse(); // Getting a file that wasn't in the cache is a write operation. cache.GetResXFileInfo(resx, useMSBuildResXReader); - Assert.True(cache.IsDirty); + cache.IsDirty.ShouldBeTrue(); - // Writing the file to disk should make the cache clean. + // Add linkedFiles to further test serialization and deserialization. + cache.resXFiles.TryGetValue(resx, out ResGenDependencies.ResXFile file).ShouldBeTrue(); + file.linkedFiles = new string[] { "third", "fourth" }; + + // Writing the file to disk should make the cache clean again. cache.SerializeCache(stateFile, /* Log */ null); - Assert.False(cache.IsDirty); + cache.IsDirty.ShouldBeFalse(); // Deserialize from disk. Result should not be dirty. - cache = ResGenDependencies.DeserializeCache(stateFile, true, /* Log */ null); - Assert.False(cache.IsDirty); + ResGenDependencies cache2 = ResGenDependencies.DeserializeCache(stateFile, true, /* Log */ null); + cache2.IsDirty.ShouldBeFalse(); + + // Validate that serialization worked + cache.portableLibraries.TryGetValue("fileName", out ResGenDependencies.PortableLibraryFile portableLibrary); + cache2.portableLibraries.TryGetValue("fileName", out ResGenDependencies.PortableLibraryFile portableLibrary2); + portableLibrary2.filename.ShouldBe(portableLibrary.filename); + portableLibrary2.exists.ShouldBe(portableLibrary.exists); + portableLibrary2.assemblySimpleName.ShouldBe(portableLibrary.assemblySimpleName); + portableLibrary2.lastModified.ShouldBe(portableLibrary.lastModified); + portableLibrary2.outputFiles.Length.ShouldBe(portableLibrary.outputFiles.Length); + portableLibrary2.outputFiles[1].ShouldBe(portableLibrary.outputFiles[1]); + cache.resXFiles.TryGetValue(resx, out ResGenDependencies.ResXFile resX); + cache2.resXFiles.TryGetValue(resx, out ResGenDependencies.ResXFile resX2); + resX2.filename.ShouldBe(resX.filename); + resX2.lastModified.ShouldBe(resX.lastModified); + resX2.linkedFiles.Length.ShouldBe(resX.linkedFiles.Length); + resX2.linkedFiles[1].ShouldBe(resX.linkedFiles[1]); // Asking for a file that's in the cache should not dirty the cache. - cache.GetResXFileInfo(resx, useMSBuildResXReader); - Assert.False(cache.IsDirty); + cache2.GetResXFileInfo(resx, useMSBuildResXReader); + cache2.IsDirty.ShouldBeFalse(); // Changing UseSourcePath to false should dirty the cache. - cache.UseSourcePath = false; - Assert.True(cache.IsDirty); + cache2.UseSourcePath = false; + cache2.IsDirty.ShouldBeTrue(); } finally { diff --git a/src/Tasks.UnitTests/XslTransformation_Tests.cs b/src/Tasks.UnitTests/XslTransformation_Tests.cs index 5fc54d6dfcf..785bab86be5 100644 --- a/src/Tasks.UnitTests/XslTransformation_Tests.cs +++ b/src/Tasks.UnitTests/XslTransformation_Tests.cs @@ -13,6 +13,7 @@ using System.Text.RegularExpressions; using System.Xml.Xsl; using System.Xml; +using Shouldly; using Xunit; namespace Microsoft.Build.UnitTests @@ -386,7 +387,7 @@ public void OutputTest() /// Setting correct "Parameter" parameters for Xsl. ///
[Fact] - public void XsltParamatersCorrect() + public void XsltParametersCorrect() { string dir; TaskItem[] xmlPaths; @@ -780,6 +781,39 @@ public void OutputFileCannotBeWritten() CleanUp(dir); } + /// + /// The files are not kept locked by the task + /// + [Fact] + public void InputFilesDontLock() + { + string dir; + TaskItem[] xmlPaths; + TaskItem xslPath; + TaskItem[] outputPaths; + MockEngine engine; + Prepare(out dir, out xmlPaths, out xslPath, out _, out outputPaths, out _, out _, out engine); + + // Test with files + { + XslTransformation t = new XslTransformation(); + t.BuildEngine = engine; + t.XmlInputPaths = xmlPaths; + t.XslInputPath = xslPath; + t.OutputPaths = outputPaths; + + t.Execute().ShouldBeTrue(); + string xmlInputPath = xmlPaths[0].ItemSpec; + File.Delete(xmlInputPath); // this should succeed (file not locked by task) + File.Exists(xmlInputPath).ShouldBeFalse(); + string xslInputPath = xslPath.ItemSpec; + File.Delete(xslInputPath); // this should succeed (file not locked by task) + File.Exists(xslInputPath).ShouldBeFalse(); + } + + CleanUp(dir); + } + /// /// XslDocument that throws runtime exception. /// diff --git a/src/Tasks/AssemblyDependency/AssemblyInformation.cs b/src/Tasks/AssemblyDependency/AssemblyInformation.cs index 4af1396f59e..9a6e36c008e 100644 --- a/src/Tasks/AssemblyDependency/AssemblyInformation.cs +++ b/src/Tasks/AssemblyDependency/AssemblyInformation.cs @@ -405,6 +405,11 @@ private void CorePopulateMetadata() } var container = metadataReader.GetMemberReference((MemberReferenceHandle) ctorHandle).Parent; + if (container.Kind != HandleKind.TypeReference) + { + continue; + } + var name = metadataReader.GetTypeReference((TypeReferenceHandle) container).Name; if (!string.Equals(metadataReader.GetString(name), "TargetFrameworkAttribute")) { diff --git a/src/Tasks/AssemblyDependency/Reference.cs b/src/Tasks/AssemblyDependency/Reference.cs index e5b3b361455..c6b179e69ad 100644 --- a/src/Tasks/AssemblyDependency/Reference.cs +++ b/src/Tasks/AssemblyDependency/Reference.cs @@ -513,6 +513,13 @@ internal string FullPath } } + internal void NormalizeFullPath() + { + _fullPath = FileUtilities.NormalizePath(_fullPath); + _fullPathWithoutExtension = null; + _directoryName = null; + } + /// /// The directory that this assembly lives in. /// diff --git a/src/Tasks/AssemblyDependency/ReferenceTable.cs b/src/Tasks/AssemblyDependency/ReferenceTable.cs index 5967f4c1cdf..196a70b8747 100644 --- a/src/Tasks/AssemblyDependency/ReferenceTable.cs +++ b/src/Tasks/AssemblyDependency/ReferenceTable.cs @@ -407,6 +407,12 @@ internal void AddReference(AssemblyNameExtension assemblyName, Reference referen } } + if (reference.FullPath.Length > 0 && ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0)) + { + // Saves effort and makes deduplication possible downstream + reference.NormalizeFullPath(); + } + References[assemblyName] = reference; } @@ -1337,7 +1343,11 @@ out userRequestedSpecificFile // If the path was resolved, then specify the full path on the reference. if (resolvedPath != null) { - if (!Path.IsPathRooted(resolvedPath)) + if (ChangeWaves.AreFeaturesEnabled(ChangeWaves.Wave17_0)) + { + resolvedPath = FileUtilities.NormalizePath(resolvedPath); + } + else if (!Path.IsPathRooted(resolvedPath)) { resolvedPath = Path.GetFullPath(resolvedPath); } diff --git a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs index 169eebec17a..2cea34c71bb 100644 --- a/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs +++ b/src/Tasks/AssemblyDependency/ResolveAssemblyReference.cs @@ -1247,7 +1247,8 @@ quiet at the engine level. } #if FEATURE_WIN32_REGISTRY - if (dependencyTable.Resolvers != null) + MessageImportance messageImportance = MessageImportance.Low; + if (dependencyTable.Resolvers != null && Log.LogsMessagesOfImportance(messageImportance)) { foreach (Resolver r in dependencyTable.Resolvers) { @@ -1255,7 +1256,6 @@ quiet at the engine level. { AssemblyFoldersEx assemblyFoldersEx = ((AssemblyFoldersExResolver)r).AssemblyFoldersExLocations; - MessageImportance messageImportance = MessageImportance.Low; if (assemblyFoldersEx != null && _showAssemblyFoldersExLocations.TryGetValue(r.SearchPath, out messageImportance)) { Log.LogMessageFromResources(messageImportance, "ResolveAssemblyReference.AssemblyFoldersExSearchLocations", r.SearchPath); @@ -1347,6 +1347,10 @@ private void LogReference(Reference reference, string fusionName) { // Set an importance level to be used for secondary messages. MessageImportance importance = ChooseReferenceLoggingImportance(reference); + if (!Log.LogsMessagesOfImportance(importance)) + { + return; + } // Log the fusion name and whether this is a primary or a dependency. LogPrimaryOrDependency(reference, fusionName, importance); @@ -1413,7 +1417,8 @@ private MessageImportance ChooseReferenceLoggingImportance(Reference reference) ///
private void LogInputs() { - if (Traits.Instance.EscapeHatches.LogTaskInputs || Silent) + MessageImportance importance = MessageImportance.Low; + if (Silent || Log.IsTaskInputLoggingEnabled || !Log.LogsMessagesOfImportance(importance)) { // the inputs will be logged automatically anyway, avoid duplication in the logs return; @@ -1421,7 +1426,6 @@ private void LogInputs() string indent = Strings.FourSpaces; string property = Strings.LogTaskPropertyFormat; - MessageImportance importance = MessageImportance.Low; Log.LogMessage(importance, property, "TargetFrameworkMoniker"); Log.LogMessage(importance, indent + _targetedFrameworkMoniker); @@ -2003,12 +2007,12 @@ private void LogConflict(Reference reference, string fusionName, StringBuilder l ///
internal void ReadStateFile(FileExists fileExists) { - _cache = SystemState.DeserializeCacheByTranslator(_stateFile, Log); + _cache = SystemState.DeserializeCache(_stateFile, Log, typeof(SystemState)) as SystemState; // Construct the cache only if we can't find any caches. if (_cache == null && AssemblyInformationCachePaths != null && AssemblyInformationCachePaths.Length > 0) { - _cache = SystemState.DeserializePrecomputedCachesByTranslator(AssemblyInformationCachePaths, Log, fileExists); + _cache = SystemState.DeserializePrecomputedCaches(AssemblyInformationCachePaths, Log, fileExists); } if (_cache == null) @@ -2024,11 +2028,11 @@ internal void WriteStateFile() { if (!String.IsNullOrEmpty(AssemblyInformationCacheOutputPath)) { - _cache.SerializePrecomputedCacheByTranslator(AssemblyInformationCacheOutputPath, Log); + _cache.SerializePrecomputedCache(AssemblyInformationCacheOutputPath, Log); } else if (!String.IsNullOrEmpty(_stateFile) && _cache.IsDirty) { - _cache.SerializeCacheByTranslator(_stateFile, Log); + _cache.SerializeCache(_stateFile, Log); } } #endregion @@ -2265,7 +2269,7 @@ ReadMachineTypeFromPEHeader readMachineTypeFromPEHeader // Cache delegates. getAssemblyName = _cache.CacheDelegate(getAssemblyName); getAssemblyMetadata = _cache.CacheDelegate(getAssemblyMetadata); - fileExists = _cache.CacheDelegate(fileExists); + fileExists = _cache.CacheDelegate(); directoryExists = _cache.CacheDelegate(directoryExists); getDirectories = _cache.CacheDelegate(getDirectories); getRuntimeVersion = _cache.CacheDelegate(getRuntimeVersion); @@ -2579,7 +2583,7 @@ out _copyLocalFiles } } } - MSBuildEventSource.Log.RarOverallStop(); + MSBuildEventSource.Log.RarOverallStop(_assemblyNames?.Length ?? -1, _assemblyFiles?.Length ?? -1, _resolvedFiles?.Length ?? -1, _resolvedDependencyFiles?.Length ?? -1, _copyLocalFiles?.Length ?? -1, _findDependencies); return success && !Log.HasLoggedErrors; } catch (ArgumentException e) @@ -2596,7 +2600,7 @@ out _copyLocalFiles } } - MSBuildEventSource.Log.RarOverallStop(); + MSBuildEventSource.Log.RarOverallStop(_assemblyNames?.Length ?? -1, _assemblyFiles?.Length ?? -1, _resolvedFiles?.Length ?? -1, _resolvedDependencyFiles?.Length ?? -1, _copyLocalFiles?.Length ?? -1, _findDependencies); return success && !Log.HasLoggedErrors; } diff --git a/src/Tasks/AssemblyInfo.cs b/src/Tasks/AssemblyInfo.cs index 6a11208e44d..df3964f3cd5 100644 --- a/src/Tasks/AssemblyInfo.cs +++ b/src/Tasks/AssemblyInfo.cs @@ -17,14 +17,7 @@ // so that we don't run into known security issues with loading libraries from unsafe locations [assembly: DefaultDllImportSearchPaths(DllImportSearchPath.SafeDirectories)] -// Needed for the "hub-and-spoke model to locate and retrieve localized resources": https://msdn.microsoft.com/en-us/library/21a15yht(v=vs.110).aspx -// We want "en" to require a satellite assembly for debug builds in order to flush out localization -// issues, but we want release builds to work without it. Also, .net core does not have resource fallbacks -#if (DEBUG && !RUNTIME_TYPE_NETCORE) -[assembly: NeutralResourcesLanguage("en", UltimateResourceFallbackLocation.Satellite)] -#else [assembly: NeutralResourcesLanguage("en")] -#endif [assembly: ComVisible(false)] diff --git a/src/Tasks/AssemblyRegistrationCache.cs b/src/Tasks/AssemblyRegistrationCache.cs index 63ae6fdd833..3877f422450 100644 --- a/src/Tasks/AssemblyRegistrationCache.cs +++ b/src/Tasks/AssemblyRegistrationCache.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using Microsoft.Build.BackEnd; using Microsoft.Build.Shared; namespace Microsoft.Build.Tasks @@ -10,18 +11,19 @@ namespace Microsoft.Build.Tasks /// /// This class is a caching mechanism for the Register/UnregisterAssembly task to keep track of registered assemblies to clean up /// - [Serializable()] - internal sealed class AssemblyRegistrationCache : StateFileBase + /// Serializable should be included in all state files. It permits BinaryFormatter-based calls, including from GenerateResource, which we cannot move off BinaryFormatter. + [Serializable] + internal sealed class AssemblyRegistrationCache : StateFileBase, ITranslatable { /// /// The list of registered assembly files. /// - private readonly List _assemblies = new List(); + internal List _assemblies = new List(); /// /// The list of registered type library files. /// - private readonly List _typeLibraries = new List(); + internal List _typeLibraries = new List(); /// /// The number of entries in the state file @@ -53,5 +55,19 @@ internal void GetEntry(int index, out string assemblyPath, out string typeLibrar assemblyPath = _assemblies[index]; typeLibraryPath = _typeLibraries[index]; } + + public AssemblyRegistrationCache(ITranslator translator) + { + Translate(translator); + } + + public AssemblyRegistrationCache() { } + + public override void Translate(ITranslator translator) + { + ErrorUtilities.VerifyThrowArgumentNull(translator, nameof(translator)); + translator.Translate(ref _assemblies); + translator.Translate(ref _typeLibraries); + } } } diff --git a/src/Tasks/CSharpParserUtilities.cs b/src/Tasks/CSharpParserUtilities.cs index b0f343dd06f..facac7734c6 100644 --- a/src/Tasks/CSharpParserUtilities.cs +++ b/src/Tasks/CSharpParserUtilities.cs @@ -79,7 +79,16 @@ private static ExtractedClassName Extract(CSharpTokenizer tokens) { if (state.ResolvingNamespace) { - if (t.InnerText == ".") + // If we see a ';' while resolving a namespace, we assume it's a file-scoped namespace + // namespace foo.bar; <- At this point in code, we're at the semicolon. + // class test { ... } + // https://github.com/dotnet/csharplang/blob/088f20b6f9b714a7b68f6d792d54def0f3b3057e/proposals/csharp-10.0/file-scoped-namespaces.md + if (t.InnerText == ";") + { + state.PushNamespacePart(state.Namespace); + state.Reset(); + } + else if (t.InnerText == ".") { state.Namespace += "."; } diff --git a/src/Tasks/CombineTargetFrameworkInfoProperties.cs b/src/Tasks/CombineTargetFrameworkInfoProperties.cs index 612f27d3b88..bfd7caae236 100644 --- a/src/Tasks/CombineTargetFrameworkInfoProperties.cs +++ b/src/Tasks/CombineTargetFrameworkInfoProperties.cs @@ -2,11 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Microsoft.Build.Shared; using System.Xml.Linq; namespace Microsoft.Build.Tasks @@ -26,6 +22,11 @@ public class CombineTargetFrameworkInfoProperties : TaskExtension /// public ITaskItem[] PropertiesAndValues { get; set; } + /// + /// Opts into or out of using the new schema with Property Name=... rather than just specifying the RootElementName. + /// + public bool UseAttributeForTargetFrameworkInfoPropertyNames { get; set; } = false; + /// /// The generated XML representation of the properties and values. /// @@ -36,9 +37,11 @@ public override bool Execute() { if (PropertiesAndValues != null) { - XElement root = new XElement(RootElementName); + XElement root = UseAttributeForTargetFrameworkInfoPropertyNames ? + new("TargetFramework", new XAttribute("Name", EscapingUtilities.Escape(RootElementName))) : + new(RootElementName); - foreach (var item in PropertiesAndValues) + foreach (ITaskItem item in PropertiesAndValues) { root.Add(new XElement(item.ItemSpec, item.GetMetadata("Value"))); } diff --git a/src/Tasks/CombineXmlElements.cs b/src/Tasks/CombineXmlElements.cs index c42aed7f1bd..214207b1b6e 100644 --- a/src/Tasks/CombineXmlElements.cs +++ b/src/Tasks/CombineXmlElements.cs @@ -2,11 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using Microsoft.Build.Framework; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Xml.Linq; namespace Microsoft.Build.Tasks diff --git a/src/Tasks/Copy.cs b/src/Tasks/Copy.cs index ba793b673d6..6c91abeaab3 100644 --- a/src/Tasks/Copy.cs +++ b/src/Tasks/Copy.cs @@ -12,6 +12,7 @@ using Microsoft.Build.Utilities; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; +using Microsoft.Build.Eventing; namespace Microsoft.Build.Tasks { @@ -139,6 +140,9 @@ public Copy() [Output] public ITaskItem[] CopiedFiles { get; private set; } + [Output] + public bool WroteAtLeastOneFile { get; private set; } + /// /// Whether to overwrite files in the destination /// that have the read-only attribute set. @@ -298,6 +302,9 @@ FileState destinationFileState // The destination file File.Copy(sourceFileState.Name, destinationFileState.Name, true); } + + // Files were successfully copied or linked. Those are equivalent here. + WroteAtLeastOneFile = true; destinationFileState.Reset(); @@ -431,6 +438,7 @@ private bool CopySingleThreaded( { bool copyComplete = false; string destPath = DestinationFiles[i].ItemSpec; + MSBuildEventSource.Log.CopyUpToDateStart(destPath); if (filesActuallyCopied.TryGetValue(destPath, out string originalSource)) { if (String.Equals(originalSource, SourceFiles[i].ItemSpec, StringComparison.OrdinalIgnoreCase)) @@ -452,6 +460,10 @@ private bool CopySingleThreaded( success = false; } } + else + { + MSBuildEventSource.Log.CopyUpToDateStop(destPath, true); + } if (copyComplete) { @@ -534,6 +546,7 @@ private bool CopyParallel( string sourcePath = sourceItem.ItemSpec; // Check if we just copied from this location to the destination, don't copy again. + MSBuildEventSource.Log.CopyUpToDateStart(destItem.ItemSpec); bool copyComplete = partitionIndex > 0 && String.Equals( sourcePath, @@ -555,6 +568,10 @@ private bool CopyParallel( success = false; } } + else + { + MSBuildEventSource.Log.CopyUpToDateStop(destItem.ItemSpec, true); + } if (copyComplete) { @@ -710,6 +727,7 @@ private bool DoCopyIfNecessary(FileState sourceFileState, FileState destinationF "SkipUnchangedFiles", "true" ); + MSBuildEventSource.Log.CopyUpToDateStop(destinationFileState.Name, true); } // We only do the cheap check for identicalness here, we try the more expensive check // of comparing the fullpaths of source and destination to see if they are identical, @@ -719,8 +737,13 @@ private bool DoCopyIfNecessary(FileState sourceFileState, FileState destinationF destinationFileState.Name, StringComparison.OrdinalIgnoreCase)) { + MSBuildEventSource.Log.CopyUpToDateStop(destinationFileState.Name, false); success = DoCopyWithRetries(sourceFileState, destinationFileState, copyFile); } + else + { + MSBuildEventSource.Log.CopyUpToDateStop(destinationFileState.Name, true); + } } catch (OperationCanceledException) { diff --git a/src/Tasks/CreateItem.cs b/src/Tasks/CreateItem.cs index cb7bd833620..7b4bfa75764 100644 --- a/src/Tasks/CreateItem.cs +++ b/src/Tasks/CreateItem.cs @@ -31,14 +31,16 @@ public class CreateItem : TaskExtension /// A typical input: "metadataname1=metadatavalue1", "metadataname2=metadatavalue2", ... /// /// - /// The fact that this is a string[] makes the following illegal: - /// - /// The engine fails on this because it doesn't like item lists being concatenated with string - /// constants when the data is being passed into an array parameter. So the workaround is to - /// write this in the project file: - /// + /// ` + /// The engine fails on this because it doesn't like item lists being concatenated with string + /// constants when the data is being passed into an array parameter. So the workaround is to + /// write this in the project file: + /// `` + /// ]]> + /// /// public string[] AdditionalMetadata { get; set; } diff --git a/src/Tasks/CreateProperty.cs b/src/Tasks/CreateProperty.cs index 2d97dc4b6d0..9631f5cb80e 100644 --- a/src/Tasks/CreateProperty.cs +++ b/src/Tasks/CreateProperty.cs @@ -22,8 +22,8 @@ public class CreateProperty : TaskExtension /// Output TaskParameter="Value" PropertyName="MyTargetsToBuild" /// /CreateProperty /// - /// We need to respect the semicolon that he put in the value, and need to treat - /// this exactly as if he had done: + /// We need to respect the semicolon that they put in the value, and need to treat + /// this exactly as if they had done: /// /// PropertyGroup /// MyTargetsToBuild="Clean;Build" diff --git a/src/Tasks/Dependencies.cs b/src/Tasks/Dependencies.cs deleted file mode 100644 index 80761d7464f..00000000000 --- a/src/Tasks/Dependencies.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections; - -namespace Microsoft.Build.Tasks -{ - /// - /// Represents a cache of inputs to a compilation-style task. - /// - /// On-disk serialization format, don't change field names or types or use readonly. - [Serializable] - internal class Dependencies - { - /// - /// Hashtable of other dependency files. - /// Key is filename and value is DependencyFile. - /// - private Hashtable dependencies = new Hashtable(); - - /// - /// Look up a dependency file. Return null if its not there. - /// - /// - /// - internal DependencyFile GetDependencyFile(string filename) - { - return (DependencyFile)dependencies[filename]; - } - - /// - /// Add a new dependency file. - /// - internal void AddDependencyFile(string filename, DependencyFile file) - { - dependencies[filename] = file; - } - - /// - /// Remove new dependency file. - /// - internal void RemoveDependencyFile(string filename) - { - dependencies.Remove(filename); - } - - /// - /// Remove all entries from the dependency table. - /// - internal void Clear() - { - dependencies.Clear(); - } - } -} diff --git a/src/Tasks/DependencyFile.cs b/src/Tasks/DependencyFile.cs index f8306894c3b..d517f39a296 100644 --- a/src/Tasks/DependencyFile.cs +++ b/src/Tasks/DependencyFile.cs @@ -12,20 +12,19 @@ namespace Microsoft.Build.Tasks /// /// Represents a single input to a compilation-style task. /// Keeps track of timestamp for later comparison. - /// - /// On-disk serialization format, don't change field names or types or use readonly. /// + /// Serializable should be included in all state files. It permits BinaryFormatter-based calls, including from GenerateResource, which we cannot move off BinaryFormatter. [Serializable] internal class DependencyFile { // Filename - private string filename; + internal string filename; // Date and time the file was last modified - private DateTime lastModified; + internal DateTime lastModified; // Whether the file exists or not. - private bool exists = false; + internal bool exists = false; /// /// The name of the file. @@ -70,6 +69,10 @@ internal DependencyFile(string filename) } } + internal DependencyFile() + { + } + /// /// Checks whether the file has changed since the last time a timestamp was recorded. /// diff --git a/src/Tasks/FileIO/WriteLinesToFile.cs b/src/Tasks/FileIO/WriteLinesToFile.cs index c4fdfbb1a50..776e48bbddb 100644 --- a/src/Tasks/FileIO/WriteLinesToFile.cs +++ b/src/Tasks/FileIO/WriteLinesToFile.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using Microsoft.Build.Eventing; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using System; @@ -87,27 +88,33 @@ public override bool Execute() { Directory.CreateDirectory(directoryPath); string contentsAsString = buffer.ToString(); - try + + // When WriteOnlyWhenDifferent is set, read the file and if they're the same return. + if (WriteOnlyWhenDifferent) { - // When WriteOnlyWhenDifferent is set, read the file and if they're the same return. - if (WriteOnlyWhenDifferent && FileUtilities.FileExistsNoThrow(File.ItemSpec)) + MSBuildEventSource.Log.WriteLinesToFileUpToDateStart(); + try { - string existingContents = System.IO.File.ReadAllText(File.ItemSpec); - if (existingContents.Length == buffer.Length) + if (FileUtilities.FileExistsNoThrow(File.ItemSpec)) { - if (existingContents.Equals(contentsAsString)) + string existingContents = System.IO.File.ReadAllText(File.ItemSpec); + if (existingContents.Length == buffer.Length) { - Log.LogMessageFromResources(MessageImportance.Low, "WriteLinesToFile.SkippingUnchangedFile", File.ItemSpec); - return true; + if (existingContents.Equals(contentsAsString)) + { + Log.LogMessageFromResources(MessageImportance.Low, "WriteLinesToFile.SkippingUnchangedFile", File.ItemSpec); + MSBuildEventSource.Log.WriteLinesToFileUpToDateStop(File.ItemSpec, true); + return true; + } } } } + catch (IOException) + { + Log.LogMessageFromResources(MessageImportance.Low, "WriteLinesToFile.ErrorReadingFile", File.ItemSpec); + } + MSBuildEventSource.Log.WriteLinesToFileUpToDateStop(File.ItemSpec, false); } - catch (IOException) - { - Log.LogMessageFromResources(MessageImportance.Low, "WriteLinesToFile.ErrorReadingFile", File.ItemSpec); - } - System.IO.File.WriteAllText(File.ItemSpec, contentsAsString, encoding); } diff --git a/src/Tasks/GenerateResource.cs b/src/Tasks/GenerateResource.cs index bc15a0328a1..26967ca64e7 100644 --- a/src/Tasks/GenerateResource.cs +++ b/src/Tasks/GenerateResource.cs @@ -3151,10 +3151,13 @@ internal void ReadAssemblyResources(String name, String outFileOrDir) // We can't easily filter those. We can simply skip them. return; } - catch (Exception e) + catch (ArgumentException e) when (e.InnerException is BadImageFormatException) + { + // BadImageFormatExceptions can be wrapped in ArgumentExceptions, so catch those, too. See https://referencesource.microsoft.com/#mscorlib/system/reflection/module.cs,857 + return; + } + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { - if (ExceptionHandling.IsCriticalException(e)) - throw; _logger.LogErrorWithCodeFromResources("GenerateResource.CannotLoadAssemblyLoadFromFailed", name, e); } diff --git a/src/Tasks/GetCompatiblePlatform.cs b/src/Tasks/GetCompatiblePlatform.cs new file mode 100644 index 00000000000..c86c88199d7 --- /dev/null +++ b/src/Tasks/GetCompatiblePlatform.cs @@ -0,0 +1,154 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#nullable enable +using Microsoft.Build.Framework; +using Microsoft.Build.Shared; +using Microsoft.Build.Utilities; +using System; +using System.Collections.Generic; + +namespace Microsoft.Build.Tasks +{ + /// + /// Performs SetPlatform negotiation for all project references when opted + /// in via the EnableDynamicPlatformResolution property. + /// + /// See ProjectReference-Protocol.md for details. + /// + public class GetCompatiblePlatform : TaskExtension + { + /// + /// All ProjectReference items. + /// + [Required] + public ITaskItem[] AnnotatedProjects { get; set; } + + /// + /// The platform the current project is building as. + /// + [Required] + public string CurrentProjectPlatform { get; set; } + + /// + /// Optional parameter that defines mappings from current project platforms + /// to what the ProjectReference should build as. + /// Win32=x86, for example. + /// + public string PlatformLookupTable { get; set; } + + /// + /// The resulting items with NearestPlatform metadata set. + /// + [Output] + public ITaskItem[]? AssignedProjectsWithPlatform { get; set; } + + public GetCompatiblePlatform() + { + AnnotatedProjects = new ITaskItem[0]; + CurrentProjectPlatform = string.Empty; + PlatformLookupTable = string.Empty; + } + + public override bool Execute() + { + Dictionary? currentProjectLookupTable = ExtractLookupTable(PlatformLookupTable); + + AssignedProjectsWithPlatform = new ITaskItem[AnnotatedProjects.Length]; + for (int i = 0; i < AnnotatedProjects.Length; i++) + { + AssignedProjectsWithPlatform[i] = new TaskItem(AnnotatedProjects[i]); + + string projectReferencePlatformMetadata = AssignedProjectsWithPlatform[i].GetMetadata("Platforms"); + + if (string.IsNullOrEmpty(projectReferencePlatformMetadata)) + { + Log.LogWarningWithCodeFromResources("GetCompatiblePlatform.NoPlatformsListed", AssignedProjectsWithPlatform[i].ItemSpec); + continue; + } + + string projectReferenceLookupTableMetadata = AssignedProjectsWithPlatform[i].GetMetadata("PlatformLookupTable"); + // Pull platformlookuptable metadata from the referenced project. This allows custom + // mappings on a per-ProjectReference basis. + Dictionary? projectReferenceLookupTable = ExtractLookupTable(projectReferenceLookupTableMetadata); + + HashSet projectReferencePlatforms = new HashSet(); + foreach (string s in projectReferencePlatformMetadata.Split(MSBuildConstants.SemicolonChar, StringSplitOptions.RemoveEmptyEntries)) + { + projectReferencePlatforms.Add(s); + } + + string buildProjectReferenceAs = string.Empty; + + // Prefer matching platforms + if (projectReferencePlatforms.Contains(CurrentProjectPlatform)) + { + buildProjectReferenceAs = CurrentProjectPlatform; + Log.LogMessageFromResources(MessageImportance.Low, "GetCompatiblePlatform.SamePlatform"); + } + // Prioritize PlatformLookupTable **metadata** attached to the ProjectReference item + // before the current project's table. We do this to allow per-ProjectReference fine tuning. + else if (projectReferenceLookupTable != null && + projectReferenceLookupTable.ContainsKey(CurrentProjectPlatform) && + projectReferencePlatforms.Contains(projectReferenceLookupTable[CurrentProjectPlatform])) + { + buildProjectReferenceAs = projectReferenceLookupTable[CurrentProjectPlatform]; + Log.LogMessageFromResources(MessageImportance.Low, "GetCompatiblePlatform.FoundMappingInTable", CurrentProjectPlatform, buildProjectReferenceAs, projectReferenceLookupTableMetadata); + } + // Current project's translation table follows + else if (currentProjectLookupTable != null && + currentProjectLookupTable.ContainsKey(CurrentProjectPlatform) && + projectReferencePlatforms.Contains(currentProjectLookupTable[CurrentProjectPlatform])) + { + buildProjectReferenceAs = currentProjectLookupTable[CurrentProjectPlatform]; + Log.LogMessageFromResources(MessageImportance.Low, "GetCompatiblePlatform.FoundMappingInTable", CurrentProjectPlatform, buildProjectReferenceAs, PlatformLookupTable); + } + // AnyCPU if possible + else if (projectReferencePlatforms.Contains("AnyCPU")) + { + buildProjectReferenceAs = "AnyCPU"; + Log.LogMessageFromResources(MessageImportance.Low, "GetCompatiblePlatform.AnyCPUDefault"); + } + else + { + // Keep NearestPlatform empty, log a warning. Common.CurrentVersion.targets will undefine + // Platform/PlatformTarget when this is the case. + Log.LogWarningWithCodeFromResources("GetCompatiblePlatform.NoCompatiblePlatformFound", AssignedProjectsWithPlatform[i].ItemSpec); + } + + AssignedProjectsWithPlatform[i].SetMetadata("NearestPlatform", buildProjectReferenceAs); + Log.LogMessageFromResources(MessageImportance.Low, "GetCompatiblePlatform.DisplayChosenPlatform", AssignedProjectsWithPlatform[i].ItemSpec, buildProjectReferenceAs); + } + + return !Log.HasLoggedErrors; + } + + private Dictionary? ExtractLookupTable(string stringTable) + { + if (string.IsNullOrEmpty(stringTable)) + { + return null; + } + + Dictionary table = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (string s in stringTable.Trim().Split(MSBuildConstants.SemicolonChar, StringSplitOptions.RemoveEmptyEntries)) + { + string[] keyVal = s.Trim().Split(MSBuildConstants.EqualsChar); + + // Invalid table, don't use it. + if (keyVal.Length != 2 || string.IsNullOrEmpty(keyVal[0]) || string.IsNullOrEmpty(keyVal[1])) + { + Log.LogWarningWithCodeFromResources("GetCompatiblePlatform.InvalidLookupTableFormat", stringTable); + return null; + } + + table[keyVal[0]] = keyVal[1]; + } + + Log.LogMessageFromResources(MessageImportance.Low, "GetCompatiblePlatform.LookupTableParsed", stringTable); + + return table; + } + } +} diff --git a/src/Tasks/GetSDKReferenceFiles.cs b/src/Tasks/GetSDKReferenceFiles.cs index 43b7d8ffcd9..dfc8d0050ac 100644 --- a/src/Tasks/GetSDKReferenceFiles.cs +++ b/src/Tasks/GetSDKReferenceFiles.cs @@ -9,9 +9,9 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Runtime.Serialization.Formatters.Binary; using System.Threading; using System.Threading.Tasks; +using Microsoft.Build.BackEnd; using Microsoft.Build.Framework; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; @@ -877,7 +877,7 @@ public bool Equals(ResolvedRedistFile other) /// /// Methods which are used to save and read the cache files per sdk from and to disk. /// - private class SDKFilesCache + internal class SDKFilesCache { /// /// Thread-safe queue which contains exceptions throws during cache file reading and writing. @@ -927,16 +927,15 @@ internal SDKInfo LoadAssemblyListFromCacheFile(string sdkIdentity, string sdkRoo { if (!string.IsNullOrEmpty(cacheFile)) { - return SDKInfo.Deserialize(cacheFile); + using FileStream fs = new FileStream(cacheFile, FileMode.Open); + using var translator = BinaryTranslator.GetReadTranslator(fs, buffer: null); + SDKInfo sdkInfo = new SDKInfo(); + sdkInfo.Translate(translator); + return sdkInfo; } } - catch (Exception e) + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { - if (ExceptionHandling.IsCriticalException(e)) - { - throw; - } - // Queue up for later logging, does not matter if the file is deleted or not _exceptionMessages.Enqueue(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("GetSDKReferenceFiles.ProblemReadingCacheFile", cacheFile, e.ToString())); } @@ -965,31 +964,21 @@ internal void SaveAssemblyListToCacheFile(object data) { File.Delete(existingCacheFile); } - catch (Exception e) + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { - if (ExceptionHandling.IsCriticalException(e)) - { - throw; - } - // Queue up for later logging, does not matter if the file is deleted or not _exceptionMessages.Enqueue(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("GetSDKReferenceFiles.ProblemDeletingCacheFile", existingCacheFile, e.Message)); } } - var formatter = new BinaryFormatter(); using (var fs = new FileStream(referencesCacheFile, FileMode.Create)) { - formatter.Serialize(fs, cacheFileInfo); + var translator = BinaryTranslator.GetWriteTranslator(fs); + cacheFileInfo.Translate(translator); } } - catch (Exception e) + catch (Exception e) when (!ExceptionHandling.IsCriticalException(e)) { - if (ExceptionHandling.IsCriticalException(e)) - { - throw; - } - // Queue up for later logging, does not matter if the cache got written _exceptionMessages.Enqueue(ResourceUtilities.FormatResourceStringStripCodeAndKeyword("GetSDKReferenceFiles.ProblemWritingCacheFile", referencesCacheFile, e.Message)); } @@ -1205,11 +1194,8 @@ private static IEnumerable GetAllReferenceDirectories(string sdkRoot) /// /// This is a serialization format. Do not change member naming. [Serializable] - private class SdkReferenceInfo + internal class SdkReferenceInfo { - /// - /// Constructor - /// public SdkReferenceInfo(string fusionName, string imageRuntime, bool isWinMD, bool isManagedWinmd) { FusionName = fusionName; @@ -1219,25 +1205,11 @@ public SdkReferenceInfo(string fusionName, string imageRuntime, bool isWinMD, bo } #region Properties - /// - /// The fusionName - /// - public string FusionName { get; } - /// - /// Is the file a winmd or not - /// - public bool IsWinMD { get; } - - /// - /// Is the file a managed winmd or not - /// - public bool IsManagedWinmd { get; } - - /// - /// What is the imageruntime information on it. - /// - public string ImageRuntime { get; } + public string FusionName { get; internal set; } + public bool IsWinMD { get; internal set; } + public bool IsManagedWinmd { get; internal set; } + public string ImageRuntime { get; internal set; } #endregion } @@ -1245,64 +1217,75 @@ public SdkReferenceInfo(string fusionName, string imageRuntime, bool isWinMD, bo /// /// Structure that contains the on disk representation of the SDK in memory. /// - /// This is a serialization format. Do not change member naming. - [Serializable] - private class SDKInfo + internal class SDKInfo : ITranslatable { - // Current version for serialization. This should be changed when breaking changes - // are made to this class. - private const byte CurrentSerializationVersion = 1; + private IDictionary _pathToReferenceMetadata; + private IDictionary> _directoryToFileList; + private int _hash; - // Version this instance is serialized with. - private byte _serializedVersion = CurrentSerializationVersion; + internal SDKInfo() + { + _pathToReferenceMetadata = new Dictionary(StringComparer.OrdinalIgnoreCase); + _directoryToFileList = new Dictionary>(StringComparer.OrdinalIgnoreCase); + _hash = 0; + } - /// - /// Constructor - /// - public SDKInfo(ConcurrentDictionary pathToReferenceMetadata, ConcurrentDictionary> directoryToFileList, int cacheHash) + public SDKInfo(ITranslator translator) : this() + { + Translate(translator); + } + + public SDKInfo(IDictionary pathToReferenceMetadata, IDictionary> directoryToFileList, int cacheHash) { - PathToReferenceMetadata = pathToReferenceMetadata; - DirectoryToFileList = directoryToFileList; - Hash = cacheHash; + this._pathToReferenceMetadata = pathToReferenceMetadata; + this._directoryToFileList = directoryToFileList; + this._hash = cacheHash; } /// /// A dictionary which maps a file path to a structure that contain some metadata information about that file. /// - public ConcurrentDictionary PathToReferenceMetadata { get; } + public IDictionary PathToReferenceMetadata { get { return _pathToReferenceMetadata; } } - /// - /// Dictionary which maps a directory to a list of file names within that directory. This is used to shortcut hitting the disk for the list of files inside of it. - /// - public ConcurrentDictionary> DirectoryToFileList { get; } + public IDictionary> DirectoryToFileList { get { return _directoryToFileList; } } /// /// Hashset /// - public int Hash { get; } + public int Hash { get { return _hash; } } - public static SDKInfo Deserialize(string cacheFile) + public void Translate(ITranslator translator) { - using (var fs = new FileStream(cacheFile, FileMode.Open)) + translator.TranslateDictionary(ref _pathToReferenceMetadata, (ITranslator t, ref string s) => t.Translate(ref s), (ITranslator t, ref SdkReferenceInfo info) => { - var formatter = new BinaryFormatter(); - var info = (SDKInfo)formatter.Deserialize(fs); - - // If the serialization versions don't match, don't use the cache - if (info != null && info._serializedVersion != CurrentSerializationVersion) - { - return null; - } + info ??= new SdkReferenceInfo(null, null, false, false); + string fusionName = info.FusionName; + string imageRuntime = info.ImageRuntime; + bool isManagedWinmd = info.IsManagedWinmd; + bool isWinmd = info.IsWinMD; + t.Translate(ref fusionName); + t.Translate(ref imageRuntime); + t.Translate(ref isManagedWinmd); + t.Translate(ref isWinmd); + info.FusionName = fusionName; + info.ImageRuntime = imageRuntime; + info.IsManagedWinmd = isManagedWinmd; + info.IsWinMD = isWinmd; + }, count => new Dictionary(count, StringComparer.OrdinalIgnoreCase)); + + translator.TranslateDictionary(ref _directoryToFileList, (ITranslator t, ref string s) => t.Translate(ref s), (ITranslator t, ref List fileList) => + { + t.Translate(ref fileList, (ITranslator t, ref string str) => { t.Translate(ref str); }); + }, count => new Dictionary>(count, StringComparer.OrdinalIgnoreCase)); - return info; - } + translator.Translate(ref _hash); } } /// /// This class represents the context information used by the background cache serialization thread. /// - private class SaveContext + internal class SaveContext { /// /// Constructor diff --git a/src/Tasks/Hash.cs b/src/Tasks/Hash.cs index c822fa90f90..81699764e51 100644 --- a/src/Tasks/Hash.cs +++ b/src/Tasks/Hash.cs @@ -11,11 +11,11 @@ namespace Microsoft.Build.Tasks { /// /// Generates a hash of a given ItemGroup items. Metadata is not considered in the hash. + /// /// /// Currently uses SHA1. Implementation subject to change between MSBuild versions. Not /// intended as a cryptographic security measure, only uniqueness between build executions. /// - /// public class Hash : TaskExtension { private const char ItemSeparatorCharacter = '\u2028'; diff --git a/src/Tasks/MSBuild.cs b/src/Tasks/MSBuild.cs index 0d5fe24e603..6bbc68964b3 100644 --- a/src/Tasks/MSBuild.cs +++ b/src/Tasks/MSBuild.cs @@ -55,14 +55,16 @@ private enum SkipNonexistentProjectsBehavior /// A typical input: "propname1=propvalue1", "propname2=propvalue2", "propname3=propvalue3". /// /// - /// The fact that this is a string[] makes the following illegal: - /// - /// The engine fails on this because it doesn't like item lists being concatenated with string - /// constants when the data is being passed into an array parameter. So the workaround is to - /// write this in the project file: - /// + /// ` + /// The engine fails on this because it doesn't like item lists being concatenated with string + /// constants when the data is being passed into an array parameter. So the workaround is to + /// write this in the project file: + /// `` + /// ]]> + /// /// public string[] Properties { get; set; } diff --git a/src/Tasks/ManifestUtil/Resources/xlf/Strings.ManifestUtilities.en.xlf b/src/Tasks/ManifestUtil/Resources/xlf/Strings.ManifestUtilities.en.xlf deleted file mode 100644 index 5949f78fc3a..00000000000 --- a/src/Tasks/ManifestUtil/Resources/xlf/Strings.ManifestUtilities.en.xlf +++ /dev/null @@ -1,99 +0,0 @@ - - - - - - Out of process servers are not supported - Out of process servers are not supported - - - - Registry key '{0}' is missing value '{1}'. - Registry key '{0}' is missing value '{1}'. - - - - No registered classes were detected for this component. - No registered classes were detected for this component. - - - - Components under system file protection should not be isolated. - Components under system file protection should not be isolated. - - - - Could not load type library. - Could not load type library. - - - - Registry key '{0}' was not imported. - Registry key '{0}' was not imported. - - - - Registry value '{0}' was not imported. - Registry value '{0}' was not imported. - - - - The file to be signed {0} could not be found. - The file to be signed {0} could not be found. - - - - Failed to sign {0}. {1} - Failed to sign {0}. {1} - - - - Warning while signing {0}. {1} - Warning while signing {0}. {1} - - - - SignTool.exe was not found at path {0}. - SignTool.exe was not found at path {0}. - - - - Timestamp URL server name or address could not be resolved. - Timestamp URL server name or address could not be resolved. - - - - Only certificates using RSA encryption are valid for signing ClickOnce manifests. - Only certificates using RSA encryption are valid for signing ClickOnce manifests. - - - - - UAC Manifest Options - If you want to change the Windows User Account Control level replace the - requestedExecutionLevel node with one of the following. - - <requestedExecutionLevel level="asInvoker" uiAccess="false" /> - <requestedExecutionLevel level="requireAdministrator" uiAccess="false" /> - <requestedExecutionLevel level="highestAvailable" uiAccess="false" /> - - If you want to utilize File and Registry Virtualization for backward - compatibility then delete the requestedExecutionLevel node. - - - UAC Manifest Options - If you want to change the Windows User Account Control level replace the - requestedExecutionLevel node with one of the following. - - <requestedExecutionLevel level="asInvoker" uiAccess="false" /> - <requestedExecutionLevel level="requireAdministrator" uiAccess="false" /> - <requestedExecutionLevel level="highestAvailable" uiAccess="false" /> - - If you want to utilize File and Registry Virtualization for backward - compatibility then delete the requestedExecutionLevel node. - - - - - - diff --git a/src/Tasks/ManifestUtil/Resources/xlf/Strings.ManifestUtilities.zh-Hans.xlf b/src/Tasks/ManifestUtil/Resources/xlf/Strings.ManifestUtilities.zh-Hans.xlf index 3c85e336dcc..f962020b89a 100644 --- a/src/Tasks/ManifestUtil/Resources/xlf/Strings.ManifestUtilities.zh-Hans.xlf +++ b/src/Tasks/ManifestUtil/Resources/xlf/Strings.ManifestUtilities.zh-Hans.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Tasks/ManifestUtil/Resources/xlf/Strings.ManifestUtilities.zh-Hant.xlf b/src/Tasks/ManifestUtil/Resources/xlf/Strings.ManifestUtilities.zh-Hant.xlf index a0726f458b3..37d5db669fa 100644 --- a/src/Tasks/ManifestUtil/Resources/xlf/Strings.ManifestUtilities.zh-Hant.xlf +++ b/src/Tasks/ManifestUtil/Resources/xlf/Strings.ManifestUtilities.zh-Hant.xlf @@ -1,4 +1,4 @@ - + diff --git a/src/Tasks/ManifestUtil/SecurityUtil.cs b/src/Tasks/ManifestUtil/SecurityUtil.cs index 68fc07ccf03..b7c34b2bb5f 100644 --- a/src/Tasks/ManifestUtil/SecurityUtil.cs +++ b/src/Tasks/ManifestUtil/SecurityUtil.cs @@ -582,7 +582,11 @@ public static void SignFile(string certPath, SecureString certPassword, Uri time private static bool UseSha256Algorithm(X509Certificate2 cert) { Oid oid = cert.SignatureAlgorithm; - return string.Equals(oid.FriendlyName, "sha256RSA", StringComparison.OrdinalIgnoreCase); + // Issue 6732: Clickonce does not support sha384/sha512 file hash so we default to sha256 + // for certs with that signature algorithm. + return string.Equals(oid.FriendlyName, "sha256RSA", StringComparison.OrdinalIgnoreCase) || + string.Equals(oid.FriendlyName, "sha384RSA", StringComparison.OrdinalIgnoreCase) || + string.Equals(oid.FriendlyName, "sha512RSA", StringComparison.OrdinalIgnoreCase); } /// @@ -864,19 +868,20 @@ private static bool IsCertInStore(X509Certificate2 cert) private static string GetVersionIndependentToolPath(string toolName) { - RegistryKey localMachineKey = Registry.LocalMachine; const string versionIndependentToolKeyName = @"Software\Microsoft\ClickOnce\SignTool"; - - using (RegistryKey versionIndependentToolKey = localMachineKey.OpenSubKey(versionIndependentToolKeyName, writable: false)) + using (RegistryKey localMachineKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32)) { - string versionIndependentToolPath = null; - - if (versionIndependentToolKey != null) + using (RegistryKey versionIndependentToolKey = localMachineKey.OpenSubKey(versionIndependentToolKeyName, writable: false)) { - versionIndependentToolPath = versionIndependentToolKey.GetValue("Path") as string; - } + string versionIndependentToolPath = null; - return versionIndependentToolPath != null ? Path.Combine(versionIndependentToolPath, toolName) : null; + if (versionIndependentToolKey != null) + { + versionIndependentToolPath = versionIndependentToolKey.GetValue("Path") as string; + } + + return versionIndependentToolPath != null ? Path.Combine(versionIndependentToolPath, toolName) : null; + } } } } diff --git a/src/Tasks/Microsoft.Build.Tasks.csproj b/src/Tasks/Microsoft.Build.Tasks.csproj index 183dabbcac3..d848805a5c3 100644 --- a/src/Tasks/Microsoft.Build.Tasks.csproj +++ b/src/Tasks/Microsoft.Build.Tasks.csproj @@ -15,7 +15,7 @@ true This package contains the $(MSBuildProjectName) assembly which implements the commonly used tasks of MSBuild. false - partial + full @@ -91,7 +91,7 @@ NGen.cs - + PropertyParser.cs True @@ -342,6 +342,7 @@ true + @@ -500,6 +501,9 @@ true + + true + @@ -530,7 +534,6 @@ - @@ -649,9 +652,6 @@ true - - true - true @@ -806,6 +806,9 @@ PreserveNewest + + PreserveNewest + $(AssemblyName).Strings.resources @@ -977,10 +980,11 @@ + + - diff --git a/src/Tasks/Microsoft.CSharp.CurrentVersion.targets b/src/Tasks/Microsoft.CSharp.CurrentVersion.targets index 0f44610476a..1f9d78bdbd1 100644 --- a/src/Tasks/Microsoft.CSharp.CurrentVersion.targets +++ b/src/Tasks/Microsoft.CSharp.CurrentVersion.targets @@ -243,70 +243,71 @@ Copyright (C) Microsoft Corporation. All rights reserved. - + + false true + + + false + $(Platforms) @@ -165,7 +169,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. --> $([MSBuild]::IsRunningFromVisualStudio()) - $(MSBuildToolsPath32)\..\..\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets + $([MSBuild]::GetToolsDirectory32())\..\..\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets $(MSBuildToolsPath)\NuGet.targets @@ -216,8 +220,4 @@ Copyright (C) Microsoft Corporation. All rights reserved. - - - diff --git a/src/Tasks/Microsoft.Common.CurrentVersion.targets b/src/Tasks/Microsoft.Common.CurrentVersion.targets index 6a813f7c7e1..74801c9b397 100644 --- a/src/Tasks/Microsoft.Common.CurrentVersion.targets +++ b/src/Tasks/Microsoft.Common.CurrentVersion.targets @@ -185,6 +185,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_DebugSymbolsProduced Condition="'$(DebugType)'=='full'">true <_DebugSymbolsProduced Condition="'$(DebugType)'=='portable'">true <_DebugSymbolsProduced Condition="'$(DebugType)'=='embedded'">false + <_DebugSymbolsProduced Condition="'$(ProduceOnlyReferenceAssembly)'=='true'">false <_DocumentationFileProduced>true @@ -580,7 +581,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. AssemblyFoldersEx Software\Microsoft\$(TargetFrameworkIdentifier) Software\Microsoft\Microsoft SDKs\$(TargetPlatformIdentifier) - $(MSBuildToolsPath32)\AssemblyFolders.config + $([MSBuild]::GetToolsDirectory32())\AssemblyFolders.config {AssemblyFoldersFromConfig:$(AssemblyFoldersConfigFile),$(TargetFrameworkVersion)}; - + $(NoWarn) $(WarningsAsErrors) @@ -1618,6 +1619,82 @@ Copyright (C) Microsoft Corporation. All rights reserved. + + + + true + + + + + + + + <_MSBuildProjectReferenceExistent Condition="'%(_MSBuildProjectReferenceExistent.SetPlatform)' != ''"> + true + + + + + <_ProjectReferencePlatformPossibilities Include="@(_MSBuildProjectReferenceExistent)" + Condition="'%(_MSBuildProjectReferenceExistent.SkipGetPlatformProperties)' != 'true'"/> + + + + + + <_ProjectReferencePlatformPossibilities Condition="'$(MSBuildProjectExtension)' != '.vcxproj' and '$(MSBuildProjectExtension)' != '.nativeproj' and '%(_ProjectReferencePlatformPossibilities.IsVcxOrNativeProj)' == 'true'"> + + x86=Win32 + + + + <_ProjectReferencePlatformPossibilities Condition="('$(MSBuildProjectExtension)' == '.vcxproj' or '$(MSBuildProjectExtension)' == '.nativeproj') and '%(_ProjectReferencePlatformPossibilities.IsVcxOrNativeProj)' != 'true'"> + Win32=x86 + + + + + + + + + + + + Platform=%(ProjectsWithNearestPlatform.NearestPlatform) + + + + + %(ProjectsWithNearestPlatform.UndefineProperties);Platform + + + <_MSBuildProjectReferenceExistent Remove="@(_MSBuildProjectReferenceExistent)" Condition="'%(_MSBuildProjectReferenceExistent.SkipGetPlatformProperties)' != 'true'"/> + <_MSBuildProjectReferenceExistent Include="@(ProjectsWithNearestPlatform)"/> + + + + <_MSBuildProjectReferenceExistent Condition="'%(_MSBuildProjectReferenceExistent.SkipGetTargetFrameworkProperties)' == '' and ('%(Extension)' == '.vcxproj' or '%(Extension)' == '.nativeproj')"> - true + + true %(_MSBuildProjectReferenceExistent.UndefineProperties);TargetFramework @@ -1691,7 +1772,11 @@ Copyright (C) Microsoft Corporation. All rights reserved. --> <_MSBuildProjectReferenceExistent Condition="'%(_MSBuildProjectReferenceExistent.SetTargetFramework)' != ''"> - true + + true @@ -1735,8 +1820,8 @@ Copyright (C) Microsoft Corporation. All rights reserved. CurrentProjectTargetPlatform="$(TargetPlatformMoniker)" CurrentProjectName="$(MSBuildProjectName)" FallbackTargetFrameworks="$(AssetTargetFallback)" - Condition="'@(_ProjectReferenceTargetFrameworkPossibilities->Count())' != '0' and '$(ReferringTargetFrameworkForProjectReferences)' != '' - And '$(GetReferenceNearestTargetFrameworkTaskSupportsTargetPlatformParameter)' == 'true'"> + Condition="'@(_ProjectReferenceTargetFrameworkPossibilities)' != '' and '$(ReferringTargetFrameworkForProjectReferences)' != '' + And '$(GetReferenceNearestTargetFrameworkTaskSupportsTargetPlatformParameter)' == 'true' and '%(_ProjectReferenceTargetFrameworkPossibilities.IsVcxOrNativeProj)' != 'true'"> @@ -1744,8 +1829,8 @@ Copyright (C) Microsoft Corporation. All rights reserved. CurrentProjectTargetFramework="$(ReferringTargetFrameworkForProjectReferences)" CurrentProjectName="$(MSBuildProjectName)" FallbackTargetFrameworks="$(AssetTargetFallback)" - Condition="'@(_ProjectReferenceTargetFrameworkPossibilities->Count())' != '0' and '$(ReferringTargetFrameworkForProjectReferences)' != '' - And '$(GetReferenceNearestTargetFrameworkTaskSupportsTargetPlatformParameter)' != 'true'"> + Condition="'@(_ProjectReferenceTargetFrameworkPossibilities)' != '' and '$(ReferringTargetFrameworkForProjectReferences)' != '' + And '$(GetReferenceNearestTargetFrameworkTaskSupportsTargetPlatformParameter)' != 'true' and '%(_ProjectReferenceTargetFrameworkPossibilities.IsVcxOrNativeProj)' != 'true'"> @@ -1754,9 +1839,14 @@ Copyright (C) Microsoft Corporation. All rights reserved. If the task was skipped or the current TargetFramework is empty, AnnotatedProjects will be empty. In this case, copy _ProjectReferenceTargetFrameworkPossibilities as is. See: https://github.com/dotnet/sdk/issues/416 + + Furthermore, if we're referencing a .vcxproj or .nativeproj, those items won't be populated into `AnnotatedProjects` + by `GetReferenceNearestTargetFrameworkTask`, so let them flow when `EnableDynamicPlatformResolution` is set. --> + Condition="'$(ReferringTargetFrameworkForProjectReferences)' == '' or + ('$(EnableDynamicPlatformResolution)' == 'true' and '%(_ProjectReferenceTargetFrameworkPossibilities.IsVcxOrNativeProj)' == 'true')" /> + TargetFramework=%(AnnotatedProjects.NearestTargetFramework) @@ -1808,6 +1898,12 @@ Copyright (C) Microsoft Corporation. All rights reserved. false true + + true + $(Platforms) + + @(ProjectConfiguration->'%(Platform)'->Distinct()) @@ -1822,9 +1918,14 @@ Copyright (C) Microsoft Corporation. All rights reserved. + + <_UseAttributeForTargetFrameworkInfoPropertyNames Condition="'$(_UseAttributeForTargetFrameworkInfoPropertyNames)' == ''">false + + + PropertiesAndValues="@(_AdditionalTargetFrameworkInfoPropertyWithValue)" + UseAttributeForTargetFrameworkInfoPropertyNames="$(_UseAttributeForTargetFrameworkInfoPropertyNames)"> @@ -1875,7 +1976,8 @@ Copyright (C) Microsoft Corporation. All rights reserved. AssignProjectConfiguration; _SplitProjectReferencesByFileExistence; - _GetProjectReferenceTargetFrameworkProperties + _GetProjectReferenceTargetFrameworkProperties; + _GetProjectReferencePlatformProperties @@ -2330,9 +2432,10 @@ Copyright (C) Microsoft Corporation. All rights reserved. ==================================================================================================== --> + Condition="'$(AutoGenerateBindingRedirects)' == 'true' and '$(GenerateBindingRedirectsOutputType)' == 'true'" + DependsOnTargets="_GenerateSuggestedBindingRedirectsCache"> + + + + $(IntermediateOutputPath)$(MSBuildProjectFile).SuggestedBindingRedirects.cache + + + + + + + + + + + + + + <_FileAssociationIcons Include="%(FileAssociation.DefaultIcon)"/> + <_ClickOnceFiles Include="@(ContentWithTargetPath)" Condition="'%(Identity)'=='@(_FileAssociationIcons)'"/> @@ -4705,6 +4839,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. + @@ -4713,10 +4848,12 @@ Copyright (C) Microsoft Corporation. All rights reserved. not be considered up to date, so touch this marker file that is considered an input to projects that reference this one. --> - - + AlwaysCreate="true" + Condition="'@(ReferencesCopiedInThisBuild)' != '' and '$(WroteAtLeastOneFile)' == 'true'" /> + + + + @@ -4736,13 +4873,11 @@ Copyright (C) Microsoft Corporation. All rights reserved. GetCopyToOutputDirectoryItems Get all project items that may need to be transferred to the output directory. - This includes baggage items from transitively referenced projects. It would appear - that this target computes full transitive closure of content items for all referenced - projects; however that is not the case. It only collects the content items from its - immediate children and not children of children. The reason this happens is that - the ProjectReferenceWithConfiguration list that is consumed by _SplitProjectReferencesByFileExistence - is only populated in the current project and is empty in the children. The empty list - causes _MSBuildProjectReferenceExistent to be empty and terminates the recursion. + This includes baggage items from transitively referenced projects. + + As of 17.0, content items are copied transitively by default. + Set `MSBuildCopyContentTransitively` to false to opt out. + See https://github.com/dotnet/msbuild/pull/6622 for more info. ============================================================ --> @@ -4751,9 +4886,10 @@ Copyright (C) Microsoft Corporation. All rights reserved. GetCopyToOutputDirectoryItems depends on an unspecified dependency _SplitProjectReferencesByFileExistence -> AssignProjectConfiguration (https://github.com/microsoft/msbuild/issues/4677). When the unspecified dependency does not happen by accident, content copying is only 1 level deep instead of transitive. This target enforces the dependency. - - TODO: make transitive content copying the default when the breaking change is acceptable. --> + + true + <_TargetsThatPrepareProjectReferences Condition=" '$(MSBuildCopyContentTransitively)' == 'true' "> AssignProjectConfiguration; _SplitProjectReferencesByFileExistence @@ -6546,7 +6682,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. $([MSBuild]::IsRunningFromVisualStudio()) - $(MSBuildToolsPath32)\..\..\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets + $([MSBuild]::GetToolsDirectory32())\..\..\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.targets $(MSBuildToolsPath)\NuGet.targets diff --git a/src/Tasks/Microsoft.Common.props b/src/Tasks/Microsoft.Common.props index 32f5f05ab9f..b08b6558352 100644 --- a/src/Tasks/Microsoft.Common.props +++ b/src/Tasks/Microsoft.Common.props @@ -171,7 +171,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. --> $([MSBuild]::IsRunningFromVisualStudio()) - $(MSBuildToolsPath32)\..\..\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.props + $([MSBuild]::GetToolsDirectory32())\..\..\..\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.props $(MSBuildToolsPath)\NuGet.props diff --git a/src/Tasks/Microsoft.Common.tasks b/src/Tasks/Microsoft.Common.tasks index 6cef8ef8661..f6b98da83af 100644 --- a/src/Tasks/Microsoft.Common.tasks +++ b/src/Tasks/Microsoft.Common.tasks @@ -127,6 +127,7 @@ + diff --git a/src/Tasks/Microsoft.VisualStudioVersion.v17.Common.props b/src/Tasks/Microsoft.VisualStudioVersion.v17.Common.props new file mode 100644 index 00000000000..452a3fe9cd4 --- /dev/null +++ b/src/Tasks/Microsoft.VisualStudioVersion.v17.Common.props @@ -0,0 +1,20 @@ + + + + + + 17.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + diff --git a/src/Tasks/ResGenDependencies.cs b/src/Tasks/ResGenDependencies.cs index 3632a916d83..35b26a5e72f 100644 --- a/src/Tasks/ResGenDependencies.cs +++ b/src/Tasks/ResGenDependencies.cs @@ -8,7 +8,7 @@ using System.IO; using System.Resources; using System.Xml; - +using Microsoft.Build.BackEnd; using Microsoft.Build.Shared; using Microsoft.Build.Shared.FileSystem; using Microsoft.Build.Tasks.ResourceHandling; @@ -22,18 +22,19 @@ namespace Microsoft.Build.Tasks /// /// This is an on-disk serialization format, don't change field names or types or use readonly. /// + /// Serializable should be included in all state files. It permits BinaryFormatter-based calls, including from GenerateResource, which we cannot move off BinaryFormatter. [Serializable] - internal sealed class ResGenDependencies : StateFileBase + internal sealed class ResGenDependencies : StateFileBase, ITranslatable { /// /// The list of resx files. /// - private Dependencies resXFiles = new Dependencies(); + internal IDictionary resXFiles = new Dictionary(); /// /// A list of portable libraries and the ResW files they can produce. /// - private Dependencies portableLibraries = new Dependencies(); + internal IDictionary portableLibraries = new Dictionary(); /// /// A newly-created ResGenDependencies is not dirty. @@ -47,7 +48,7 @@ internal sealed class ResGenDependencies : StateFileBase /// If this is NULL then we use the directory in which the .resx is in (that should always /// be the default!) /// - private string baseLinkedFileDirectory; + internal string baseLinkedFileDirectory; internal string BaseLinkedFileDirectory { @@ -90,11 +91,38 @@ internal bool UseSourcePath } } + public ResGenDependencies() { } + + public ResGenDependencies(ITranslator translator) + { + Translate(translator); + } + + public override void Translate(ITranslator translator) + { + translator.TranslateDictionary(ref resXFiles, + (ITranslator translator, ref string s) => translator.Translate(ref s), + (ITranslator translator, ref ResXFile resx) => { + ResXFile temp = resx ?? new(); + temp.Translate(translator); + resx = temp; + }, + count => new Dictionary(count)); + translator.TranslateDictionary(ref portableLibraries, + (ITranslator translator, ref string s) => translator.Translate(ref s), + (ITranslator translator, ref PortableLibraryFile portableLibrary) => { + PortableLibraryFile temp = portableLibrary ?? new(); + temp.Translate(translator); + portableLibrary = temp; + }, + count => new Dictionary(count)); + translator.Translate(ref baseLinkedFileDirectory); + } + internal ResXFile GetResXFileInfo(string resxFile, bool useMSBuildResXReader) { // First, try to retrieve the resx information from our hashtable. - var retVal = (ResXFile)resXFiles.GetDependencyFile(resxFile); - if (retVal == null) + if (!resXFiles.TryGetValue(resxFile, out ResXFile retVal)) { // Ok, the file wasn't there. Add it to our cache and return it to the caller. retVal = AddResxFile(resxFile, useMSBuildResXReader); @@ -105,7 +133,7 @@ internal ResXFile GetResXFileInfo(string resxFile, bool useMSBuildResXReader) // by removing it from the hashtable and readding it. if (retVal.HasFileChanged()) { - resXFiles.RemoveDependencyFile(resxFile); + resXFiles.Remove(resxFile); _isDirty = true; retVal = AddResxFile(resxFile, useMSBuildResXReader); } @@ -120,7 +148,7 @@ private ResXFile AddResxFile(string file, bool useMSBuildResXReader) // to be cracked for contained files. var resxFile = new ResXFile(file, BaseLinkedFileDirectory, useMSBuildResXReader); - resXFiles.AddDependencyFile(file, resxFile); + resXFiles.Add(file, resxFile); _isDirty = true; return resxFile; } @@ -128,13 +156,13 @@ private ResXFile AddResxFile(string file, bool useMSBuildResXReader) internal PortableLibraryFile TryGetPortableLibraryInfo(string libraryPath) { // First, try to retrieve the portable library information from our hashtable. - var retVal = (PortableLibraryFile)portableLibraries.GetDependencyFile(libraryPath); + portableLibraries.TryGetValue(libraryPath, out PortableLibraryFile retVal); // The file is in our cache. Make sure it's up to date. If not, discard // this entry from the cache and rebuild all the state at a later point. if (retVal?.HasFileChanged() == true) { - portableLibraries.RemoveDependencyFile(libraryPath); + portableLibraries.Remove(libraryPath); _isDirty = true; retVal = null; } @@ -144,11 +172,10 @@ internal PortableLibraryFile TryGetPortableLibraryInfo(string libraryPath) internal void UpdatePortableLibrary(PortableLibraryFile library) { - var cached = (PortableLibraryFile)portableLibraries.GetDependencyFile(library.FileName); - if (cached == null || !library.Equals(cached)) + if (!portableLibraries.TryGetValue(library.FileName, out PortableLibraryFile cached) || !library.Equals(cached)) { // Add a new entry or replace the existing one. - portableLibraries.AddDependencyFile(library.FileName, library); + portableLibraries.Add(library.FileName, library); _isDirty = true; } } @@ -188,11 +215,12 @@ internal static ResGenDependencies DeserializeCache(string stateFile, bool useSo /// /// This is an on-disk serialization format, don't change field names or types or use readonly. /// + /// Serializable should be included in all state files. It permits BinaryFormatter-based calls, including from GenerateResource, which we cannot move off BinaryFormatter. [Serializable] - internal sealed class ResXFile : DependencyFile + internal sealed class ResXFile : DependencyFile, ITranslatable { // Files contained within this resx file. - private string[] linkedFiles; + internal string[] linkedFiles; internal string[] LinkedFiles => linkedFiles; @@ -209,6 +237,18 @@ internal ResXFile(string filename, string baseLinkedFileDirectory, bool useMSBui } } + internal ResXFile() + { + } + + public void Translate(ITranslator translator) + { + translator.Translate(ref linkedFiles); + translator.Translate(ref filename); + translator.Translate(ref lastModified); + translator.Translate(ref exists); + } + /// /// Given a .RESX file, returns all the linked files that are referenced within that .RESX. /// @@ -281,12 +321,27 @@ private static string[] GetLinkedFiles(string filename, string baseLinkedFileDir /// /// This is an on-disk serialization format, don't change field names or types or use readonly. /// + /// Serializable should be included in all state files. It permits BinaryFormatter-based calls, including from GenerateResource, which we cannot move off BinaryFormatter. [Serializable] - internal sealed class PortableLibraryFile : DependencyFile + internal sealed class PortableLibraryFile : DependencyFile, ITranslatable { - private string[] outputFiles; - private string neutralResourceLanguage; - private string assemblySimpleName; + internal string[] outputFiles; + internal string neutralResourceLanguage; + internal string assemblySimpleName; + + internal PortableLibraryFile() + { + } + + public void Translate(ITranslator translator) + { + translator.Translate(ref assemblySimpleName); + translator.Translate(ref outputFiles); + translator.Translate(ref neutralResourceLanguage); + translator.Translate(ref filename); + translator.Translate(ref lastModified); + translator.Translate(ref exists); + } internal PortableLibraryFile(string filename) : base(filename) diff --git a/src/Tasks/ResolveComReferenceCache.cs b/src/Tasks/ResolveComReferenceCache.cs index d0f80dca18f..133acb02f6f 100644 --- a/src/Tasks/ResolveComReferenceCache.cs +++ b/src/Tasks/ResolveComReferenceCache.cs @@ -2,7 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Collections; +using System.Collections.Generic; +using Microsoft.Build.BackEnd; using Microsoft.Build.Shared; namespace Microsoft.Build.Tasks @@ -16,17 +17,18 @@ namespace Microsoft.Build.Tasks /// /// This is an on-disk serialization format, don't change field names or types or use readonly. /// + /// Serializable should be included in all state files. It permits BinaryFormatter-based calls, including from GenerateResource, which we cannot move off BinaryFormatter. [Serializable] - internal sealed class ResolveComReferenceCache : StateFileBase + internal sealed class ResolveComReferenceCache : StateFileBase, ITranslatable { /// /// Component timestamps. /// Key: Component path on disk /// Value: DateTime struct /// - private Hashtable componentTimestamps; - private string tlbImpLocation; - private string axImpLocation; + internal Dictionary componentTimestamps; + internal string tlbImpLocation; + internal string axImpLocation; /// /// indicates whether the cache contents have changed since it's been created @@ -46,7 +48,7 @@ internal ResolveComReferenceCache(string tlbImpPath, string axImpPath) tlbImpLocation = tlbImpPath; axImpLocation = axImpPath; - componentTimestamps = new Hashtable(); + componentTimestamps = new(); } /// @@ -69,9 +71,9 @@ internal DateTime this[string componentPath] { get { - if (componentTimestamps.ContainsKey(componentPath)) + if (componentTimestamps.TryGetValue(componentPath, out DateTime time)) { - return (DateTime)componentTimestamps[componentPath]; + return time; } // If the entry is not present in the cache, return the current time. Since no component should be timestamped @@ -81,12 +83,24 @@ internal DateTime this[string componentPath] set { // only set the value and dirty the cache if the timestamp doesn't exist yet or is different than the current one - if (DateTime.Compare(this[componentPath], value) != 0) + if (!DateTime.Equals(this[componentPath], value)) { componentTimestamps[componentPath] = value; _dirty = true; } } } + + public ResolveComReferenceCache(ITranslator translator) + { + Translate(translator); + } + + public override void Translate(ITranslator translator) + { + translator.Translate(ref axImpLocation); + translator.Translate(ref tlbImpLocation); + translator.TranslateDictionary(ref componentTimestamps, StringComparer.Ordinal); + } } } diff --git a/src/Tasks/ResolveManifestFiles.cs b/src/Tasks/ResolveManifestFiles.cs index ee59ffb9b5e..48877a31c2f 100644 --- a/src/Tasks/ResolveManifestFiles.cs +++ b/src/Tasks/ResolveManifestFiles.cs @@ -52,6 +52,8 @@ public sealed class ResolveManifestFiles : TaskExtension // if signing manifests is on and not all app files are included, then the project can't be published. private bool _canPublish; private Dictionary _runtimePackAssets; + // map of satellite assemblies that are included in References + private SatelliteRefAssemblyMap _satelliteAssembliesPassedAsReferences = new SatelliteRefAssemblyMap(); #endregion #region Properties @@ -380,6 +382,28 @@ private void GetOutputAssemblies(List publishInfos, List { if (!IsFiltered(item)) { + // ClickOnce for .NET 4.X should not publish duplicate satellite assemblies. + // This will cause ClickOnce install to fail. This can happen if some package + // decides to publish the en-us resource assemblies for other locales also. + if (!LauncherBasedDeployment && _satelliteAssembliesPassedAsReferences.ContainsItem(item)) + { + continue; + } + + // Apply the culture publishing rules to include or exclude satellite assemblies + AssemblyIdentity identity = AssemblyIdentity.FromManagedAssembly(item.ItemSpec); + if (identity != null && !String.Equals(identity.Culture, "neutral", StringComparison.Ordinal)) + { + CultureInfo satelliteCulture = GetItemCulture(item); + if (PublishFlags.IsSatelliteIncludedByDefault(satelliteCulture, _targetCulture, _includeAllSatellites)) + { + _satelliteAssembliesPassedAsReferences.Add(item); + } + else + { + continue; + } + } item.SetMetadata("AssemblyType", "Managed"); assemblyMap.Add(item); } @@ -574,6 +598,10 @@ private void GetOutputSatellites(List publishInfos, List foreach (ITaskItem item in _satelliteAssemblies) { item.SetMetadata("AssemblyType", "Satellite"); + if (_satelliteAssembliesPassedAsReferences.ContainsItem(item)) + { + continue; + } satelliteMap.Add(item, true); } } @@ -855,6 +883,54 @@ IEnumerator IEnumerable.GetEnumerator() } #endregion + #region SatelliteRefAssemblyMap + private class SatelliteRefAssemblyMap : IEnumerable + { + private readonly Dictionary _dictionary = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + + public MapEntry this[string fusionName] + { + get + { + _dictionary.TryGetValue(fusionName, out MapEntry entry); + return entry; + } + } + + public bool ContainsItem(ITaskItem item) + { + AssemblyIdentity identity = AssemblyIdentity.FromManagedAssembly(item.ItemSpec); + if (identity != null) + { + return _dictionary.ContainsKey(identity.ToString()); + } + return false; + } + + public void Add(ITaskItem item) + { + var entry = new MapEntry(item, true); + AssemblyIdentity identity = AssemblyIdentity.FromManagedAssembly(item.ItemSpec); + if (identity != null && !String.Equals(identity.Culture, "neutral", StringComparison.Ordinal)) + { + // Use satellite assembly strong name signature as key + string key = identity.ToString(); + Debug.Assert(!_dictionary.ContainsKey(key), String.Format(CultureInfo.CurrentCulture, "Two or more items with same key '{0}' detected", key)); + if (!_dictionary.ContainsKey(key)) + { + _dictionary.Add(key, entry); + } + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _dictionary.Values.GetEnumerator(); + } + } + #endregion + + #region FileMap private class FileMap : IEnumerable { @@ -1036,7 +1112,7 @@ public static PublishFlags GetSatelliteFlags(PublishState state, CultureInfo sat public bool IsPublished { get; } - private static bool IsSatelliteIncludedByDefault(CultureInfo satelliteCulture, CultureInfo targetCulture, bool includeAllSatellites) + public static bool IsSatelliteIncludedByDefault(CultureInfo satelliteCulture, CultureInfo targetCulture, bool includeAllSatellites) { // If target culture not specified then satellite is not included by default... if (targetCulture == null) diff --git a/src/Tasks/ResolveProjectBase.cs b/src/Tasks/ResolveProjectBase.cs index 334b17f17d9..e3c7c7770be 100644 --- a/src/Tasks/ResolveProjectBase.cs +++ b/src/Tasks/ResolveProjectBase.cs @@ -121,6 +121,7 @@ internal bool VerifyProjectReferenceItems(ITaskItem[] references, bool treatAsEr /// internal void CacheProjectElementsFromXml(string xmlString) { + // TODO: fix code clone for parsing CurrentSolutionConfiguration xml: https://github.com/dotnet/msbuild/issues/6751 XmlDocument doc = null; if (!string.IsNullOrEmpty(xmlString)) diff --git a/src/Tasks/Resources/Strings.resx b/src/Tasks/Resources/Strings.resx index 48b0bfb05a3..45b52e509f0 100644 --- a/src/Tasks/Resources/Strings.resx +++ b/src/Tasks/Resources/Strings.resx @@ -2893,6 +2893,37 @@ MSB3965: No output path specified in build settings. {StrBegin="MSB3965: "} + + + + MSB3981: Could not determine what '{0}' should be built as. The project will be built without the Platform property set. + {StrBegin="MSB3981: "} + + + MSB3982: EnableDynamicPlatformResolution is true but referenced project '{0}' has no 'Platforms' metadata set. It will be built without a specified platform. + {StrBegin="MSB3982: "} + + + MSB3983: The PlatformLookupTable '{0}' is in an invalid format and won't be used. The format should be 'A=B;C=D'. + {StrBegin="MSB3983: "} + + + Parsed lookup table:'{0}'. + + + Found mapping '{0}'='{1}' in given lookup table: '{2}'. + + + Project '{0}' will build with platform: '{1}'. + + + ProjectReference and current project have the same platform. + + + Choosing AnyCPU by default. +