-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Support generating non-source files #57608
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
Are these outputs also included in the final compilation somehow, or are they just independent artifacts of the build? |
They are secondary outputs of the build process, just like the .xml doc file produced for XML doc comments. If C# didn't support XML doc comments, you could in fact implement them fully as a source generator. Ideally, the output files would be produced in the same directory as the final output assembly, rather than being buried somewhere in the obj directory. But I can live with the obj directory output. |
probably, something related? #52677 |
I agree with this proposal. in my case I would need something as simple as this: void AddContent(string fileName, byte[] content); // add file as a content item Notice that in my case, I also need that the output file to be considered as a Content item, so it also needs to handle CopyToOutputDirectory, pack to nuget as a content file, etc... and not limited to text files In the grand scale of things, I would like to have is a framework similar to source generator, but for content files... basically a "content generator". I am not sure if this can be achieved by simply adding an "AddContent" method, or it would require a completely separated, and more specialised framework. A practical use case scenario from which we could benefit: We have a localization framework that parses the source code looking for specific localization attributes. Then it queries our database for localized strings, and writes an XML file containing the attribute keys and the localized strings. This is now running as a command line tool, which is nasty. |
This is also of interest to the Interop team and the DllImport source generator. We would like to emit artifacts that could be used in other languages and likely used in a post-compile MSBuild Target. Roslyn could give us a From within MSBuild, it would be helpful to either have a convention of a new set of |
@AaronRobinsonMSFT This is basically the design we landed on for the feature :) We actually have a (somewhat out of date) PR to implement this on the analyzer side #49046 but it's been stuck in limbo due to lack of prioritization. Feel free to bug @jaredpar if this is something you think is important to the success of the DllImport generator. |
As a +1, I'd also like this for the RegexGenerator. I'd like to be able to output non-compiled files that a developer could browse to in solution explorer to be able to provide additional diagnostic information about the regular expressions being used. Imagine, for example, if in addition to outputting RegexGenerator.g.cs that included the source code, it could also output a .dgml file containing a graph of the DFA for the regex, and a developer could simply double-click that .dgml file to have it opened in VS to visualize the shape of their regex. Today the best I can do here seems to be emitting that dgml content as a comment embedded in the .cs file file, at which point a developer needs to find it, copy/paste it to a separate file, and then open that file they created (and I need to somehow escape the content in a way that it doesn't break the .cs compilation but is still valid dgml). |
This would also be really useful for projects like Swagger. It could have a content generator that would output OpenAPI data for the API controllers in the project at compile-time rather than runtime. Thus avoiding reflecting classes and parsing the XML file at runtime. Even better if the API let you choose if the content should be added as a file next to the build output, or if it should be embedded as a part of the assembly dll file. |
@ChrML in the Swagger case how would you use the output though:
|
The exact same way as any solution item marked as "Content". Except in this case the content is not a solution item, but dynamically provided during compile by the source generator. As for the Swagger-like use-case there could be no change to the code for the library consumers. Internally all the runtime code could be almost-zero, and the library would just serve the compile-time generated OpenAPI file directly to the API. Assuming ofcourse that there's no dynamic logic involved and all metadata is available at compile-time. I would think this is the most common scenario. |
Adding my +1 as well for VisualStudio.Extensibility, we would love to have this natively supported since we generate both source files and also a metadata asset to be included as part of the extension build output. So being able to call a method like AddContent(string relativePath, ) which handles copying the file to the correct output location like a Content item would be great. |
Things like shader compilers also need to typically generate manifests or metadata in addition to shader binaries - I can imagine someone wanting to do a thing where you write shaders in C# or in string literals, and then a source generator would produce C# methods that expose access to the shader and then embed shader binaries and metadata for use at runtime. The alternative is manually pulling out manifest resources etc, which is error prone (especially since embedding manifest resources exposes you to msbuild's broken dependency tracking). |
I would love this for generating SQL change scripts based on classes representing tables. |
I'd also like this to generate Razor pages with Source Generators. Since it's probably too late for .NET 7, can anyone say if this is triaged for .NET 8? |
Adding a +1 to this proposal. We have a few scenarios where we use targets to launch a separate executable that uses reflection to generate a content file and includes that content as part of the build. This process works, but can be fragile. Would be fantastic to be able to utilize the context within source generators to achieve this. |
Encountered a need for this today to wield Blazor's WASM tooling to generate a native P/Invoke trampoline to account for scenarios that the current P/Invoke tooling in Blazor doesn't handle very well. However, I fully appreciate that analogues to this feature in other languages (such as the Rust proc_macro) also do not implement this because fundamentally it doesn't make sense for a A->B compiler to also generate C to then be fed into another compiler, they're fundamentally different stages of the pipeline. Perhaps a happy medium could be allowing the generation of a metadata file from a source generator to allow SGs to provide information about their execution retrospectively, to then be picked up by another tool, using an opt-in parameter on csc. |
You can generate cs file with block comment on full json/xml content) make extension something like foo.metadata.cs. on pre build step (but after generation) rename file to .json/.xml with comment block cut. Of course it is a trick but still if you really need this - would work. |
@Perksey I have been able to successfuly do that by writing a custom MSBuild task: |
Would allowing generating non-source files only within Alternatively if we really must avoid writing files at all cost during this process, could there be a mechanism to push information back to msbuild so a msbuild task can pick it up and further process however/whenever needed? That would allow code inspection during compilation while allowing generating whatever non-code files based on that after compilation. |
No. This feature still has a number of open issues and likely requires changes to other products for us to ship. For example non-source file outputs must be written to disk. That represents dynamic build outuputs from the compilation task. That is not supported by MSBuild today and would lead to basic build correctness issues. Dealing with this is an active discussion topic between us and the MSBuild team. This is a feature we're very much looking at for .NET 9 but there is not a simple fix. |
Want to elaborate on the problem this presents for MSBuild. The purpose of this feature is to allow source generators to produce non-source files outputs. These outputs will be written to disk for later phases of the build to consume. MSBuild tracks both inputs and outputs to targets. At a high level when evaluating a given target for a project MSBuild will determine if the inputs to the target are the same as the last evaluation and the outputs still exist, if so then it can skip the target entirely. This is how incremental builds, fast up to date, etc ... function in MSBuild. This means though that if you do a build of MSBuild support though requires that all of the inputs and outputs are known at the time the target is evaluated. That is where the check occurs. The issue with non-source file outputs is they are determined dynamically by the generators based on the content of the I understand that some contributors here may see that as an acceptable trade off but we do not. Really hard to push through a compiler feature that is fundamentally incompatible with our basic build principles. It's a recipe for customer confusion and lots of feedback issues. There are a couple of ways this could be fixed:
At the moment (2) is the preferred approach and we're digging into its viability. MSBuild tracking issue: dotnet/msbuild#701 |
Nit: it's at execution time, just before executing the tasks inside the target (step 6 of the target order determination). (It could have been at evaluation time but it's slightly more dynamic than that--if it was at evaluation, plan 1 wouldn't work.) |
This does feel like it may invoke a future feature request for |
@jaredpar I remember being unconvinced that this feature was truly needed (in MSBuild). Did you or the team come up with scenarios that are driving this current work? |
For browser scenarios, CSP rejects attempts to dynamically generate code or run code that wasn't loaded from a trusted network source, so if you are (for example) generating C#/JS interop bindings, it is ideal if you can produce a paired .cs file and .js file from the same inputs in one go. Embedding the JS in a string literal would violate CSP, so the only alternatives are to have a separate tool that generates the .js files from the source metadata, or to embed the JS in the generated C# somehow and extract it with an msbuild task. IIRC we ended up working around the lack of good solutions by using an interpreter instead of generating JS, even though it's slower. I don't know if we would actually rewrite our code to use this feature when it arrives (we were told to expect it a long time ago). I also saw someone suggest an analyzer+source generator as a way to implement dotnet/csharplang#7529 which would require the generator be able to generate string tables as secondary outputs. (I don't think that is actually a solution for the use case, but it was suggested there.) |
Can't tell which feature you're speaking about here: non-source outputs in compiler or dynamic output tracking in MSBuild. If it's non-source outputs, it's still not 100% clear if it's fully needed. There are many scenarios where it would be a strong benefit to the ecosystem. There are ways to work around this by just writing more MSBulid code but it would be a lot better integrated in the source generator pipeline. Essentially there is enough benefit here for us to be doing deeper investigation. At the same time the "just write more MSBuild code" is not always viable. As long as the output file set depends on the input code then it's still a dynamic output problem. That pretty much has no other solution than a new MSBuild feature or always run targets. If it's msbuild tracking dynamic output,, I outlined one of the more important scenarios above. Effectively a general build principal for us is deleting build output should cause rebuild to re-execute compilation. Lacking MSBuild features or the targets trick I mentioned this can't happen. That's a non-starter for us right now. |
I just wanted add a +1 to this scenario and link to the .NET Aspire Components use case for this: dotnet/aspire#1146. .NET Aspire Components allow app settings to be configured in The input to these JSON schema files is .NET Types, in this case the class is the StackExchange.Redis ConfigurationOptions class. As we update this class, I'd want the JSON schema files to be automatically updated, instead of having to hand-craft the files that we do today. This seems like a perfect fit for a Roslyn Source Generator, if it supported outputting non-source files. |
@eerhardt why does this need to be a SG thought? Nothing in the same app will consume that same json schema right? So this could just be a trivial tool that runs over the compilation as a post-step and produce the json schema file. |
Because of double syntax tree build maybe?) With proper cache SG could do this automatically. Also it will be convenient to encapsulate this logic to separate nugget package so proj file will be untouched |
Note: i'm fairly sure people want a post-generator, since SGs can't see the results of other SGs. So if you wrote an SG here, for example, your json-schema could not include any information about things generated by other generators, which is not what i think people want. |
It doesn't need to be a SG. To unblock the scenario my plan is exactly as you suggest - a post-compilation step. It just feels more natural to do this during |
definitely seems like a general way to hook into the compiler to access syntax and the semantic model during other tasks/targets is what people actually need. can say that would handily cover my use-cases for this feature too, as long as that can be made to run early enough to create embedded resources |
Yeah, this is definitely possible, though we coudl likely make things better here by making the tasks easier to write. As an example, the roslyn IDE does this ourselves in order to generate LSIF files from the built compilations. If we could extract out the portion of that taht handles getting the same data and then operating on that, that would likely be the most valuable thing here. IMO, given a compielr server, you have the best of all worlds:
etc. etc. |
This may work but only because the file name is known before the build occurs. Thus it's not subject to the issues I listed before. That is the rare case though. Most non-source generators don't know their content until they inspect the code. I generally agree though that most of these generators fall into post-compilation. Essentially has no IDE / keystroke interaction. If we do this feature the interface will be geared to that. There have been requests to generate resources that could be embedded into the compilation. That doesn't have a nice clean split. At the same time this request seems to be the minority use case right now.
We can do this but I would push back strongly on it. The msbuild task for translating build properties into a correct compilation is complex. Maintaining a compiler server that meets the reliability constraints we have for build is very hard. Duplicating that would be a decent effort. Even if it were easy though there is also the problem that many teams in the industry maintain copy of our .target files. When you alter them like this they get out of sync with those targets and suddenly code that should compile stops compiling. I think it makes a lot more sense to follow our existing generator model where such plugins run in the existing compiler infrastructure. Because they are purely tools of build (don't impact IDE experience) such generators won't come into play in the IDE space.
jared starts laughing and ends by crying |
I took a stab at writing this today, and I don't think this is trivial. How exactly would you do this? What I tried:
The issue I immediately hit is that the Is using a
How do you make a custom task or tool that uses a compiler server? Are there examples? |
I don't think it's realistic to do.
You can't really. The only task that can access the compiler server is the I do not think hosting the compiler yourself is a realistic path forward. We've seen many teams attempt to do this over the years and it does not end well for them. |
This article contains many use cases. May be it is enough now to generalize... I don't want to seem annoying, but let me ask please: is there any plan in Net9 dev cycle to work about this topic? if no, what is about some kind of workaround: many of us will be happy if we will have a way to touch |
We have a few use cases where we'd like to leverage source generators to produce non-source files. Basically, we want to use source generators to generate secondary build outputs. For example:
We annotate data models with data classification attributes to denote PII and we produce a CSV file that enumerates the types and members in the project and their data classification. You can then take this CSV file and use it when doing privacy audits.
We use source generators to produce a bunch of code around metrics. We'd like to emit a schema file describing the effective shape of the metrics being produced by the code. This would probably be in the form of an output JSON file.
We have a few other scenarios in the wings which could leverage this approach.
Unfortunately, source generation is currently limited to C# sources. Trying to emit files with other extensions doesn't work. As a result, our source generator currently contains this horrible hack:
Could we get 1st class support for producing more than .cs files?
The text was updated successfully, but these errors were encountered: