Open Pzixel opened 5 years ago
I'm not the project owner so I can't decide whether it'll be accepted or not, but I'll share my thoughts nonetheless, assuming it'll be.
I think it should be tool-wide - so it'd rather be an MSBuild property to be set in .csproj, passed then into parameters of dotnet-codegen
. I don't think doing it as a per-generator property has any purpose.
Making it an MSBuild parameter could be as simple as exposing another specific Intermediate output property, which would default to the current value.
The way to do this "right" (if there is a right way to check in generated code at all, which I doubt), may be to integrate with Visual Studio Single File Generators. That's the precedent that winforms, resx, and others have set for checking in generated code. A very long time ago this project offered an SFG option, but I took it out long ago.
Why do you want to check in generated code?
I was thinking about versioning issues as well as sharing issue, i.e. I cannot send a code snippet without having compiler.
The code issue is more severe, but IIRC NuGet packages always reference exact version so you will always have reproducible builds. But if someone uses semver like CodeGenerator.Roslyn ^0.4.0
then they could be large difference in versions that may lead to strange bugs.
However, when I write these lines I see that it's quite artificial so I start doubt if it worth it at all.
I don't think nuget even supports a ^0.4.0
syntax. They may support wildcards at the end, but ya, folks who do so do it at their own risk. I never do it.
Let's wait for a real use case to discuss.
I think a valid case would be ReSharper support. If you use the generated code, ReSharper shows red squiggles everywhere. Since ReSharper does not use Roslyn, I think the only way for it to recognize the generated code is writing it to a file in the project.
ReSharper doesn't need to support Roslyn, they could do with supporting MsBuild. Do they support WPF by now? WPF also generates partial classes into obj
but provides certain MsBuild patterns to allow intellisense support without doing a full build.
IMHO if ReSharper doesn't support WPF they should do their homework first, WPF and its code generation pattern has been around long enough to be considered established.
Just my opinion though.
Thanks for the answer. Seems like ReSharper was behaving oddly. I deleted the caches and the ReSharper intellisense is working correctly now.
R# works with this, but a bit buggy. It indeed requires reruns and cache cleanup. VS itself goes crazy sometimes, not to say about R# which is much less stable.
Would it be possible to re-open this issue? For my use case, I really want to save generated files to my repo. This would mean using the file source hash in the filename (filename.3kt2df.generated.cs) would be a bit cumbersome. Ideally, it should be simply filename.generated.cs.
My reasoning for wanting to check in generated code is that I'm trying to replace an existing "code generation" feature that uses reflection at run-time to find attributed fields, and uses LINQ expression trees to write the code. I want to take as much 'magic' out of that process as possible, and seeing a code diff of changes in a check-in would facilitate that.
I would even have potentially a few generators, but these would all generate partial classes of the original so could go into one single file or one per generator. The Roslyn Source Generators proposal also lists checking in generated files as a use case.
Reading about SFGs seems also very interesting, but I would prefer that not all members of my team, and all build agents, would require installing a VSIX. At least one other product, SpecFlow has deprecated using SFG in favour of using MSBuild task.
If agreed, I would try submit a PR for this feature.
The hash is not of the file source, but for the file path. It's there to enable saving files with the same name but from different subfolders into single output folder:
Source file | Generated file |
---|---|
~/Class.cs |
~/obj/Class.hash1.cs |
~/Folder/Class.cs |
~/obj/Class.hash2.cs |
I think the first step would be to allow setting the property passed as "output" to a different value than IntermediateOutputDirectory
. It's a quick and easy feature.
The second step is outputting the files without hashes, probably also preserving their relative directory (subpath). This will be much more complex and will need broader design discussion.
My proposal would be to support the following properties:
<PropertyGroup>
<CodeGenerationRoslynFilesOutputPath>C:\temp\gen\</CodeGenerationRoslynFilesOutputPath>
<CodeGenerationMirrorProjectStructure>True</CodeGenerationMirrorProjectStructure>
</PropertyGroup>
The first property CodeGenerationRoslynFilesOutputPath
would simply set the directory that the files should output to, and would default to $(IntermediateOutputPath)
. The other property would cause the output file paths to mirror the path to the file when rooted from the project directory.
I imagine the following use cases:
**\*.generated.*
from the input.Given the following folder structure:
+ C:\Projects\ProjectDir
+ SomeDir
- SomeFile.cs
+ OtherDir
- OtherFile.cs
Setting only the property CodeGenerationRoslynFilesOutputPath
(to C:\temp\gen
) would output:
C:\temp\gen\SomeFile.hash.generated.cs
C:\temp\gen\OtherFile.hash.generated.cs
Setting the property CodeGenerationRoslynFilesOutputPath
(to C:\temp\gen
) and CodeGenerationMirrorProjectStructure
would output:
C:\temp\gen\SomeDir\SomeFile.generated.cs
C:\temp\gen\OtherDir\OtherFile.generated.cs
This would however require that CodeGenerationMirrorProjectStructure
is passed to CGR.Roslyn.Tool and therefore would need a new commandline arg.
Another easier alternative would be to just check if the project directory and the output directory are the same, and assume that mirroring should happen. Or just drop it, in the worst case I can live with the hashes given that they are only based off the file name :)
I'll accept a PR for CodeGenerationRoslynFilesOutputPath
quickly, it's just a new extension/configurability point.
For the other thing, it's much more complicated. Currently we don't care about source location, we just generate content for it in the intermediate directory under some semi-unique name - collisions are almost impossible. This will drastically change, and just off the top of my head:
DEBUG
, NET4
, TRACE
), target framework, configuration (debug/release) - those are all unique inputs. If you save your files when building in Debug, they will be re-generated (possibly with different content!) in a Release build; when multi-targeting (2+ TargetFrameworks), if a generator uses that in it's output, it'll cause undefined behavior, since the builds may run in parallel, creating race condition on reads/writes to the same file.../../MyApp.Shared/Class.cs
) Where do you save that, or how do you calculate output?
In many cases it's desirable to save generated code in repository. It's a common practice for binaries, see recomendation for different
lock
files in different ecosystems.It seems reasonable to have some setting like
bool GenerateFilesInProject
that could enable in-project generation, similar to how WinForm does. It would be nice to have access to generated files in repository, without rebuilding the whole solution.I can make it myself if we are agreed on that, so my changes wouldn't be futile.