multiple build rules using vc2010 and msbuild - c++

I'm managing a C++ project in VS2010 and want to have ALL .cpp files run through an external tool before going to the C++ compiler. All signs seem to indicate this is possible. See, for example, here.
Since this will happen over multiple projects, it makes sense to put this functionality in a property sheet and then just importing this property sheet everywhere. Before I touched the property sheet, it looked like this in its entirety:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>
<ExtensionsToDeleteOnClean>...</ExtensionsToDeleteOnClean>
<CustomBuildBeforeTargets>ClCompile</CustomBuildBeforeTargets>
</PropertyGroup>
<ItemDefinitionGroup>
<ClCompile>
<AdditionalIncludeDirectories>...</AdditionalIncludeDirectories>
<ForcedIncludeFiles>%(ForcedIncludeFiles)</ForcedIncludeFiles>
</ClCompile>
<Link>
<AdditionalDependencies>...</AdditionalDependencies>
</Link>
<Outputs>...</Outputs>
</CustomBuildStep>
</ItemDefinitionGroup>
</Project>
As per the above linked document, I have added the following lines:
<Project>
<PropertyGroup>
...
<CustomBuildBeforeTargets>ClCompile</CustomBuildBeforeTargets>
</PropertyGroup>
...
<ItemGroup>
<CustomBuild Include="*.cpp">
<Message>Running Custom Build Step</Message>
<Command>dummy</Command>
<Outputs>dummy</Outputs>
</CustomBuild>
</ItemGroup>
</Project>
This appears to have no effect, and my custom build tool never runs before ClCompile. I have tried various ways of moving things around and renaming tags (the Xml editor complains that CustomBuild isn't even valid according to the schema, for example), but nothing works.
What am I doing wrong?

Although this is a very old question, I will attempt to answer it.
The Include attribute expects to find *.cpp files in the Project directory. If there are no .cpp files in the Project directory, then this CustomBuild "Task" will never kick in.
What the OP could have done was
<CustomBuild Include="**\*.cpp">
<Message>Custombuild kicking in</Message>
<Command>echo %(Identity)</Command>
<Outputs>dummy</Outputs>
</CustomBuild>
instead of
<CustomBuild Include="*.cpp">
...
and he would have performed the CustomBuild action on all the .cpp files in all the subdirectories of the project directory, and not just the .cpp files waiting to be compiled.
If the .cpp files are not availble under the Project directory (can happen when such a project directory structure is used) then the OP must explicitly point to the right "root" directory.

Alternatively you can save yourself wildcard usage by simply running an Exec task on every included ClCompile.
<Target Name="ProcessClCompileFiles" BeforeTargets="ClCompile" Condition="'#(ClCompile)'!=''">
<Message Text="== Starting processing cpp files ==" Importance="High"/>
<Exec Command="echo processing %(ClCompile.filename)%(ClCompile.extension) & ////YOUR COMMAND HERE////" />
</Target>
As a note. This requires the ClCompile itemgroup to be initialised before this Target. if you were to create a target that dynamically adds ClCompile, you'd have to ensure that this happens before this Target is called.
Another note is that these Exec tasks will always run if there are any items in the ClCompile itemgroup. If you have a condition where it should skip them (eg, already processed), use Condition="A=B"
For easy reference, here's how to validate it hasn't created a file and if it created a file; if the file is out of date:
Condition="!Exists('$(OutDir)%(Filename)%(Extension)') OR ($([System.DateTime]::Parse('%(ClCompile.ModifiedTime)').Ticks) > $([System.IO.File]::GetLastWriteTime('$(OutDir)%(Filename)%(Extension)').Ticks))"

Related

How to efficienlty get all include paths in MSVC2013

I'm tyring to refactor my code using include-what-you-use, but I need to figure out exactly which directories VS is looking in.
How do I get a listing of all include paths visible to files in my project?
If I add an incorrect include, something like "#include " I get the listing I want, but I can't copy and paste the damn thing! (and OCR doesn't seem to work).
How do I get this list?
Copy/pasting from the dialog is ok if you want to do this once manually but doesn't scale well. Since you tagged this msbuild, here's a solution using it. Create a file names showincludes.targets and paste this msbuild code:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="ShowIncludes">
<Message Text="$(Include);%(ClCompile.AdditionalIncludeDirectories)"/>
</Target>
</Project>
Now to get the list of includes for any project file use
msbuild \path\to\vcxproj /p:ForceImportAfterCppTargets=\path\to\showincludes.targets /t:ShowIncludes
This will import said file into the project give, so it has access to all properties, and print out the value of the INCLUDE environment variable which is by default used by the compiler, and also the value of AdditionalIncludeDirectories which is passed to the compiler by msbuild.

