Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Push context all the way to csc resolution #198

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
and loads first, thereby breaking loading of MSBuild assemblies. Force-upgrade it.
-->
<PackageReference Include="NuGet.Frameworks" Version="6.9.1" />
<PackageReference Include="System.Text.Json" Version="7.0.3" />
<PackageReference Include="System.Text.Json" Version="8.0.5" />
</ItemGroup>

<ItemGroup>
Expand Down
16 changes: 16 additions & 0 deletions Mono.TextTemplating.Build/BuildSpecificCodeCompilationContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Mono.TextTemplating.CodeCompilation;

namespace Mono.TextTemplating.Build
{
public sealed class BuildSpecificCodeCompilationContext : ICodeCompilationContext
{

readonly string _compilerSearchPath;
public BuildSpecificCodeCompilationContext (string compilerSearchPath)
{
_compilerSearchPath = compilerSearchPath;
}

public string CompilerSearchPath => _compilerSearchPath;
}
}
2 changes: 1 addition & 1 deletion Mono.TextTemplating.Build/Mono.TextTemplating.Build.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<None Include="readme.md" Pack="true" PackagePath="\" />
<ProjectReference Include="..\Mono.TextTemplating\Mono.TextTemplating.csproj" PrivateAssets="all" />
<PackageReference Include="MessagePackAnalyzer" Version="2.5.129" PrivateAssets="all" />
<PackageReference Include="MessagePack" Version="2.5.129" PrivateAssets="all" />
<PackageReference Include="MessagePack" Version="2.5.192" PrivateAssets="all" />
<!-- intentionally downlevel these so they can be loaded in older VS versions -->
<PackageReference Include="Microsoft.Build.Framework" Version="17.0.0" PrivateAssets="all" IncludeAssets="compile" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.0.0" PrivateAssets="all" IncludeAssets="compile" />
Expand Down
1 change: 1 addition & 0 deletions Mono.TextTemplating.Build/T4.BuildTools.targets
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
IncludePaths="@(T4IncludePath)"
ParameterValues="@(T4Argument)"
ReferencePaths="@(T4ReferencePath)"
RoslynTargetsPath="$(RoslynTargetsPath)"
TransformTemplates="@(T4Transform)"
PreprocessTemplates="@(T4Preprocess)"
AssemblyReferences="@(T4AssemblyReference)"
Expand Down
10 changes: 9 additions & 1 deletion Mono.TextTemplating.Build/TextTransform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Mono.TextTemplating.CodeCompilation;

namespace Mono.TextTemplating.Build
{
Expand Down Expand Up @@ -37,6 +38,11 @@ public TextTransform () : base (Messages.ResourceManager) { }

public string PreprocessTargetRuntimeIdentifier { get; set; }

/// <summary>
/// The path to roslyn which will be used for this build.
/// </summary>
public string RoslynTargetsPath { get; set; }

[Required]
public string IntermediateDirectory { get; set; }

Expand Down Expand Up @@ -130,7 +136,9 @@ public override bool Execute ()
}
}

TextTransformProcessor.Process (Log, previousBuildState, buildState, PreprocessOnly);
ICodeCompilationContext context = RoslynTargetsPath != null ? new BuildSpecificCodeCompilationContext(RoslynTargetsPath) : new DefaultCodeCompilationContext ();

TextTransformProcessor.Process (Log, previousBuildState, buildState, PreprocessOnly, context);

if (buildState.TransformTemplates != null) {
TransformTemplateOutput = new ITaskItem[buildState.TransformTemplates.Count];
Expand Down
5 changes: 3 additions & 2 deletions Mono.TextTemplating.Build/TextTransformProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@

using Microsoft.Build.Utilities;
using Microsoft.VisualStudio.TextTemplating;
using Mono.TextTemplating.CodeCompilation;

namespace Mono.TextTemplating.Build
{
static class TextTransformProcessor
{
public static bool Process (TaskLoggingHelper taskLog, TemplateBuildState previousBuildState, TemplateBuildState buildState, bool preprocessOnly)
public static bool Process (TaskLoggingHelper taskLog, TemplateBuildState previousBuildState, TemplateBuildState buildState, bool preprocessOnly, ICodeCompilationContext context)
{
(var transforms, var preprocessed) = buildState.GetStaleAndNewTemplates (previousBuildState, preprocessOnly, new WriteTimeCache ().GetWriteTime, taskLog);

Expand Down Expand Up @@ -50,7 +51,7 @@ public static bool Process (TaskLoggingHelper taskLog, TemplateBuildState previo
}

string outputContent;
(outputFile, outputContent) = generator.ProcessTemplateAsync (pt, inputFile, inputContent, outputFile, settings).Result;
(outputFile, outputContent) = generator.ProcessTemplateAsync (pt, inputFile, inputContent, outputFile, settings, context).Result;

if (generator.Errors.HasErrors) {
return;
Expand Down
4 changes: 2 additions & 2 deletions Mono.TextTemplating.Tests/ProcessingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
using System.IO;
using System.Linq;
using System.Threading.Tasks;

using Mono.TextTemplating.CodeCompilation;
using Xunit;

namespace Mono.TextTemplating.Tests
Expand Down Expand Up @@ -150,7 +150,7 @@ public async Task SetOutputExtension ()
var gen = new TemplateGenerator ();
var pt = gen.ParseTemplate (inputFile, inputContent);
TemplateSettings settings = TemplatingEngine.GetSettings (gen, pt);
(outputName, _) = await gen.ProcessTemplateAsync (pt, inputFile, inputContent, outputName, settings);
(outputName, _) = await gen.ProcessTemplateAsync (pt, inputFile, inputContent, outputName, settings, new DefaultCodeCompilationContext());

Assert.Equal ("hello.cfg", outputName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,37 @@ public static bool IsLangVersionArg (string arg) =>
return $"-langversion:{ToString (runtime.RuntimeLangVersion)}";
}

public static CSharpLangVersion ParseLangVersionOutput(string stdOut){
// first remove any lines which are not numbers, and handle the default of something similar to "13.0 (default)"
var lines = stdOut.Split(new[] { Environment.NewLine, " " }, StringSplitOptions.RemoveEmptyEntries);
// now find any numbers remaining - these are the versions and 1-5 are ints and greater than 6.0 are floats
var version = lines.Where(l => float.TryParse(l, out _))
.Select(float.Parse)
.ToArray();

// if we have any numbers, return the highest one
if(version.Length > 0){
return version.Max() switch {
1 => CSharpLangVersion.v5_0,
2 => CSharpLangVersion.v6_0,
3 => CSharpLangVersion.v7_0,
4 => CSharpLangVersion.v7_1,
5 => CSharpLangVersion.v7_2,
6 => CSharpLangVersion.v7_3,
7 => CSharpLangVersion.v8_0,
8 => CSharpLangVersion.v9_0,
9 => CSharpLangVersion.v10_0,
10 => CSharpLangVersion.v11_0,
11 => CSharpLangVersion.v12_0,
12 => CSharpLangVersion.v13_0,
_ => CSharpLangVersion.Latest
};
}

// we didn't find any numbers, so return the latest version
return CSharpLangVersion.Latest;
}

//https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history
public static CSharpLangVersion FromNetCoreSdkVersion (SemVersion sdkVersion)
=> sdkVersion switch {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System;
using System.IO;

namespace Mono.TextTemplating.CodeCompilation
{
public class DefaultCodeCompilationContext : ICodeCompilationContext
{
public string CompilerSearchPath { get; } = Path.GetDirectoryName (typeof (object).Assembly.Location);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace Mono.TextTemplating.CodeCompilation
{
public interface ICodeCompilationContext
{
/// <summary>
/// The directory to search for the compiler in.
/// </summary>
string CompilerSearchPath { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,17 @@ class RuntimeInfo
public string RefAssembliesDir { get; }
public string RuntimeFacadesDir { get; }

public static RuntimeInfo GetRuntime ()
public static RuntimeInfo GetRuntime () => GetRuntime (new DefaultCodeCompilationContext ());

public static RuntimeInfo GetRuntime (ICodeCompilationContext context)
{
if (Type.GetType ("Mono.Runtime") != null)
{
return GetMonoRuntime ();
}
else if (RuntimeInformation.FrameworkDescription.StartsWith (".NET Framework", StringComparison.OrdinalIgnoreCase))
{
return GetNetFrameworkRuntime ();
return GetNetFrameworkRuntime (context);
}
else
{
Expand Down Expand Up @@ -123,24 +125,56 @@ static RuntimeInfo GetMonoRuntime ()
);
}

static RuntimeInfo GetNetFrameworkRuntime ()
static RuntimeInfo GetNetFrameworkRuntime (ICodeCompilationContext context)
{
var runtimeDir = Path.GetDirectoryName (typeof (object).Assembly.Location);
var csc = Path.Combine (runtimeDir, "csc.exe");
var csc = Path.Combine (context.CompilerSearchPath, "csc.exe");
if (!File.Exists (csc)) {
return FromError (RuntimeKind.NetFramework, "Could not find csc in host .NET Framework installation");
}
return new RuntimeInfo (
RuntimeKind.NetFramework,
runtimeDir: runtimeDir,
// we don't really care about the version if it's not .net core
runtimeVersion: new Version ("4.7.2"),
refAssembliesDir: null,
runtimeFacadesDir: runtimeDir,
cscPath: csc,
cscMaxLangVersion: CSharpLangVersion.v5_0,
runtimeLangVersion: CSharpLangVersion.v5_0
);

// now, determine the csc max lang version, just run csc -langversion:? and parse the output
var psi = new System.Diagnostics.ProcessStartInfo (csc, "-langversion:?") {
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};

psi.RedirectStandardOutput = true;
using (var p = System.Diagnostics.Process.Start (psi)) {
p.WaitForExit ();
var output = p.StandardOutput.ReadToEnd ();
if (output.Contains ("error CS2008")) {
// then we have a basic csc that doesn't support -langversion, probably net4
return new RuntimeInfo (
RuntimeKind.NetFramework,
runtimeDir: context.CompilerSearchPath,
// we don't really care about the version if it's not .net core
runtimeVersion: new Version ("4.7.2"),
refAssembliesDir: null,
runtimeFacadesDir: context.CompilerSearchPath,
cscPath: csc,
cscMaxLangVersion: CSharpLangVersion.v5_0,
runtimeLangVersion: CSharpLangVersion.v5_0
);
}

var maxLangVersion = CSharpLangVersionHelper.ParseLangVersionOutput (output);

// now that we know the max lang version, we need to find the runtime dir, which if it's msbuild, it's gonna be one dir up from CompilerSearchPath
// C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\Roslyn -> C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin

return new RuntimeInfo (
RuntimeKind.NetFramework,
runtimeDir: Directory.GetParent (context.CompilerSearchPath).FullName,
// we don't really care about the version if it's not .net core
runtimeVersion: new Version ("4.7.2"),
refAssembliesDir: null,
runtimeFacadesDir: context.CompilerSearchPath,
cscPath: csc,
cscMaxLangVersion: maxLangVersion,
runtimeLangVersion: maxLangVersion
);
}
}

static RuntimeInfo GetDotNetCoreSdk ()
Expand Down Expand Up @@ -193,8 +227,6 @@ static RuntimeInfo GetDotNetCoreSdk ()
out _
);



return new RuntimeInfo (
RuntimeKind.NetCore,
runtimeDir: runtimeDir,
Expand Down
30 changes: 28 additions & 2 deletions Mono.TextTemplating/Mono.TextTemplating/TemplateGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
using Microsoft.VisualStudio.TextTemplating;
using System.Threading;
using System.Threading.Tasks;
using Mono.TextTemplating.CodeCompilation;

namespace Mono.TextTemplating
{
Expand Down Expand Up @@ -119,7 +120,22 @@ void InitializeForRun (string inputFileName = null, string outputFileName = null
public bool ProcessTemplate (string inputFile, string outputFile)
=> ProcessTemplateAsync (inputFile, outputFile, CancellationToken.None).Result;

public async Task<bool> ProcessTemplateAsync (string inputFile, string outputFile, CancellationToken token = default)
public async Task<bool> ProcessTemplateAsync (string inputFile, string outputFile)
=> await ProcessTemplateAsync (inputFile, outputFile, CancellationToken.None).ConfigureAwait (false);

public async Task<bool> ProcessTemplateAsync (string inputFile, string outputFile, CancellationToken token)
=> await ProcessTemplateAsync (inputFile, outputFile, new DefaultCodeCompilationContext(), token).ConfigureAwait (false);

internal async Task<(string outputFile, string outputContent)> ProcessTemplateAsync (ParsedTemplate pt, string inputFile, string inputContent, string outputFile, TemplateSettings settings)
{
InitializeForRun (inputFileName: inputFile, outputFileName: outputFile);

var outputContent = await Engine.ProcessTemplateAsync (pt, inputContent, settings, this, new DefaultCodeCompilationContext()).ConfigureAwait (false);

return (OutputFile, outputContent);
}

public async Task<bool> ProcessTemplateAsync (string inputFile, string outputFile, ICodeCompilationContext context, CancellationToken token)
{
if (string.IsNullOrEmpty (inputFile))
throw new ArgumentNullException (nameof (inputFile));
Expand Down Expand Up @@ -263,10 +279,20 @@ public string PreprocessTemplate (
string outputFileName,
TemplateSettings settings,
CancellationToken token = default)
=> await ProcessTemplateAsync (pt, inputFileName, inputContent, outputFileName, settings, new DefaultCodeCompilationContext (), token).ConfigureAwait (false);

public async Task<(string fileName, string content)> ProcessTemplateAsync (
ParsedTemplate pt,
string inputFileName,
string inputContent,
string outputFileName,
TemplateSettings settings,
ICodeCompilationContext context,
CancellationToken token = default)
{
InitializeForRun (inputFileName, outputFileName);

var outputContent = await Engine.ProcessTemplateAsync (pt, inputContent, settings, this, token).ConfigureAwait (false);
var outputContent = await Engine.ProcessTemplateAsync (pt, inputContent, settings, this, context, token).ConfigureAwait (false);

return (OutputFile, outputContent);
}
Expand Down
Loading