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

C#: Generate source files from cshtml files in standalone #13957

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

tamasvajk
Copy link
Contributor

@tamasvajk tamasvajk commented Aug 14, 2023

Supersedes #13722.

This PR adds cshtml/razor file handling to the standalone extractor. We're generating C# files to a temporary location from the view files with csc by running the razor source generators on all the cshtml/razor files in the repo. The generated source files are then extracted as normal source files.

@tamasvajk tamasvajk requested review from a team as code owners August 14, 2023 14:01
@tamasvajk tamasvajk marked this pull request as draft August 14, 2023 14:01
@tamasvajk tamasvajk force-pushed the razor-standalone-2 branch 3 times, most recently from 6a72e79 to 4ce95b9 Compare August 18, 2023 08:58
@tamasvajk tamasvajk removed the request for review from a team August 18, 2023 09:24
@github-actions github-actions bot removed the C++ label Aug 18, 2023
@tamasvajk tamasvajk marked this pull request as ready for review August 18, 2023 11:54
@tamasvajk tamasvajk requested a review from a team August 18, 2023 11:54
@michaelnebel michaelnebel self-assigned this Aug 21, 2023
Comment on lines +112 to +115
catch
{
// Ignore
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.
Copy link
Contributor

@michaelnebel michaelnebel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really really good!
It must have taken lots of reading and trial and error to get it to work!
Only some minor comments and questions.

@@ -131,6 +132,40 @@ public DependencyManager(string srcDir, IDependencyOptions options, ILogger logg
progressMonitor.UnresolvedReference(r.Key, r.Value);
}

var webViewExtractionOption = Environment.GetEnvironmentVariable("CODEQL_EXTRACTOR_CSHARP_STANDALONE_EXTRACT_WEB_VIEWS");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider to add a group of extractor options for buildless or standalone to make it easier for a user to specify this flag (not as a part of this PR)?

Maybe we should add this "option" to the standalone extractor Option and read the environment variable as a part of the creating the options object (and also make it a property on the DependencyOption) to avoid inlining the environment variable read here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in the long run we'll have a couple of configuration options, so it definitely makes sense to group them. At the same time, we should try to minimize the number of options as the extractor should "just work". I don't have full confidence in this cshtml to C# generation, so I wanted to hide it behind an undocumented feature flag, but eventually I don't think there's any reason to not have this run all the time.

this.progressMonitor = progressMonitor;
this.dotNet = dotNet;

sourceGeneratorFolder = Path.Combine(this.sdk.FullPath, "Sdks", "Microsoft.NET.Sdk.Razor", "source-generators");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Quick question: Have you found any documentation describing the structure of the location of stuff within the SDK folder or is it a best guess?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's just a best guess. It's working now on all platforms, but I don't think there's any guarantee that it works on previous versions or upcoming ones.

foreach (var f in cshtmls.Select(f => f.Replace('\\', '/')))
{
sw.WriteLine($"\n[{f}]");
var base64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(f)); // TODO: this should be the relative path of the file.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does it need to be the relative path?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what the dotnet CLI is doing. I think this change would have some runtime consequences, but I'm unsure what. Also, it's not something that we currently rely on in any of the checks.

{
var razor = new Razor(sdk, dotnet, progressMonitor);
razorWorkingDirectory = new TemporaryDirectory(ComputeTempDirectory(sourceDir.FullName, "razor"));
var generatedFiles = razor.GenerateFiles(views, usedReferences.Keys, razorWorkingDirectory.ToString());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider to use razoeWorkingDirectory.FullName instead of calling ToString().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just realized that TemporaryDirectory has no FullName. ToString() calls the underlying DirectoryInfo.FullName.

@@ -0,0 +1,3 @@
| Program.cs |
| Views/Home/Index.cshtml |
| _semmle_code_target_codeql_csharp_integration_tests_ql_csharp_ql_integration_tests_all_platforms_cshtml_standalone_Views_Home_Index_cshtml.g.cs |
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we get this long name because this is the name of the integration test folder (+ Views/Home/Index.cshtml)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

@@ -150,6 +150,8 @@ function RegisterExtractorPack(id)
end

local windowsMatchers = {
CreatePatternMatcher({ '^semmle%.extraction%.csharp%.standalone%.exe$' },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're calling dotnet exec csc, which is a call that is traced by the tracer. We don't want to extract this call, so we need to opt out of it somehow. This lua change makes sure that no processes started by the standalone extractor are traced. This was not working previously on all platforms, so in #13722 there's another implementation as a workaround. But the fix for the MacOS issue is already merged.

Comment on lines +177 to +181
catch (Exception ex)
{
// It's okay, we tried our best to generate source files from cshtml files.
progressMonitor.LogInfo($"Failed to generate source files from cshtml files: {ex.Message}");
}

Check notice

Code scanning / CodeQL

Generic catch clause Note

Generic catch clause.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants