diff --git a/src/Cli/dotnet/Commands/Run/CSharpCompilerCommand.Generated.cs b/src/Cli/dotnet/Commands/Run/CSharpCompilerCommand.Generated.cs index 30bbf04e8a33..b9c28de8c132 100644 --- a/src/Cli/dotnet/Commands/Run/CSharpCompilerCommand.Generated.cs +++ b/src/Cli/dotnet/Commands/Run/CSharpCompilerCommand.Generated.cs @@ -1,13 +1,14 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text.Json; + namespace Microsoft.DotNet.Cli.Commands.Run; // Generated by test `RunFileTests.CscArguments`. partial class CSharpCompilerCommand { private IEnumerable GetCscArguments( - string fileNameWithoutExtension, string objDir, string binDir) { @@ -29,14 +30,14 @@ .. GetFrameworkReferenceArguments(), "/debug:portable", "/filealign:512", "/optimize-", - $"/out:{binDir}/{fileNameWithoutExtension}.dll", + $"/out:{binDir}/{FileNameWithoutExtension}.dll", "/target:exe", "/warnaserror-", "/utf8output", "/deterministic+", "/langversion:14.0", "/features:FileBasedProgram", - $"/analyzerconfig:{objDir}/{fileNameWithoutExtension}.GeneratedMSBuildEditorConfig.editorconfig", + $"/analyzerconfig:{objDir}/{FileNameWithoutExtension}.GeneratedMSBuildEditorConfig.editorconfig", $"/analyzerconfig:{SdkPath}/Sdks/Microsoft.NET.Sdk/analyzers/build/config/analysislevel_10_default.globalconfig", $"/analyzer:{SdkPath}/Sdks/Microsoft.NET.Sdk/targets/../analyzers/Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll", $"/analyzer:{SdkPath}/Sdks/Microsoft.NET.Sdk/targets/../analyzers/Microsoft.CodeAnalysis.NetAnalyzers.dll", @@ -44,9 +45,9 @@ .. GetFrameworkReferenceArguments(), $"/analyzer:{NuGetCachePath}/microsoft.net.illink.tasks/{RuntimeVersion}/analyzers/dotnet/cs/ILLink.RoslynAnalyzer.dll", .. GetFrameworkAnalyzerArguments(), $"{EntryPointFileFullPath}", - $"{objDir}/{fileNameWithoutExtension}.GlobalUsings.g.cs", + $"{objDir}/{FileNameWithoutExtension}.GlobalUsings.g.cs", $"{objDir}/.NETCoreApp,Version=v10.0.AssemblyAttributes.cs", - $"{objDir}/{fileNameWithoutExtension}.AssemblyInfo.cs", + $"{objDir}/{FileNameWithoutExtension}.AssemblyInfo.cs", "/warnaserror+:NU1605,SYSLIB0011", ]; } @@ -62,4 +63,131 @@ public static IEnumerable GetPathsOfCscInputsFromNuGetCache() $"{NuGetCachePath}/microsoft.net.illink.tasks/{RuntimeVersion}/analyzers/dotnet/cs/ILLink.RoslynAnalyzer.dll", ]; } + + private string GetRuntimeConfigContent() + { + return $$""" +{ + "runtimeOptions": { + "tfm": {{JsonSerializer.Serialize(TargetFramework)}}, + "framework": { + "name": "Microsoft.NETCore.App", + "version": {{JsonSerializer.Serialize(DefaultRuntimeVersion)}} + }, + "configProperties": { + "EntryPointFilePath": {{JsonSerializer.Serialize(EntryPointFileFullPath)}}, + "EntryPointFileDirectoryPath": {{JsonSerializer.Serialize(BaseDirectory)}}, + "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, + "System.ComponentModel.DefaultValueAttribute.IsSupported": false, + "System.ComponentModel.Design.IDesignerHost.IsSupported": false, + "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, + "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, + "System.Data.DataSet.XmlSerializationIsSupported": false, + "System.Diagnostics.Tracing.EventSource.IsSupported": false, + "System.Linq.Enumerable.IsSizeOptimized": true, + "System.Net.SocketsHttpHandler.Http3Support": false, + "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, + "System.Resources.ResourceManager.AllowCustomResourceTypes": false, + "System.Resources.UseSystemResourceKeys": false, + "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, + "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, + "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, + "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, + "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, + "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, + "System.StartupHookProvider.IsSupported": false, + "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, + "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, + "System.Threading.Thread.EnableAutoreleasePool": false, + "System.Linq.Expressions.CanEmitObjectArrayDelegate": false + } + } +} +"""; + } + + private string GetAssemblyAttributesContent() + { + return $""" +// +using System; +using System.Reflection; +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v{TargetFrameworkVersion}", FrameworkDisplayName = ".NET {TargetFrameworkVersion}")] + +"""; + } + + private string GetAssemblyInfoContent() + { + return $""" +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Reflection; + +[assembly: System.Reflection.AssemblyCompanyAttribute("{FileNameWithoutExtension}")] +[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] +[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] +[assembly: System.Reflection.AssemblyProductAttribute("{FileNameWithoutExtension}")] +[assembly: System.Reflection.AssemblyTitleAttribute("{FileNameWithoutExtension}")] +[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] + +// Generated by the MSBuild WriteCodeFragment class. + + +"""; + } + + private string GetGeneratedMSBuildEditorConfigContent() + { + return $""" +is_global = true +build_property.EnableAotAnalyzer = true +build_property.EnableSingleFileAnalyzer = true +build_property.EnableTrimAnalyzer = true +build_property.IncludeAllContentForSelfExtract = +build_property.VerifyReferenceTrimCompatibility = +build_property.VerifyReferenceAotCompatibility = +build_property.TargetFramework = net{TargetFrameworkVersion} +build_property.TargetFrameworkIdentifier = .NETCoreApp +build_property.TargetFrameworkVersion = v{TargetFrameworkVersion} +build_property.TargetPlatformMinVersion = +build_property.UsingMicrosoftNETSdkWeb = +build_property.ProjectTypeGuids = +build_property.InvariantGlobalization = +build_property.PlatformNeutralAssembly = +build_property.EnforceExtendedAnalyzerRules = +build_property._SupportedPlatformList = Linux,macOS,Windows +build_property.RootNamespace = {FileNameWithoutExtension} +build_property.ProjectDir = {BaseDirectoryWithTrailingSeparator} +build_property.EnableComHosting = +build_property.EnableGeneratedComInterfaceComImportInterop = false +build_property.EffectiveAnalysisLevelStyle = {TargetFrameworkVersion} +build_property.EnableCodeStyleSeverity = + +"""; + } + + private string GetGlobalUsingsContent() + { + return """ +// +global using System; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Net.Http; +global using System.Threading; +global using System.Threading.Tasks; + +"""; + } } diff --git a/src/Cli/dotnet/Commands/Run/CSharpCompilerCommand.cs b/src/Cli/dotnet/Commands/Run/CSharpCompilerCommand.cs index fba2892a05f5..73150b149e39 100644 --- a/src/Cli/dotnet/Commands/Run/CSharpCompilerCommand.cs +++ b/src/Cli/dotnet/Commands/Run/CSharpCompilerCommand.cs @@ -45,14 +45,17 @@ internal sealed partial class CSharpCompilerCommand private static string ClientDirectory => field ??= Path.Combine(SdkPath, "Roslyn", "bincore"); private static string NuGetCachePath => field ??= SettingsUtility.GetGlobalPackagesFolder(Settings.LoadDefaultSettings(null)); internal static string RuntimeVersion => field ??= RuntimeInformation.FrameworkDescription.Split(' ').Last(); - private static string DefaultRuntimeVersion => field ??= GetDefaultRuntimeVersion(); - private static string TargetFrameworkVersion => Product.TargetFrameworkVersion; + internal static string DefaultRuntimeVersion => field ??= GetDefaultRuntimeVersion(); + internal static string TargetFrameworkVersion => Product.TargetFrameworkVersion; + internal static string TargetFramework => field ??= $"net{TargetFrameworkVersion}"; public required string EntryPointFileFullPath { get; init; } public required string ArtifactsPath { get; init; } public required bool CanReuseAuxiliaryFiles { get; init; } public string BaseDirectory => field ??= Path.GetDirectoryName(EntryPointFileFullPath)!; + internal string BaseDirectoryWithTrailingSeparator => field ??= BaseDirectory + Path.DirectorySeparatorChar; + internal string FileNameWithoutExtension => field ??= Path.GetFileNameWithoutExtension(EntryPointFileFullPath); /// /// Compiler command line arguments to use. If empty, default arguments are used. @@ -209,9 +212,6 @@ private void PrepareAuxiliaryFiles(out string rspPath) return; } - string fileDirectory = Path.GetDirectoryName(EntryPointFileFullPath) ?? string.Empty; - string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(EntryPointFileFullPath); - // Note that Release builds won't go through this optimized code path because `-c Release` translates to global property `Configuration=Release` // and customizing global properties triggers a full MSBuild run. string objDir = Path.Join(ArtifactsPath, "obj", "debug"); @@ -222,93 +222,28 @@ private void PrepareAuxiliaryFiles(out string rspPath) string assemblyAttributes = Path.Join(objDir, $".NETCoreApp,Version=v{TargetFrameworkVersion}.AssemblyAttributes.cs"); if (ShouldEmit(assemblyAttributes)) { - File.WriteAllText(assemblyAttributes, /* lang=C#-test */ $""" - // - using System; - using System.Reflection; - [assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v{TargetFrameworkVersion}", FrameworkDisplayName = ".NET {TargetFrameworkVersion}")] - - """); + File.WriteAllText(assemblyAttributes, GetAssemblyAttributesContent()); } - string globalUsings = Path.Join(objDir, $"{fileNameWithoutExtension}.GlobalUsings.g.cs"); + string globalUsings = Path.Join(objDir, $"{FileNameWithoutExtension}.GlobalUsings.g.cs"); if (ShouldEmit(globalUsings)) { - File.WriteAllText(globalUsings, /* lang=C#-test */ """ - // - global using System; - global using System.Collections.Generic; - global using System.IO; - global using System.Linq; - global using System.Net.Http; - global using System.Threading; - global using System.Threading.Tasks; - - """); + File.WriteAllText(globalUsings, GetGlobalUsingsContent()); } - string assemblyInfo = Path.Join(objDir, $"{fileNameWithoutExtension}.AssemblyInfo.cs"); + string assemblyInfo = Path.Join(objDir, $"{FileNameWithoutExtension}.AssemblyInfo.cs"); if (ShouldEmit(assemblyInfo)) { - File.WriteAllText(assemblyInfo, /* lang=C#-test */ $""" - //------------------------------------------------------------------------------ - // - // This code was generated by a tool. - // - // Changes to this file may cause incorrect behavior and will be lost if - // the code is regenerated. - // - //------------------------------------------------------------------------------ - - using System; - using System.Reflection; - - [assembly: System.Reflection.AssemblyCompanyAttribute("{fileNameWithoutExtension}")] - [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] - [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] - [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")] - [assembly: System.Reflection.AssemblyProductAttribute("{fileNameWithoutExtension}")] - [assembly: System.Reflection.AssemblyTitleAttribute("{fileNameWithoutExtension}")] - [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] - - // Generated by the MSBuild WriteCodeFragment class. - - - """); + File.WriteAllText(assemblyInfo, GetAssemblyInfoContent()); } - string editorconfig = Path.Join(objDir, $"{fileNameWithoutExtension}.GeneratedMSBuildEditorConfig.editorconfig"); + string editorconfig = Path.Join(objDir, $"{FileNameWithoutExtension}.GeneratedMSBuildEditorConfig.editorconfig"); if (ShouldEmit(editorconfig)) { - File.WriteAllText(editorconfig, $""" - is_global = true - build_property.EnableAotAnalyzer = true - build_property.EnableSingleFileAnalyzer = true - build_property.EnableTrimAnalyzer = true - build_property.IncludeAllContentForSelfExtract = - build_property.VerifyReferenceTrimCompatibility = - build_property.VerifyReferenceAotCompatibility = - build_property.TargetFramework = net{TargetFrameworkVersion} - build_property.TargetFrameworkIdentifier = .NETCoreApp - build_property.TargetFrameworkVersion = v{TargetFrameworkVersion} - build_property.TargetPlatformMinVersion = - build_property.UsingMicrosoftNETSdkWeb = - build_property.ProjectTypeGuids = - build_property.InvariantGlobalization = - build_property.PlatformNeutralAssembly = - build_property.EnforceExtendedAnalyzerRules = - build_property._SupportedPlatformList = Linux,macOS,Windows - build_property.RootNamespace = {fileNameWithoutExtension} - build_property.ProjectDir = {fileDirectory}{Path.DirectorySeparatorChar} - build_property.EnableComHosting = - build_property.EnableGeneratedComInterfaceComImportInterop = false - build_property.EffectiveAnalysisLevelStyle = {TargetFrameworkVersion} - build_property.EnableCodeStyleSeverity = - - """); + File.WriteAllText(editorconfig, GetGeneratedMSBuildEditorConfigContent()); } - var apphostTarget = Path.Join(binDir, $"{fileNameWithoutExtension}{FileNameSuffixes.CurrentPlatform.Exe}"); + var apphostTarget = Path.Join(binDir, $"{FileNameWithoutExtension}{FileNameSuffixes.CurrentPlatform.Exe}"); if (ShouldEmit(apphostTarget)) { var rid = RuntimeInformation.RuntimeIdentifier; @@ -316,57 +251,19 @@ private void PrepareAuxiliaryFiles(out string rspPath) HostWriter.CreateAppHost( appHostSourceFilePath: apphostSource, appHostDestinationFilePath: apphostTarget, - appBinaryFilePath: $"{fileNameWithoutExtension}.dll", + appBinaryFilePath: $"{FileNameWithoutExtension}.dll", enableMacOSCodeSign: OperatingSystem.IsMacOS()); } - var runtimeConfig = Path.Join(binDir, $"{fileNameWithoutExtension}{FileNameSuffixes.RuntimeConfigJson}"); + var runtimeConfig = Path.Join(binDir, $"{FileNameWithoutExtension}{FileNameSuffixes.RuntimeConfigJson}"); if (ShouldEmit(runtimeConfig)) { - File.WriteAllText(runtimeConfig, $$""" - { - "runtimeOptions": { - "tfm": "net{{TargetFrameworkVersion}}", - "framework": { - "name": "Microsoft.NETCore.App", - "version": {{JsonSerializer.Serialize(DefaultRuntimeVersion)}} - }, - "configProperties": { - "EntryPointFilePath": {{JsonSerializer.Serialize(EntryPointFileFullPath)}}, - "EntryPointFileDirectoryPath": {{JsonSerializer.Serialize(fileDirectory)}}, - "Microsoft.Extensions.DependencyInjection.VerifyOpenGenericServiceTrimmability": true, - "System.ComponentModel.DefaultValueAttribute.IsSupported": false, - "System.ComponentModel.Design.IDesignerHost.IsSupported": false, - "System.ComponentModel.TypeConverter.EnableUnsafeBinaryFormatterInDesigntimeLicenseContextSerialization": false, - "System.ComponentModel.TypeDescriptor.IsComObjectDescriptorSupported": false, - "System.Data.DataSet.XmlSerializationIsSupported": false, - "System.Diagnostics.Tracing.EventSource.IsSupported": false, - "System.Linq.Enumerable.IsSizeOptimized": true, - "System.Net.SocketsHttpHandler.Http3Support": false, - "System.Reflection.Metadata.MetadataUpdater.IsSupported": false, - "System.Resources.ResourceManager.AllowCustomResourceTypes": false, - "System.Resources.UseSystemResourceKeys": false, - "System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported": false, - "System.Runtime.InteropServices.BuiltInComInterop.IsSupported": false, - "System.Runtime.InteropServices.EnableConsumingManagedCodeFromNativeHosting": false, - "System.Runtime.InteropServices.EnableCppCLIHostActivation": false, - "System.Runtime.InteropServices.Marshalling.EnableGeneratedComInterfaceComImportInterop": false, - "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false, - "System.StartupHookProvider.IsSupported": false, - "System.Text.Encoding.EnableUnsafeUTF7Encoding": false, - "System.Text.Json.JsonSerializer.IsReflectionEnabledByDefault": false, - "System.Threading.Thread.EnableAutoreleasePool": false, - "System.Linq.Expressions.CanEmitObjectArrayDelegate": false - } - } - } - """); + File.WriteAllText(runtimeConfig, GetRuntimeConfigContent()); } if (ShouldEmit(rspPath)) { IEnumerable args = GetCscArguments( - fileNameWithoutExtension: fileNameWithoutExtension, objDir: objDir, binDir: binDir); diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index 61e8f00b7524..1dc2a08953ed 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -3961,13 +3961,14 @@ public void CscArguments() // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. + using System.Text.Json; + namespace Microsoft.DotNet.Cli.Commands.Run; // Generated by test `{{nameof(RunFileTests)}}.{{nameof(CscArguments)}}`. partial class CSharpCompilerCommand { private IEnumerable GetCscArguments( - string fileNameWithoutExtension, string objDir, string binDir) { @@ -4059,7 +4060,7 @@ private IEnumerable GetCscArguments( // Use variable program name. if (rewritten.Contains(programName, StringComparison.OrdinalIgnoreCase)) { - rewritten = rewritten.Replace(programName, "{fileNameWithoutExtension}", StringComparison.OrdinalIgnoreCase); + rewritten = rewritten.Replace(programName, "{FileNameWithoutExtension}", StringComparison.OrdinalIgnoreCase); needsInterpolation = true; } @@ -4143,6 +4144,70 @@ public static IEnumerable GetPathsOfCscInputsFromNuGetCache() code.AppendLine(""" ]; } + """); + + // Generate file content templates. + var baseDirectory = TestPathUtility.ResolveTempPrefixLink(Path.GetDirectoryName(entryPointPath)!); + var replacements = new List<(string, string)> + { + (TestPathUtility.ResolveTempPrefixLink(entryPointPath), nameof(CSharpCompilerCommand.EntryPointFileFullPath)), + (baseDirectory + Path.DirectorySeparatorChar, nameof(CSharpCompilerCommand.BaseDirectoryWithTrailingSeparator)), + (baseDirectory, nameof(CSharpCompilerCommand.BaseDirectory)), + (programName, nameof(CSharpCompilerCommand.FileNameWithoutExtension)), + (CSharpCompilerCommand.TargetFrameworkVersion, nameof(CSharpCompilerCommand.TargetFrameworkVersion)), + (CSharpCompilerCommand.TargetFramework, nameof(CSharpCompilerCommand.TargetFramework)), + (CSharpCompilerCommand.DefaultRuntimeVersion, nameof(CSharpCompilerCommand.DefaultRuntimeVersion)), + }; + var emittedFiles = Directory.EnumerateFiles(artifactsDir, "*", SearchOption.AllDirectories).Order(); + foreach (var emittedFile in emittedFiles) + { + var emittedFileName = Path.GetFileName(emittedFile); + var generatedMethodName = GetGeneratedMethodName(emittedFileName); + if (generatedMethodName is null) + { + Log.WriteLine($"Skipping unrecognized file '{emittedFile}'."); + continue; + } + + var emittedFileContent = File.ReadAllText(emittedFile); + + string interpolatedString = emittedFileContent; + string interpolationPrefix; + + if (emittedFileName.EndsWith(".json", StringComparison.Ordinal)) + { + interpolationPrefix = "$$"; + foreach (var (key, value) in replacements) + { + interpolatedString = interpolatedString.Replace(JsonSerializer.Serialize(key), "{{JsonSerializer.Serialize(" + value + ")}}"); + } + } + else + { + interpolationPrefix = "$"; + foreach (var (key, value) in replacements) + { + interpolatedString = interpolatedString.Replace(key, "{" + value + "}"); + } + } + + if (interpolatedString == emittedFileContent) + { + interpolationPrefix = ""; + } + + code.AppendLine($$"""" + + private string Get{{generatedMethodName}}Content() + { + return {{interpolationPrefix}}""" + {{interpolatedString}} + """; + } + """"); + } + + code.AppendLine(""" } """); @@ -4242,6 +4307,19 @@ static string RemoveQuotes(string arg) { return arg.Replace("\"", string.Empty); } + + static string? GetGeneratedMethodName(string fileName) + { + return fileName switch + { + $".NETCoreApp,Version=v{ToolsetInfo.CurrentTargetFrameworkVersion}.AssemblyAttributes.cs" => "AssemblyAttributes", + $"{programName}.GlobalUsings.g.cs" => "GlobalUsings", + $"{programName}.AssemblyInfo.cs" => "AssemblyInfo", + $"{programName}.GeneratedMSBuildEditorConfig.editorconfig" => "GeneratedMSBuildEditorConfig", + $"{programName}{FileNameSuffixes.RuntimeConfigJson}" => "RuntimeConfig", + _ => null, + }; + } } /// @@ -4309,7 +4387,7 @@ Hello from {programName} var msbuildFileText = File.ReadAllText(msbuildFile); if (cscOnlyFileText.ReplaceLineEndings() != msbuildFileText.ReplaceLineEndings()) { - Log.WriteLine($"File differs between MSBuild and CSC-only runs (if this is expected, find the template in '{nameof(CSharpCompilerCommand)}.cs' and update it): {cscOnlyFile}"); + Log.WriteLine($"File differs between MSBuild and CSC-only runs (if this is expected, run test '{nameof(CscArguments)}' locally to re-generate the template): {cscOnlyFile}"); const int limit = 3_000; if (cscOnlyFileText.Length < limit && msbuildFileText.Length < limit) {