Include file to VS2017 c++ project by condition - msbuild

I am trying to build the c/c++ project in VS2017
[https://git.postgresql.org/gitweb/?p=psqlodbc.git;a=blob;f=winbuild/psqlodbc.vcxproj;h=c54c93007c07c2b13bbea4ede14a6ee0e11fdf5a;hb=c54c93007c07c2b13bbea4ede14a6ee0e11fdf5a][1]
There are conditions in the project file
<ClCompile Include="$(srcPath)odbcapi30.c" />\r
<ClCompile Condition="'$(ANSI_VERSION)'=='no'" Include="$(srcPath)odbcapi30w.c" />\r
<ClCompile Condition="'$(ANSI_VERSION)'=='no'" Include="$(srcPath)odbcapiw.c" />\r
I have created
Unicode Debug/Release
ANSI Debug/Release
configurattions
and in the project properties->c\c++->Preprocessor I have added the ANSI_VERSION=no for Unicode and ANSI_VERSION=yes for ANSI.
But for any Platform/Configuration I see these files in the Solution Explorer and they are compiled by VS2017. How to include these files into project when condition is true only?

The condition requires that ANSI_VERSION is a MSBuild property. These are different from the C++ Preprocessor definitions (which are inputs used when compiling a file but not used by MSBUILD when testing for which files to compile - strictly speaking its used by the pre-processor but its part of the compile step from an msbuild point of view)
You can set the ANSI_VERSION as an MSBUILD property in your project file:-
For example:-
<PropertyGroup>
<ANSI_VERSION>no</ANSI_VERSION>
<ANSI_VERSION Condition="'$(Configuration)' == 'ANSI_DEBUG'">yes</ANSI_VERSION>
<ANSI_VERSION Condition="'$(Configuration)' == 'ANSI_RELEASE'">yes</ANSI_VERSION>
</PropertyGroup>
The above defaults ANSI_VERSION to no and overrides to yes when condition is met, but you could just as well test each possible configuration in turn instead if you prefer.
The conditions could also be combined into a single condition with an or if you prefer.
Personally I'd use true/false rather than yes/no. With true false you can just test the property as a Boolean rather than compare to string (although maybe this also works with yes/no - but I haven't tried that)
Edit in response to question:
The above conditionally excludes the files from the build, excluding them from the display is a little different as it would require the UI to re-parse the projects to update the list of files. You may find things works better for you to create a filter in the project for these files (i.e. right click on project in solution view and use Add->New Filter). Then conditionally use the ExcludeFromBuild setting to control which configurations actually compile them instead of making the CLCompile include conditional, something like:-
<ClCompile Include="SomeFile.cpp">
<ExcludedFromBuild Condition="'$(Configuration)'=='Debug'">true</ExcludedFromBuild>
</ClCompile>

Related

In VS2022, how do I specify a conditional compilation symbol for a build configuration?

In VS2017, I had several different build configurations that built an application in different ways. One configuration would produce the default application. Another build configuration would produce the application with more features, etc.
This was done in the source code with #if FEATURE blocks. FEATURE was defined in the Conditional compilation symbols for a project's build configuration.
Now, I ported the code to Visual Studio 2022. It appears that the Conditional compilation symbols are now part of the project and not part of the build configuration. So I have to define FEATURE for the project and not the build configuration.
I've used #if FEATURE to put in attributes to classes and methods, so I can't replace this with a simple if statement in the source code.
I don't want to change the project settings every time I need to build the different applications.
What is the workaround for being able to build a project with different compilation symbols easily?
Realise this is a year old now, but I've been looking at conditional compilation symbols this morning.
First, have a look at this: https://learn.microsoft.com/en-gb/dotnet/csharp/language-reference/compiler-options/language
The <DefineConstants> section deals with conditional compilation.
Edit the .csproj file directly, and in any applicable property group you can define constants that you can reference in code. e.g.
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<DefineConstants>MYDEBUGCONSTANT</DefineConstants>
</PropertyGroup>
Then in code you can use:
#if MYDEBUGCONSTANT
// Some debug code
#endif
I had some issues with the conditions on the property groups, VS2022 got a little confused when two of the property groups applied at the same time. Once I sorted that everything worked as expected.
Hope that helps?

Target not running when using BeforeTargets="Build" on Build Server

