-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Add support for source generators in Razor compiler/SDK #15756
Conversation
I couldn't figure out the best area label to add to this PR. If you have write-permissions please help me learn by adding exactly one area label. |
/azp run |
Azure Pipelines successfully started running 1 pipeline(s). |
059e3f0
to
9a2b607
Compare
/azp run |
Azure Pipelines successfully started running 1 pipeline(s). |
</Target> | ||
|
||
<Target Name="MoveRazorSdkTools" AfterTargets="PublishRazorSdkTools"> | ||
<ItemGroup> | ||
<_RazorToolOutput Include="$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor.Tool\$(Configuration)\$(SdkTargetFramework)\publish\*.*" /> | ||
<_MvcRazorExtensionOutput Include="$(ArtifactsBinDir)$(Configuration)\Sdks\Microsoft.NET.Sdk.Razor\tasks\$(SdkTargetFramework)\Microsoft.AspNetCore.Mvc.Razor.Extensions.dll" /> | ||
<_RazorSourceGeneratorsOutput Include="$(ArtifactsBinDir)Microsoft.NET.Sdk.Razor.SourceGenerators\$(Configuration)\netstandard2.0\publish\*.*" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slightly relevant: dotnet/installer#9635 (comment).
src/RazorSdk/SourceGenerators/Microsoft.NET.Sdk.Razor.SourceGenerators.csproj
Show resolved
Hide resolved
} | ||
|
||
Directory.CreateDirectory(Path.GetDirectoryName(file.GeneratedOutputPath)); | ||
File.WriteAllText(file.GeneratedOutputPath, csharpDocument.GeneratedCode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Follow up: this compiles in to the assembly. There might be some runtime work involved here. See dotnet/aspnetcore#29862
<_TargetingNET60OrLater Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND | ||
$([MSBuild]::VersionGreaterThanOrEquals('$(TargetFrameworkVersion)', '6.0')) ">true</_TargetingNET60OrLater> | ||
|
||
<_UseRazorSourceGenerator Condition="'$(_TargetingNET60OrLater)' == 'true'">true</_UseRazorSourceGenerator> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have a sense for what it would take to un-jankify this file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took a stab at cleaning this up. Let me know what you think about using the Choose/When pattern for conditional setting these properties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is alright. Maybe a further step would be to try and isolate < 5 and this further to make sure the build order is easier to grock across tfms and we don't accidentally break things.
src/Tests/Microsoft.NET.Sdk.Razor.Tests/MvcBuildIntegrationTestLegacy.cs
Outdated
Show resolved
Hide resolved
9a2b607
to
bcba240
Compare
return null; | ||
} | ||
|
||
var fileWriteTime = File.GetLastWriteTimeUtc(portableExecutableReference.FilePath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible I'm misreading the flow here, but are we assuming that the last write time of a .dll
on disk has some relationship to whether our cache is up-to-date?
What happens when someone changes a reference to an older version of a package (which was already cached locally) - don't we need to regenerate the compilation output then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup, it's the same issue we've sorted multiple times with the Blazor SDK. We could make this more robust, but we're also waiting for better support for this sort of incrementality from the compiler (tracked dotnet/roslyn#51257).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Directly accessing the file system from a generator violates the assumptions we have around compilation. Performance is important but correctness is more so. 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could make this more robust
Yes, I'd think we need to do so without waiting for new compiler features. I don't know if there was already an issue filed to track this, so just in case I filed https://github.com/dotnet/sdk/issues/15913
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, now I realise that @captainsafia has already filed dotnet/aspnetcore#30234
Would it make sense to keep https://github.com/dotnet/sdk/issues/15913 open to track the actual correctness issue (which should be addressed in the short term), and dotnet/aspnetcore#30234 to track the broader performance/architectural issue of accessing the filesystem at all (which can wait longer)?
If @captainsafia or @pranavkm prefers there being just one issue for all of this, please feel free to close mine as a duplicate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@SteveSandersonMS Having another issue is fine. If only to capture this particular side-effect of requiring read/write interactions to compute the cache. Ultimately, whatever work is done here will be usurped by what we do long term do deal with the FS-access issue.
var descriptor = new DiagnosticDescriptor( | ||
razorDiagnostic.Id, | ||
razorDiagnostic.GetMessage(CultureInfo.CurrentCulture), | ||
razorDiagnostic.GetMessage(CultureInfo.CurrentCulture), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't the right pattern here. The compiler doesn't generate diagnostics based on CultureInfo.CurrentCulture
but rather by the culture that's passed into the compiler options. In general you should be using LocalizableResourceString
here. That will do the heavy lifting for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think that pattern should fit what you're doing here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice tip -- I've tracked this in dotnet/aspnetcore#30237 since I don't think this will happen for preview2 but we can sort out the diagnostics story in preview3.
globalOptions.TryGetValue("build_property.DesignTimeBuild", out var designTimeBuild); | ||
if (!globalOptions.TryGetValue("build_property._RazorReferenceAssemblyTagHelpersOutputPath", out var refsTagHelperOutputCachePath)) | ||
{ | ||
throw new InvalidOperationException("_RazorReferenceAssemblyTagHelpersOutputPath is not specified."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than throwing consider adding a diagnostic with error severity. That will produce a better customer experience here
} | ||
|
||
Directory.CreateDirectory(Path.GetDirectoryName(file.GeneratedOutputPath)); | ||
File.WriteAllText(file.GeneratedOutputPath, csharpDocument.GeneratedCode); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generators should not access the file system directly. That breaks several assumptions by the build system here. This is something we need to address.
Parallel.For(0, files.Count, GetParallelOptions(), i => | ||
{ | ||
var file = files[i]; | ||
if (File.GetLastWriteTimeUtc(file.GeneratedDeclarationPath) > File.GetLastWriteTimeUtc(file.AdditionalText.Path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we accessing the file system here? Generators should not be interacting with the file system.
Can we file an issue to track the direct access to the file system here? That is fine short term but near term it's something we need to address and find a different approach for. |
@jaredpar Sure! I've created dotnet/aspnetcore#30234 and jotted a few notes about where things stand long term with this. |
Migrates the contents of dotnet/aspnetcore#28807 into the SDK repo with: