20

Does anyone know how to modify a csproj file in a way to generate code files during build without actually referencing the files?

A process like :

  • create file,
  • dynamically reference temporary file during build
  • compiled assembly has additional members, depending on the files created during build

The purpose of this is to create a way of generating code files using roslyn instead of using t4 templates, which are very awkward to use once you're trying to do something depending on attributes.

Hence i am planning on providing a way to use a special csharp file (for full syntax support) to generate files programatically based on the contents of that special file.

I've spent a couple of weeks looking into resources on the internet (with the topic msbuild), but until now it seems i didn't use the right keywords.

This one has been the most insightful one to me yet: https://www.simple-talk.com/dotnet/.net-tools/extending-msbuild/

My guess is, that the correct build target for my purpose should be "BeforeCompile" in order to somehow populate the build process with custom code files.

Does anyone have experience with my issue, or is aware of any particular resources which deal with the task?

Solution i got it working with:

<UsingTask TaskName="DynamicCodeGenerator.DynamicFileGeneratorTask" AssemblyFile="..\DynamicCodeGenerator\bin\Debug\DynamicCodeGenerator.dll" />
<Target Name="DynamicCodeGeneratorTarget" BeforeTargets="BeforeBuild;BeforeRebuild">
    <DynamicFileGeneratorTask>
        <Output ItemName="Generated" TaskParameter="GeneratedFilePaths" />
    </DynamicFileGeneratorTask>
    <ItemGroup>
        <Compile Include="@(Generated)" />
        <FileWrites Include="@(Generated)" />
        <!-- For clean to work properly -->
    </ItemGroup>
</Target>

Unfortunately i did not get it to work with a propertygroup override as suggested

Update: This link is interesting too: https://github.com/firstfloorsoftware/xcc/blob/master/FirstFloor.Xcc/Targets/Xcc.targets

1
  • 1
    BeforeTargets="BeforeBuild;BeforeRebuild" - this was the game changing portion. Commented Aug 10, 2019 at 22:21

2 Answers 2

20

Generating the code file can be achieved by msbuild task or msbuild inline task. It is up to you to generate the proper code. One thing that you must care of is creating output item parameter in order to append it to the @(Compile) item. You can use $(IntDir) location to locate your newly generated file, and add them to the @(FileWrites) item group in order for Clean target work properly.

When you finish writing your task, you must use it in your project like this:

<UsingTask TaskName="TaskTypeFullName" AssemblyFile="YourAssembly.dll"/>
<PropertyGroup>
<!-- Here you need to experiment with [Build/Compile/SomeOther]DependsOn property -->
    <BuildDependsOn>
        MyCodeGenerator;
        $(BuildDependsOn)
    </BuildDependsOn>
</PropertyGroup>

<Target Name="MyCodeGenerator">
    <YourTaskName>
        <Output ItemName="Generated" TaskParameter="GeneratedFiles" />
    </YourTaskName>
    <ItemGroup>
        <Compile Include="@(Generated)" /> 
        <FileWrites Include="@(Generated)" /> <!-- For clean to work properly -->
    </ItemGroup>
</Target>

Sign up to request clarification or add additional context in comments.

4 Comments

Nice! I've started going into the UsingTask direction a while back, but i didn't combine it with your Output + Compile declarations. That should do the job. Once i've verified this, i'll check + upvote your answer. It certainly looks promising. Thanks for your help
Also note that in order the target to be executed properly - you must declare property BuildDependsOn after the import of the project specific target, i.e. Microsoft.CSharp.targets.
I will test this once i'm home. From what i've read i can guess how this should be working, but one more confirmation question: @(Generated) supports a syntax like "subfolder1/file.cs;subfolder2/file.cs" and depends on me generating the files according to a relative path to the csproj (as it's usual with compile include)? If i got that right, files must be copied there, since they will be moved later in the build cycle. Correct?
Yes. Its all up to you about what you put inside @(Generated) items. Once you inclulde it in Compile items, they will be passed to the Csc task as Sources parameter. Moreover, items support wildcards.
17

I wanted to use a bash script to generate code for a project using dotnet core on Linux. Here is what worked for me. And thanks @stukselbax, I built this off of your answer.

  <Target Name="GenerateProtocolBuffers" BeforeTargets="BeforeBuild;BeforeRebuild">
    <Exec Command="./generatecode.sh" Outputs="proto/*.cs">
      <Output ItemName="Generated" TaskParameter="Outputs" />
    </Exec>
    <ItemGroup>
      <Compile Include="@(Generated)" />
      <FileWrites Include="@(Generated)" />
    </ItemGroup>
  </Target>

Note that the script I'm using to generate the code is called generatecode.sh. Replace this with your own.

1 Comment

I ended up going using the CoreCompile <Target Name="PreBuild" BeforeTargets="CoreCompile"> instead of trying to work with the outputs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.