I have a custom .targets file which I import into my C# MVC web application's project file. I've added custom targets to this like so:
<Target Name="CopyFiles" BeforeTargets="Build"></Target>
This works fine when building under Visual Studio, but when I use TeamCity to build it, the target never gets run, and I can't work out why.
If I change my target to use BeforeTargets="Compile" then it runs. Alternatively, if I add an additional target with the name Build to the .targets file
<Target Name="Build" />
then it will run, but doing so overrides the existing Build target and thus my application doesn't build. I can't quite make out the logic to this - it doesn't make sense. I'm using the Compile target for now, but if someone could explain why trying to execute it before the Build task doesn't work I'd really appreciate it.
'Build' is a special built-in target, so doesn't really work the same way as most other targets. It definitely can't be safely overridden.
The most relevant documentation is here: https://msdn.microsoft.com/en-us/library/ms366724.aspx
If you want something to run before build, the standard approach (as recommend by the comments in a newly-created .csproj file) is to override the BeforeBuild target (as documented above).
However, this isn't the most robust solution. As noted in the documentation above:
Overriding predefined targets is an easy way to extend the build process, but, because MSBuild evaluates the definition of targets sequentially, there is no way to prevent another project that imports your project from overriding the targets you already have overridden.
It's better (and only slightly more complex), to override the BuildDependsOn property and extend the default value of this property to include the target you want to run (this is also documented in the link above).
Another approach would be to leave BeforeBuild empty and use BeforeTargets="BeforeBuild", which feels a bit odd but is quite simple and will still work even if the BeforeBuild target gets overridden.
As to why BeforeTargets="Build" doesn't work, I can't find a reference for this in the documentation, but I think it's to do with its special nature. It doesn't work the same as ordinary targets and it's probably better not to think of it as a target at all.

MSBuild: Generate XML documentation for main project but not dependency projects