How to use the Web Publishing Pipeline and Web Deploy (MSDEPLOY) to Publish a Console Application?

I would like to use web deploy to publish a Visual Studio "Console" application to a folder on the target system.
I have had some luck, and have been able to produce something similar to what I need, but not quite.
I've added the following to the console .csproj:
added the following projectName.wpp.targets file
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
and I've added the following projectName.wpp.targets:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<DeployAsIisApp>false</DeployAsIisApp>
<IncludeSetAclProviderOnDestination>false</IncludeSetAclProviderOnDestination>
</PropertyGroup>
<ItemGroup>
<FilesForPackagingFromProject Include="$(IntermediateOutputPath)$(TargetFileName).config">
<DestinationRelativePath>bin\%(RecursiveDir)%(FileName)%(Extension)</DestinationRelativePath>
<FromTarget>projectName.wpp.targets</FromTarget>
</FilesForPackagingFromProject>
</ItemGroup>
</Project>
I then edit the .SetParameters.xml file as follows:
<parameters>
<setParameter name="IIS Web Application Name" value="c:\company\project" />
</parameters>
When I then deploy using the generated .cmd file, I get all the files deployed to C:\company\project\bin.
That's not bad, but I'd like to do better. In particular, I'd like to omit the "bin" folder and put all files in the "C:\company\project" folder, and I'd like to be able to specify the ACLs
Has anybody been able to work around these problems?
Ok, so here's the way how to omit the 'bin' folder.
First of all, I'd like to emphasize that all this msdeploy-related stuff is for web apps deployment, and 'bin' folder seems for me to be almost hardcoded deeply inside. So if you want to get rid of it - you have to do some dirty things. Which I did.
We'll have to change $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets project a little bit, so it's better to change not it, but it's copy.
Steps:
1.Backup $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets(alternatively, you could install MSBuild.Microsoft.VisualStudio.Web.targets package, redirect your csproj file to Microsoft.WebApplication.targets file obtained from package and work with it).
2. In the $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplicaton.targets find the xml node which looks like <CopyPipelineFiles PipelineItems="#(FilesForPackagingFromProject)"(there are several ones of them, take the one from the line ~2570).
3. Comment the node out, replace with the custom one, so eventually it will look like:
<!--
<CopyPipelineFiles PipelineItems="#(FilesForPackagingFromProject)"
SourceDirectory="$(WebPublishPipelineProjectDirectory)"
TargetDirectory="$(WPPAllFilesInSingleFolder)"
SkipMetadataExcludeTrueItems="True"
UpdateItemSpec="True"
DeleteItemsMarkAsExcludeTrue ="True"
Condition="'#(FilesForPackagingFromProject)' != ''">
<Output TaskParameter="ResultPipelineItems" ItemName="_FilesForPackagingFromProjectTempory"/>
</CopyPipelineFiles>-->
<!-- Copying files to package folder in 'custom'(dirty) way -->
<CreateItem Include="$(OutputPath)\**\*.*">
<Output TaskParameter="Include" ItemName="YourFilesToCopy" />
</CreateItem>
<Copy SourceFiles="#(YourFilesToCopy)"
DestinationFiles="#(YourFilesToCopy->'$(WPPAllFilesInSingleFolder)\%(RecursiveDir)%(Filename)%(Extension)')" />
Then
4. Your projectName.wpp.targets don't have to have FilesForPackagingFromProject, so it will look like:
<!-- targets -->
<PropertyGroup>
<DeployAsIisApp>false</DeployAsIisApp>
<IncludeSetAclProviderOnDestination>false</IncludeSetAclProviderOnDestination>
</PropertyGroup>
<ItemGroup>
<!-- intentionally left blank -->
</ItemGroup>
</Project>
That's it. Worked for me(tm), tested. Let me be honest, I don't like this approach, but that was the only way I made it working in the needed way. It's up to you whether you'll use it in your project or not.
My opinion is not to use msdeploy here - it was not for you task.
Better to write msbuild-scripts from scratch or accept the 'bin' folder, and fight against the framework again once next customization is required.

Msbuild resolve project reference to custom project type

I've created a custom project type (cljproj instead of csproj) for a programming language called clojure. When the project compiles it outputs multiple .dll files as well as some dependent .clj files.
This is done by overriding the default CoreCompile target in the cljproj file. Which basically copies all files needing compilation to the bin directory and then executes a separate app to compile them.
<Target Name="CoreCompile">
<PropertyGroup>
<ClojureNamespaces>#(Compile -> '%(RelativeDir)%(Filename)', ' ')</ClojureNamespaces>
</PropertyGroup>
<Copy SourceFiles="#(Compile)" SkipUnchangedFiles="true" OverwriteReadOnlyFiles="true" DestinationFiles="#(Compile -> '$(OutDir)%(RelativeDir)%(Filename)%(Extension)')" />
<Exec WorkingDirectory="$(OutDir)" Command=""$(ClojureRuntimesDirectory)\$(ClojureVersion)\Clojure.Compile" $(ClojureNamespaces.Replace('\', '.'))" />
</Target>
I've added a reference from a c# project (csproj) to my clojure project (cljproj).
<ProjectReference Include="..\Clojure ASP.Net MVC Controller Library1\Clojure ASP.Net MVC Controller Library1.cljproj">
<Project>{8fe1995b-4b6d-4911-b563-a759467fdf53}</Project>
<Name>Clojure ASP.Net MVC Controller Library1</Name>
</ProjectReference>
Visual Studio by default doesn't resolve the project reference correctly, because it assumes there will only be one output, Clojure ASP.Net MVC Controller Library1.dll.
Examples of the actual output files are MvcApplication1.Controllers.HomeController.dll and HomeController.clj
I'd like to make this work without making any changes to the C# .csproj file, so that a .cljproj can easily be referenced from any .csproj file.
My attempt to resolve the project reference is by overriding the GetTargetPath target.
<Target Name="GetTargetPath" DependsOnTargets="$(GetTargetPathDependsOn)" Returns="#(TargetPath)">
<ItemGroup>
<TargetPath Include="$(TargetDir)\**\*.dll" />
<!-- <TargetPath Include="$(TargetDir)\**\*.clj" /> -->
</ItemGroup>
</Target>
If I set the TargetPath using *.dll, it works and copies the .dll files to the c#.csproj output directory. It even copies the .pdb files to that directory, although I didn't add them to the TargetPath. However, if I uncomment the *.clj TargetPath, CSC complains that the .clj files are corrupt (probably because they are plain text, not .net assemblies).
I'm happy to use a copy command instead of overriding TargetPath, however I'm not sure which variable to use for the directory to output them to, because $(outdir) gives me the bin of my custom project (.cljproj) not the bin of the c# project trying to resolve the project reference (.csproj). I'm not sure what other target to override besides GetTargetPath, because most clojure project (.cljproj) targets are not called when compiling the c# (.csjproj) project, for example: .cljproj:AfterBuild is only called when compiling cljproj directly, not when compiling .csproj which has a project reference to .cljproj.
I was able to get it to work by overriding the GetCopyToOutputDirectoryItems Target.
<Target Name="GetCopyToOutputDirectoryItems" Returns="#(CopyToOutputDirectoryItemsWithTargetPath)">
<ItemGroup>
<CopyToOutputDirectoryItems Include="$(TargetDir)\**\*.clj">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</CopyToOutputDirectoryItems>
</ItemGroup>
<AssignTargetPath Files="#(CopyToOutputDirectoryItems)" RootFolder="$(TargetDir)">
<Output TaskParameter="AssignedFiles" ItemName="CopyToOutputDirectoryItemsWithTargetPath" />
</AssignTargetPath>
</Target>

