I'm working on a project that uses code generation to generate C# classes using a command line tool from a text-based description. We are going to start using these descriptions for javascript too.
Currently these classes are generated and then checked in, however, I would like to be able to make the code generate automatically so that any changes are propagated to both builds.
The step that is run manually is:
servicegen.exe -i:MyService.txt -o:MyService.cs
When I build I want MSBuild/VS to first generate the CS file then compile it. It is possible to do this using, by modifying the csproj, perhaps using a MSBuild Task with Exec, DependentUpon & AutoGen?
Normally I would recommend a pre-build command be placed in a pre-build event, but since your command line tool will be creating C# classes needed for compiling, this should be done in the BeforeBuild target in the .csproj file. The reason for this is because MSBuild looks for the files it needs to compile between the time BeforeBuild is called and the time when PreBuildEvent is called in the overall process (you can see this flow in the Microsoft.Common.targets file used by MSBuild).
Call the Exec task from within the BeforeBuild target to generate the files:
<Target Name="BeforeBuild">
<Exec Command="servicegen.exe -i:MyService.txt -o:MyService.cs" />
</Target>
See the Exec task MSDN documentation for more details about specifying different options for the Exec task.
Antlr has an example of a process that can be used to add generated code to a project. This has the advantage of showing the files that are generated nested under the source file, although it is more complex to add.
You need add an item group with the file to be generated from, for example:
<ItemGroup>
<ServiceDescription Include="MyService.txt"/>
</ItemGroup>
Then add the cs file to be generated to the ItemGroup containing the rest of the source code.
<ItemGroup>
...
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
...etc..
<Compile Include="MyService.txt.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>MyService.txt</DependentUpon> <!--note: this should be the file name of the source file, not the path-->
</Compile>
</ItemGroup>
And then finally add the build target to execute the code generation (using % to execute the command for each item in the ItemGroup). This could be put into a separate file, so that it can be included from many projects.
<Target Name="GenerateService">
<Exec Command="servicegen.exe -i:%(ServiceDescription.Identity) -o:%(ServiceDescription.Identity).cs" />
</Target>
<PropertyGroup>
<BuildDependsOn>GenerateService;$(BuildDependsOn)</BuildDependsOn>
</PropertyGroup>
Related
I'm migrating my project build toolchain from python/MinGW to msbuild/MSVC.
I need to perform additional preprocessing on some c++ files before each build.
This is performed by my own python script.
Is there a way to do this without writing a extension? 'Custom Build Tool' doesn't allow to modify current source files. Is there any option to "chain" this with build step?
Adding a custom preprocessing task in Visual C++
Since you do not want to modify the source file, you can use the copy task to back up your scource files to the intermediate directory:
<ItemGroup>
<MySourceFiles Include="c:\MySourceTree\**\*.cpp"/>
</ItemGroup>
<Target Name="CopyFiles">
<Copy
SourceFiles="#(MySourceFiles)"
DestinationFolder="Destination\Intermediate directory"
/>
</Target>
Then use the csc task to compile it before build:
<ItemGroup>
<Compile Include="…\Intermediate directory\filename">
</Compile>
</ItemGroup>
<Target Name="BeforeBuild">
<Message Text="Running your python script on the Intermediate directory folder..."/>
</Target>
See "Preprocessing" each source file before compilation? for more details.
Update for comment:
Question is all about whether you can call your own target on build
failed.
You can use MSBuild command line with specify your custom target to build it:
msbuild.exe "YouProjectName.vcxproj" /t:BeforeBuild;Build
Then MSBuild will build your custom build first whether your build is failed or successfully.
Hope this helps.
In my project I have a json file I use for configuration that I have git set to ignore. When the repository is first cloned, the configuration file that is part of the project and that is copied to the output directory doesn't exist. I've gotten this to work using tasks in the 'BeforeBuild' target in the project that will copy the sample file to the actual config file if it doesn't exist.
<Target Name="BeforeBuild">
<ItemGroup>
<MySourceFiles Include="Configuration.sample.json" />
</ItemGroup>
<ItemGroup>
<MyDestinationFiles Include="Configuration.json" />
</ItemGroup>
<Message Importance="high" Condition="!Exists('#(MyDestinationFiles)')"
Text="Copying #(MySourceFiles) to #(MyDestinationFiles)" />
<Copy Condition="!Exists('#(MyDestinationFiles)')"
SourceFiles="#(MySourceFiles)"
DestinationFiles="#(MyDestinationFiles)" />
</Target>
So if I build the project, then delete the configuration file and do a build, nothing happens because no changes have been made that would change the outputs I think. Is there a way to change the project file so that a build will be flagged as necessary? It shouldn't come up very often and I can always do a 'Clean' or 'Rebuild' manually, but it's nagging at me since I'm just starting to learn MSBuild files.
From the documentation on a Target's Outputs attribute:
The files that form outputs into this target. Multiple files are
separated by semicolons. The timestamps of the files will be compared
with the timestamps of files in Inputs to determine whether the Target
is up to date
So if you add the paths to the outputfiles created by your Beforebuild target to it's Outputs attribute, at the start of every build msbuild will check if those files exist and if not it will start a build because now the project is considered to not be up-to-date anymore. In practice use:
<Target Name="BeforeBuild" Outputs="#(MyDestinationFiles)">
I've just written a code generator app for FluentMigrator API that emits an unknown number of C# class files. I wish to compile the code generator, run it to emit the C# classes then add the new C# files to an existing C# project and then compile final solution.
What's the best approach for adding the code generated C# files to a project?
Given what we know from your post, there could be a couple approaches.
You have an API that will generate some class files and want to integrate this into your build process so you would make an API call to generate new class files, then incorporate those class files into your build.
If your executable emits output files into it's current working directory, you could use an Exec task to run your command in the $(IntermeidateOutputPath) so as to not clutter up your project's source tree:
<Exec Command="MyExe.exe " WorkingDirectory="$(IntermediateOutputPath)\AutoGenClasses\" />
Following this command, you could append those output classes into the default compile group:
<ItemGroup>
<Compile Include="$(IntermediateOutputPath)\AutoGenClasses\**\*.cs" />
</ItemGroup>
Now you'd probably want to control when this occurs, so you'd embed this code into a separate <Target /> and schedule it to occur before the build occurs.
<Target Name="AutoGenClasses" BeforeTargets="Compile">
<Message Text="Starting the AutoGenClasses task..." Importance="high" />
<Exec Command="MyExe.exe " WorkingDirectory="$(IntermediateOutputPath)\AutoGenClasses\" />
<ItemGroup>
<Compile Include="$(IntermediateOutputPath)\AutoGenClasses\**\*.cs" />
</ItemGroup>
<Message Text="... completed the AutoGenClasses." Importance="high" />
</Target>
We use HEAT to build a file for our web project installer. I want to know if there is a way that I can have the file included in the compilation, but not included in the project.
The reason I need this is I would like to not check the file in on our source control, but have it build when we build the wixproj. Otherwise we have to hijack/checkout the file in order to reliably build the project.
If you are using MSBuild and a .wixproj to build your .MSI then you will need to get the output from HEAT to be listed in a Compile item in the .wixproj. If the file is not included it will not get compiled into the final .MSI.
Fortunately, MSBuild provides quite a few options to dynamically include items. For example, you could have a custom target in your .wixproj that runs HEAT and adds the output
<Target Name='RunHeat'>
<Exec Command='heat.exe param param param -o path\to\output.wxs' />
<ItemGroup>
<Compile Include='path\to\output.wxs' />
</ItemGroup>
</Target>
Now if you get the RunHeat target to run before the Compile target in wix.targets then the Compile item generated in RunHeat will be included in the Compile target. Given the flexibility of MSBuild there are probably a dozen other ways you could accomplish this task.
Hope that helps and good luck!
I am seeing some strange behavior with the ItemGroup tag.
My application depends on several DLLs, and I am copying these DLLs as well as the executable to a deploy directory, which is used by NSIS to create an install package from a fresh build. However, I am running into an odd issue with this.
I define my ItemGroup as follows (at the top of the file before I define my build target:
<MyAppFiles Include="$(ProjectRoot)\$(OutputPath)\*.dll;$(ProjectRoot)\$(OutputPath)\MyApp.exe" />
So, this grabs all the DLLs in the directory as well as the binary MyApp.exe. But, if the project has been cleaned (i.e., there are no files in $(OutputPath)). The DLLs are not included in the ItemGroup list of files. Now, if I follow this up with another build, (i.e., there are files from the previous build in $(OutputPath)) the ItemGroup contains all the files I want.
Also, I have checked the output of the build script and the DLLs are copied to $(OutputPath) whether a clean happened or not.
So, my question is, how do I correct my build script such that ItemGroup always contains the DLLs? It seems like the ItemGroup populates with files before the build happens, so if the files aren't there before the build, they aren't included in the list, but if they exist before the build then they are.
For reference, here is the build target that I am using:
<PropertyGroup>
<MyAppRoot>..\MyApp</MyAppRoot>
<MyAppProject>$(MyAppRoot)\MyApp.csproj</MyAppProject>
<PropertyGroup>
<Target Name="BuildProject">
<Message Text="BEFORE: #(ProjectFiles)" />
<MSBuild Projects="$(MyAppProject)" Targets="Build" Properties="Configuration=$(Configuration)">
<Output TaskParameter="TargetOutputs" ItemName="MyAppAssembly"/>
</MSBuild>
<Message Text="AFTER: #(ProjectFiles)" />
</Target>
Presumably your item array is declared at the root level in your project, in XML as a child of the <Project> element. This means that MSBuild will evaluate the membership in the item array when the project file is first loaded. Whether or not the existance of those files changes during execution is irrelevant. If you want to populate the item array at a particular point in your build, you need to change the declaration from a static item array to a dynamic one, which means moving it inside a target, to the exact spot where you want to gather the files, as:
<Target Name="BuildProject">
...before message
...msbuild task
<ItemGroup>
<ProjectFiles Include="$(ProjectRoot)\$(OutputPath)\*.dll" />
<ProjectFiles Include="$(ProjectRoot)\$(OutputPath)\MyApp.exe" />
</ItemGroup>
...after message
</Target>
(excerpted from trick #62 in the book "MSBuild Trickery")