MSBuild or NAnt script that generates C# files, adds them to a project and compiles the project - visual-studio

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>

Related

Get list of projects in solution with MSBuild

I have a Visual Studio solution with a number of C++ projects (.vcxproj). There is one utility project with a custom build step. On this build step I would like to get a list of projects in solution and pass it to an external tool. Is there a way to have such a list? Something like $(ProjectsInSolution)?
See also https://social.msdn.microsoft.com/Forums/vstudio/en-US/51254ee1-abaf-496a-89f9-cf87fc2ae1e8/list-project-from-solution-sln-file?forum=msbuild
Get list of projects in solution with MSBuild
You could use MSBuild Community Tasks's GetSolutionProjects for this question.
To accomplish this, create a new project in that solution, like GetProjectsPath. You should add MSBuildTasks to your test project. After that, you will find following scripts in your project file(If not, add it manually):
<Import Project="..\packages\MSBuildTasks.1.5.0.235\build\MSBuildTasks.targets" Condition="Exists('..\packages\MSBuildTasks.1.5.0.235\build\MSBuildTasks.targets')" />
Then unload your project. Then at the very end of the project, just before the end-tag </Project>, place below scripts:
<Target Name="GetProjectsPath" AfterTargets="Build">
<GetSolutionProjects Solution="..\GetProjectsPath.sln">
<Output ItemName="ProjectFiles" TaskParameter="Output" />
</GetSolutionProjects>
<Message Text="Get Projects Path in the solution!" />
<Message Text="Relative project paths:" />
<Message Text="%(ProjectFiles.ProjectPath)" />
<Message Text="Full paths to project files:" />
<Message Text="%(ProjectFiles.FullPath)" />
</Target>
When you build this project, you will get the all projects path in the solution:
Hope this helps.

Adding a custom preprocessing task in Visual C++

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.

Visual Studio Express 2012 for Web - don't generate bin and obj folders

Can I turn off generating these folders on build? They contain some .dll, .pdb and other files I don't need. I'm just using Typescript compilation.
Yes, it is possible. I relied on the following nice overview on how to hijack a build process:
http://sedodream.com/2013/06/01/HijackingTheVisualStudioBuildProcess.aspx
Three main tricks:
1) project_name.csproj is the one you need to modify, it is XML MSBUILD file.
2) You cannot remove dependency from WebApplication.targets, you need to "deactivate" them as below.
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v11.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
3) You have to implement general contract for clean/build/rebuild targets.
Mine looks like:
<Target Name="Build">
<Exec Command="call node_modules\.bin\tsc.cmd src/main.ts --out js/game.min.js > $(Temp)\tscout.txt" IgnoreExitCode="true" WorkingDirectory="$(SolutionDir)" />
<Exec Command="type $(Temp)\tscout.txt" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build">
</Target>
<Target Name="Clean">
<RemoveDir Directories="js">
</RemoveDir>
</Target>
You could create a New WebSite instead of New Project. That one should not create any binary output.

How to add a command line code generator to Visual Studio?

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>

msbuild custom task

I have a custom MSBuild task that takes in a set of JavaScript files, minifies them, and outputs them, with the extension .min.js. When I do a normal build through Visual Studio, it works perfectly and the .min.js files are output to the same directory as the original files. When I try to deploy using the Publish feature in Visual Studio, only the original .js files make it to the publish directory.... How can I get the output of my task to be counted as "content" so that it will end up in the published folder?
EDIT:
I was able to figure it out by adding the Output tag inside my task and then creating an ItemGroup around that:
<Target Name="AfterBuild">
<ItemGroup>
<JavaScriptFiles Include="Scripts\*.js" Exclude="Scripts\*.min.js" />
</ItemGroup>
<JsCompress Files="#(JavaScriptFiles)" OutputPath="Scripts">
<Output TaskParameter="CompressedFiles" ItemName="CompressedFiles" />
</JsCompress>
<ItemGroup>
<Content Include="#(CompressedFiles)" />
</ItemGroup>
</Target>
Build and Publish are separate targets. Add a target to your project, abstract your minification to its own target, then make the AfterBuild and Publish target depend on the minification target. Something like this:
<Target Name="AfterBuild" DependsOnTargets="Build;Minify">
</Target>
<Target Name="Publish" DependsOnTargets="Build;Minify">
</Target>
<Target Name="Minify" DependsOnTargets="Build">
<ItemGroup>
<JavaScriptFiles Include="Scripts\*.js" Exclude="Scripts\*.min.js" />
</ItemGroup>
<JsCompress Files="#(JavaScriptFiles)" OutputPath="Scripts">
<Output TaskParameter="CompressedFiles" ItemName="CompressedFiles" />
</JsCompress>
<ItemGroup>
<Content Include="#(CompressedFiles)" />
</ItemGroup>
</Target>
This snippet, of course, means you have to have a build target, which may or not be the case. For that reason you may need to modify this. Hope this helps!
Change the file properties. Check the Build Action and Copy to Output Directory properties for those files.

Resources