Automatic copy files to output during application building

There is Copy to Output Directory property for files in C# projects. But in VC++ projects it is absent. I know, that I can use Build events in VC++ and write there something like
xcopy /y /d %(FullPath) $(OutDir)
Is there a way to avoid the use of CMD (and other scripting methods)? Can msbuild do something to help in this case?
Can MSBuild do something to help in this case?
Using MSVC 2012, this worked for me:
Assumed you have a file "Data/ThisIsData.txt" in your c++ Project.
Unload the project (right click --> Unload Project).
Edit project XML (right click --> Edit .vcxproj)
Now you see the projects MSBuild file as XML in your editor.
Find "ThisIsData.txt". It should look something like:
<ItemGroup>
<None Include="Data\ThisIsData.txt" />
...
</ItemGroup>
Now add an other item group like this:
<ItemGroup>
<Content Include="Data\ThisIsData.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
...
</ItemGroup>
Reload the project and build.
Your file "ThisIsData.txt" should get copied to $(OutDir)\Data\ThisIsData.txt.
Why duplicating the ItemGroup?
Well if you simply change the None include to a content include, the IDE does not seem to like it any more, and will not display it. So to keep a quick edit option for my data files, I decided to keep the duplicated entries.
In VS 2015 it is possible to give C projects the functionality that is in C#.
(Idea from building off of jochen's answer.)
Instead of adding another ItemGroup, modify the given itemgroup adding a CopyTo element. I.E, using his example, simply enhance the original entry to:
<ItemGroup>
<None Include="Data\ThisIsData.txt" />
<DeploymentContent>true</DeploymentContent>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
...
</ItemGroup>
No other ItemGroup required. By adding the CopyTo element, you add an "Included In Project" property.
In Visual Studio 2017 you can do this in the IDE. I am not sure about earlier versions.
Simply add the file as an included project file so it shows in the Solution Explorer. Then right click on the file and select the Properties menu.
Change the Content to "Yes" and change the Item Type to "Copy file"
If you look at the changes it made to the project file you can see it added this:
<ItemGroup>
<CopyFileToFolders Include="Filename.txt">
<DeploymentContent>true</DeploymentContent>
<FileType>Document</FileType>
</CopyFileToFolders>
</ItemGroup>
It depends on what version of Visual Studio you are using. Format of VC++ project file in Visual Studio 2008 is not MSBuild and so using xcopy in PostBuildStep is a good choice.
VC++ project in Visual Studio 2010 has MSBuild format. Thus, there is functionality of MSBuild Copy task.
Below is a sample:
<Copy
SourceFiles="%(FullPath)"
DestinationFolder="$(OutDir)"
/>
If the destination directory does not exist, it is created automatically
An MSDN Copy task reference is here
Following henri-socha's answer about VS2015 (and probably VS2013 and VS2012, or anything using MSBuild style projects), the ItemGroup item type is important.
Specifically <Text> items do not seem to be copied, whereas <Content> items do.
So, for a project directory Data containing a text file ThisIsData.txt, this will create a subdirectory Data under the $(OutDir) directory and copy the file ThisIsData.txt from the project into it if it's newer:
<ItemGroup>
<Content Include="Data\ThisIsData.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
This won't, although it is what the Visual Studio IDE will insert if you add the text file to your project, and set the Content property to True.
<ItemGroup>
<Text Include="Data\ThisIsData.txt">
<DeploymentContent>true</DeploymentContent>
</Text>
</ItemGroup>
So in other words you need to add the file via the IDE to make it realise the file is included in the project (which adds <Text> tag ItemGroup), and then open the project in a text editor and add the <Content> tag ItemGroup to get it to do what you want.
I'm not sure what the <DeploymentContent> tag actually does. It may be a remnant since the only MSDN reference I could find considers it archived: https://msdn.microsoft.com/en-us/library/aa712517.aspx
In visual studio 2019 after setting the file as "Include in project" you can edit the properties an select as Item Type "Copy file" (as shown in https://i.stack.imgur.com/vac2b.png)
This avoids the manual vcxproj file edition.
You can specify copying in the project file as Jeff G answered in another question:
In the *.vcxproj file, change:
<Text Include="Filename.txt" />
to:
<Content Include="Filename.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
Then in the *.vcxproj.filters file, change:
<Text Include="Filename.txt">
<Filter>Resource Files</Filter>
</Text>
to:
<Content Include="Filename.txt">
<Filter>Resource Files</Filter>
</Content>
where the <Text ...> tag is for specified text files (it'll be <Image ...> for image files etc.)
If it's a COM dll, you can add it to the root of your project, mark it as 'Content' and set copy to output directory to 'Always'. I had to do this for signature capture COM assembly.

How do I add a custom build target to a Visual C++ 2010 project?

There are plenty of guides out there which help you mimic VS2008's "Custom Build Step" in VS2010 with MSBuild. However, I'd like my build to be smarter and make use of MSBuild. I've written a little MSBuild task which invokes the ANTLR parser generator. That build task works flawlessly when I run it in a simple test MSBuild file. However, when I try to add my task to a C++ project, I run into problems. Essentially I've added this to the top of my project file (Right after the <project> element):
<UsingTask TaskName="ANTLR.MSBuild.AntlrGrammar"
AssemblyName = "ANTLR.MSBuild, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d50cc80512acc876" />
<Target Name="BeforeBuild"
Inputs="ConfigurationParser.g"
Outputs="ConfigurationParserParser.h;ConfigurationParserParser.cpp;ConfigurationParserLexer.h;ConfigurationParserLexer.cpp">
<AntlrGrammar
AntlrLocation="$(MSBuildProjectDirectory)Antlr.jar"
Grammar="ConfigurationParser.g"
RenameToCpp="true" />
</Target>
However, my target is not being called before build.
How can I add my task to a C++ build?
Before reading this answer, you'll probably want to see:
General .vcxproj File Reference
The New Way of doing Build Extensibility in .NET 4
The old way of extending MSBuild, and the one mentioned by the reference book I have, essentially is based on overriding default-empty targets supplied by Microsoft. The new way, as specified in the second link above, is to define your own arbitrary target, and use the "BeforeTargets" and "AfterTargets" properties to force your target to run before or after your intended target.
In my specific case, I needed the ANTLR Grammars task to run before the CLCompile target, which actually builds the C++ files, because the ANTLR Grammars task builds .cpp files. Therefore, the XML looks like this:
<Project ...
<!-- Other things put in by VS2010 ... this is the bottom of the file -->
<UsingTask TaskName="ANTLR.MSBuild.AntlrGrammar"
AssemblyName = "ANTLR.MSBuild, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d50cc80512acc876" />
<Target Name="AntlrGrammars"
Inputs="Configuration.g"
Outputs="ConfigurationParser.h;ConfigurationParser.cpp;ConfigurationLexer.h;ConfigurationLexer.cpp"
BeforeTargets="ClCompile">
<AntlrGrammar
AntlrLocation="$(MSBuildProjectDirectory)\Antlr.jar"
Grammar="Configuration.g"
RenameToCpp="true" />
</Target>
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
</Project>
As for why this is superior to a PreBuildEvent and/or PostBuildEvent; this is smart enough to not rebuild the .cpps when the grammar itself is not updated. You'll get something like:
1>AntlrGrammars:
1>Skipping target "AntlrGrammars" because all output files are up-to-date with respect to the input files.
1>ClCompile:
1> All outputs are up-to-date.
1> All outputs are up-to-date.
This also silences Visual Studio's incessant complaining every time you run the program that it needs to rebuild things, like it does with plain pre- and post- build steps.
Hope this helps someone -- took me frickin forever to figure out.