I have a .sln file with several projects in it. To keep this simple, let's call them...
ProjectA
ProjectB
ProjectC
...where A is the main project which references B and C. My goal is to update my build script to generate an XML "Intellisense" documentation file for ProjectA, without giving build warnings about missing documentation from B and C.
Current Build Script
I have an MSBuild script which includes the following in the build step:
<PropertyGroup>
<CustomOutputPath>C:\build\output\</CustomOutputPath>
</PropertyGroup>
<ItemGroup>
<Projects Include="ProjectA\ProjectA.csproj">
<Properties>OutputPath=$(CustomOutputPath)</Properties>
</Projects>
</ItemGroup>
<MSBuild Projects="#(Projects)" />
(There are actually multiple Projects listed in the ItemGroup, but again, let's keep this simple.)
When I run the build script, it's smart enough to compile B, C, and A for me, even though I've only specified A. All output appears in the "CustomOutputPath" location.
The closest I've gotten...
If I add a 'DocumentationFile' property to my Project entry...
<ItemGroup>
<Projects Include="ProjectA\ProjectA.csproj">
<Properties>OutputPath=$(CustomOutputPath);DocumentationFile=ProjectA.xml</Properties>
</Projects>
</ItemGroup>
...then 'ProjectA.xml' appears in "CustomOutputPath". However, I also get files named 'ProjectA.xml' in the project folder for all three projects:
ProjectA/ProjectA.xml
ProjectB/ProjectA.xml
ProjectC/ProjectA.xml
These files contain the "Intellisense" documentation for their respective projects, even though they're all named "ProjectA.xml".
This creates undesired and misleadingly-named files in the project folders, and (more importantly) generates build warnings for the missing documentation comments in B and C. I don't want to add documentation comments to those projects, so I'd prefer to find a way to have MSBuild generate the documentation only for ProjectA.
Can anyone provide any insight, or an alternative solution?
Based on what I've found - DocumentationFile is a global-level property (and will be used in creation of DocFileItem - global level items list). From my understanding you won't be able to alter it in any easy way in a single logical script.
What you can do is to define special target in separate file that will be imported to every proj file (directly editing proj files or using properties like $CustomBeforeMicrosoftCommonTargets) that will overwrite DocumentationFile with project-dependent value.
As a result - you probably can generate different documentation file names for different projects.
Another solution - just clean-up all unnecessary doc files right after all projs were built.

Tfs2010 Build Number and Assembly File Versions & MSBuild targets

I read John Robbins' article TFS 2010 Build Number and Assembly File Versions: Completely In Sync with Only MSBuild 4.0, and I'm wondering about the best way to go about integrating this.
The download for the article has two files, one is a targets file and one is a proj file.
The targets file has a number of tasks to scrape out a build number based on the Tfs build number (the same one used for the builds) and write that number out to some location (call it BuildNumberFile) for consumption by other proj files.
The proj file is very simple. It just imports the aforementioned targets file, and then declares a target with name "All" while also declaring DefaultTargets on the Project element to be All as well.
<Project ToolsVersion="4.0" DefaultTargets="All" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- The two required properties so the Wintellect.TFSBuildNumber tasks knows your major and minor values.-->
<TFSMajorBuildNumber>3</TFSMajorBuildNumber>
<TFSMinorBuildNumber>1</TFSMinorBuildNumber>
</PropertyGroup>
<Import Project="Wintellect.TFSBuildNumber.targets"/>
<!-- Just ask for the version information files you need. These are here to show all the diffent ones in
Wintellect.TFSBuildNumber.Targets. You can change the names -->
<Target Name="All"
DependsOnTargets="WriteSharedCSharpAssemblyVersionFile;
WriteSharedVBAssemblyVersionFile;
WriteSharedCPPCLIAssemblyVersionFile;
WriteSharedCPPAssemblyVersionFile;
WriteSharedWiXAssemblyVersionFile;
WriteSharedTextAssemblyVersionFile;"/>
</Project>
I have two questions about this:
I'm still learning MSBuild. If the name of the target isn't specified elsewhere in the targets, is the target executed? How do I ensure that this target is run?
Are the csproj files supposed to declare an Include item for the location where BuildNumberFile is, even though it doesn't exist until compiletime?
Do ItemGroups and Include have a DependsOnTargets or something that allows them make sure the file exists before they build?
Are the entire contents of the csproj file using this supposed to be wrapped in a target that expresses DependsOnTargets for BuildNumberFile?
Thanks!
I think I've got this figured out, but two people promoted my question so I'll answer it here:
You can ensure that a target is run by expressing a dependency on it from another target. Microsoft.Common.targets exposes two targets--BeforeBuild and AfterBuild--expressly for the purpose of being overridden for customizability. I found the easiest way to do this was <Target Name="BeforeBuild" DependsOnTargets="WriteSharedCSharpAssemblyVersionFile" /> where WriteSharedCSharpAssemblyVersionFile is the target declared in the download from the link in the original post. Also, if you're new to MSBuild, this BeforeBuild target must be declared after the Microsoft.CSharp.targets is imported, but the default csproj template guides you in doing this.
The WriteSharedCSharpAssemblyVersionFile target should indeed write the file to some central location, since when building a solution, all targets are executed only once. All projects should reference the file from that location even if it doesn't exist, since by the time compilation happens (or more importantly, by the time references are resolved), the BeforeBuild target will have run and the file will be in place.
In my structure, I have these versioning files in a folder directly underneath the branch root folder. Furthermore, since the file being built is generated, I have it build to the output directory. It seems a little strange to be referencing things from the output, but it preserves the invariant of having all build products in one place so that the output directory can be blown away as a means of performing a clean.
In MSBuild items constitute inputs into the system (usually files) so it's weird to think of them depending on targets. After some learning this question doesn't make a lot of sense. In any case, the answer is no.
The entire contents of the file should indeed not be all in one target--all that is required is to import the Wintellect.TFSBuildNumber.targets file at the beginning of your csproj file, and declare BeforeBuild's dependency on WriteSharedCSharpAssemblyVersionFile at the end.
Hope this helps!

Can you do parallel incremental building on a list of projects?

Is it possible to take a list of projects and parallel build them only if they aren't up to date?
<Target Name="DependenciesLevel7" DependsOnTargets="DependenciesLevel6">
<Message Text="5 items to build" />
<MSBuild Projects="C:\Projects\ApplicationManager.csproj;C:\Projects\Metrics.csproj" Properties="$(CustomAllProperties)" BuildInParallel="true">
<Output TaskParameter="TargetOutputs" ItemName="built_DependenciesLevel7" />
</MSBuild>
This is an example of the format i'm building in, I was hoping to be able to parallel build only items that aren't up to date here? Perhaps internal msbuild task calls are automatically parallel? If so how would I set this up so that it's incremental based on the previous build task? (Target DependenciesLevel6) I believed for incremental building you have to use Inputs/Outputs in your target.
Question summary:
Is the list of projects passed to an msbuild task automatically incremental (building is skipped if the build is already up to date)?
If not, is it possible to do incremental on a list of projects while parallelizing?
Can you do between target incremental where each target is a parallel build?
what you are describing "parallel incremental building" is already built in (kind of) but what you really need is parallel partially building.
Let me first explain the concepts and then I'll circle back how it works in your scenario specifically. There are two things you need to know about; incremental building and partial building. I just wrote a blog post discussing this at http://sedodream.com/2010/09/23/MSBuildYouveHeardOfIncrementalBuildingButHaveYouHeardOfPartialBuilding.aspx but I'll paste the relevant parts here.
Incremental building is the concept that you should only build what is out of date. To support this MSBuild has the attributes, inputs and outputs on the Target element. With these attributes you can specify the files that go into a target (via inputs attribute), and the files that you are expecting to come out of a target (via outputs attribute). Once you do this MSBuild will compare the timestamp of the inputs to the outputs and if all outputs are up-to-date (i.e. the inputs are older) then the target will be skipped. Take a look at the very simple project file below.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Files Include="src\01.txt;src\02.txt;src\03.txt;src\04.txt;src\05.txt;"/>
</ItemGroup>
<PropertyGroup>
<Dest>dest\</Dest>
</PropertyGroup>
<Target Name="CopyFiles"
Inputs="#(Files)"
Outputs="#(Files->'$(Dest)%(Filename)%(Extension)')">
<Message Text="CopyFiles" />
<Copy SourceFiles="#(Files)"
DestinationFiles="#(Files->'$(Dest)%(Filename)%(Extension)')"/>
</Target>
<Target Name="DeleteTwoFiles">
<Message Text="DeleteTwoFiles" />
<Delete Files="$(dest)01.txt;$(dest)02.txt"/>
</Target>
</Project>
In this project file we have two targets; CopyFiles and DeleteTwoFiles. Ignore DeleteTwoFiles for now. Also take a note that the directory where I’m executing this build has a folder, src, with the files listed in the Files item. On the CopyFiles target I have specified the inputs and outputs. The inputs is just #(Files), this are the files that the target is acting upon. The outputs contains the expression #(Files->'$(Dest)%(Filename)%(Extension)'). Which is the same expression from the Copy statement. If the Dest folder is empty and I execute the CopyFiles target the result is shown below.
So just as expected the files were copied over, so its all good. Now what happens if I execute it again? The output is shown below
So as you can see the target was skipped, the message statement “CopyFiles” was not executed nor was the copy as a result. So this, in a nutshell, is incremental building.
Now, with the dest folder containing all files, what do you think would happen I execute the command msbuild.exe PartialBuilding01.proj /t:DeleteTwoFiles;CopyFiles? This command will first delete two files from the output directory and then call the CopyFiles target again. Let’s see the result below.
When the CopyFiles target was executed you see that statement “Building target ‘CopyFiles’ partially, …”. When the time came to execute the target MSBuild examined the inputs and outputs, it determined that the files 01.txt & 02.txt were out of date (because they didn’t exist in the target) but 03.txt, 04.txt and 05.txt were up to date. So MSBuild feed the CopyFiles target a value for the Files item that only contained the 01.txt and 02.txt and let it do its thing.
Now this relates to your problem in many ways some not as direct as you might hope. Firstly MSBuild will incrementally build your project, so if your project is up to date then it will not be built again. The thing is though that in order for MSBuild to determine that your project is up to date it has to load the project run the default target (usually Build) and then the targets themselves will figure out that there is no work to do. This stuff itself takes time. So if you have a huge number of projects, or a huge number of files inside of a project then you can take matters into your own hands. What you need is a way to determine if your projects are up to date or not and correctly express that inside of your inputs and outputs attributes. Once you do this you should be able to skip building the projects which are up to date.
The core of the problem is how do you craft the inputs/outputs to be correct. If you can think of a way to do that then you will get what you want. How you craft this will depend on your scenario but I could see something like this:
After each project build drop a file to a known location that is specific to that project
Before you build a project scan its directory, find the newest file and then update the timestamp of the project file to be that value
Then you can place the project files as the Inputs values and the marker files as the Outputs
Then call your target
In this case you assume that all dependencies are fully contained in files under the directory of the project (which may not be true). I'm not saying this is the ideal solution, but a solution.
==============================================
Edit: Update based on questoins below.
You will want to put the projects into an item (though not required) like ProjectFiles and then use #(ProjectFiles) for inputs. For outputs that is what I was saying is the hard part. You have to figure out a way to know (or indicate to you via your own process) that the projects are up to date. There is nothing built in for this.
Concern fo incremental build vs. clean build. In a perfect world incremental & clean builds are the same. But sometimes that is not the case. For many projects it is. If you start adding a bunch of targets to your build process and you set them up to do incremental build, but you do not implement that properly then you may MSBuild may skip targets when they were indeed out of date. A good example of this would be when you create a target with Inputs set to a list of files and then the Outputs set to a list of created files. If you do not extend the clean process to delete those created files, then the next time you Rebuild (assuming you didn't change the files) the target will be skipped when it should have been cleaned on the previous Rebuild.
You're probably looking for IncrediBuild.
I'm not sure about the incremental part but I read that it checks inputs/ouputs.
I make parallel builds with MsBuild with a configuration similar to yours, don't forget to run msbuild with the /m option.
You can try on a simple project and check the logs to see if it's incremental or not.