MSBuild Shorten Configuration Managment In vcxproj-Files - msbuild

Windows Version: Microsoft Windows [Version 10.0.14393]
MSBuild Version : Microsoft (R)-Buildmodul, Version 15.1.1012.6693
The project is written in C.
In the *.vcxproj files of this project there is a lot of code in like this
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
[do some stuff]
</ImportGroup>
for all configurations, 'Debug|Win32', 'Release|Win32', 'Debug|x64', 'Release|x64'. But I will have the same configuration for all combinations, therefore I do not want to write it 4 times making my project file 3 times longer and less readable .
Is there a shortcut like Condition="'$(Configuration)|$(Platform)'=='Any Configuration|Any Architecture'?

The standard way of doing this would be using 'property sheets'; more concrete: one property sheet with the common options which gets imported by all platform/configuration combinations. Some reasons to choose this approach:
it exactly addresses your "I will have the same configuration for all combinations, therefore I do not want to write it 4 times making my project file 3 times longer and less readable" requirement, and more: it keeps the common options in one single file, which can also be resued by other projects (which is really the number 1 selling point if you have multiple projects and want the same options for them)
it has user interface support for editing (though it's no problem if you'd want to manually edit the vcxproj to add it)
it keeps the standard project structure intact, so still allows for per-configuration and per-platform modifications should you need those
property sheets are just msbuild files like any other and as such can Import other files so you can create hierarchies with them, do things like having one master file which based on application type (exe/dll) sets different output paths and so on

You can remove the Condition attribute and have the ImportGroup being applied for every configuration.

Related

Is is possible to pass a variable from the build process to Visual Basic code?

My goal is to create build definitions within Visual Studio Team Services for both test and production environments. I need to update 2 variables in my code which determine which database and which blob storage the environment uses. Up till now, I've juggled this value in a Resource variable, and pulled that value in code from My.Resources.DB for a library, and Microsoft.Azure.CloudConfigurationManager.GetSetting("DatabaseConnectionString") for an Azure worker role. However, changing 4 variables every time I do a release is getting tiring.
I see a lot of posts that get close to what I want, but they're geared towards C#. For reasons beyond my influence, this project is written in VB.NET. It seems I have 2 options. First, I could call the MSBuild process with a couple of defined properties, passing them to the .metaproj build file, but I don't know how to get them to be used in VB code. That's preferable, but, at this point, I'm starting to doubt that this is possible.
I've been able to set some pre-processor constants, to be recognized in #If-#Else directives.
#If DEBUG = True Then
BarStaticItemVersion.Caption = String.Format("Version: {0}", "1.18.0.xxx")
#Else
BarStaticItemVersion.Caption = String.Format("Version: {0}", "1.18.0.133")
#End If
msbuild CalbertNG.sln.metaproj /t:Rebuild /p:DefineConstants="DEBUG=False"
This seems to work, though I need to Rebuild to change the value of that constant. Should I have to? Should Build be enough? Is this normal, or an indication that I don't have something set quite right?
I've seen other posts that talk about pre-processing the source files with some other builder, like Ant, but that seems like overkill. It feels like I'm close here. But I want to zoom out and ask, from a clean sheet of paper, if you're given 2 variables which need to change per environment, you're using VB.NET, and you want to incorporate those variable values in an automated VS Team Services build process upon code check-in, what's the best way to do it? (I want to define the variables in the VSTS panel, but this just passes them to my builder, so I have to know how to parse the call to MSBuild to make these useful.)
I can control picking between 2 static strings, now, via compiler directives, but I'd really like to reference the Build.BuildNumber that comes out of the MSBuild process to display to the user, and, if I can do that, I can just feed the variables for database and blob container via the same mechanism, and skip the pre-processor.
You've already found the way you can pass data from the MsBuild Arguments directly into the code. An alternative is to use the Condition Attribute in your project files to make certain property groups optional, it allows you to even include specific files conditionally. You can control conditions by passing in /p:ConditionalProperty=value on the MsBuild command. This at least ensures people use a set of values that make sense together.
The problem is that when MsBuild is running in Incremental mode it is likely to not process your changes (as you've noticed), the reason for this, is that the input files remain unchanged since the last build and are all older than the last generated output files.
To by-pass this behavior you'd normally create a separate solution configuration and override the output location for all projects to be unique for that configuration. Combined with setting the Compiler constants for that specific configuration you're ensured that when building that Configuration/Platform combination, incremental builds work as intended.
I do want to echo some of the comments from JerryM and Daniel Mann. Some items are better stored in else where or updated before you actually start the compile phase.
Possible solutions:
Store your configuration data in config files and use Configuration Transformation to generate the right config file base don the selected solution configuration. The process is explained on MSDN. To enable configuration transformation on all project types, you can use SlowCheetah.
Store your ocnfiguration data in the config files and use MsDeploy and specify a Parameters.xml file that matches the deploy package. It will perform the transformation on deploy time and will actually allow your solution to contain a standard config file you use at runtime, plus a publish profile which will post-process your configuration. You can use a SetParameters.xml file to override the variables at deploy time.
Create an installer project (such as through Wix) and merge the final configuration at install time (similar to the MsDeploy). You could even provide a UI which prompts for specific values (and can supply default values).
Use a CI server, like the new TFS/VSTS 2015 task based build engine and combine it with a task that can search&replace tokens, like the Replace Tokens task, Tokenization Task, Colin's ALM Corner Build and Release Tasks. And a whole bunch that specifically deal with versioning. Handling these things in the CI server also allows you to do a quick build locally at all times and do these relatively expensive steps on the build server (patching source code breaks incremental build in MsBuild, because there are always newer input files.
When talking specifically about versioning, there are a number of ways to set the AssemblyVersion and AssemblyFileVersion just before compile time, usually it involves overriding the AssemblyInfo.cs file before compilation. Your code could then use reflection to read the value at runtime. You can use the AssemblyInformationalversion to specify something like you do in the example above which contains .xxx or other text. It also ensures that the version displayed always reflects the information obtained when reading the file properties through Windows Explorer.

Visual Studio 2010 solution - how copy common exe to two main projects?

Situation: An application, that is built into several variations, with different functionality enabled. Each variation has its own main project, with its own output "bin" folder. Call the variations A and B.
There are various common class libraries, which generate dlls. Those all get automatically copied to both output folders. This is working fine.
Now, add another project. It generates an exe, C.exe. It will be loaded as a separate process by the application. (It creates a NamedPipe, providing a ServiceContract via NetNamedPipedBinding.)
When there was only "A" (no "B"), I simply had A and C specify the same output folder.
But now there are TWO places that C needs to go.
For Debug build, must go to A/bin/Debug and B/bin/Debug. Similar for Release build.
The source language is VB, but an answer based on C# projects would almost certainly be adaptable to my situation.
I've written an answer by using XCopy in Post Build Events.
Looking for alternative answers.
Is there a way that is easier to maintain / not dependent on manually entering paths?
My concern is that as variants are added, or moved around, it is necessary to know about the Build Events and manually edit them.
Doing work for a company that is not great at keeping track of such details over the years.
Looking for a way that is less likely to break, or easier for a junior programmer to maintain.
In Project "C", Properties, Post Build Events, use xcopy to copy the .exe and corresponding .pdb. Do this for both the "Debug" configuration and the "Release" configuration of Project C.
(For VB, this is under Compile tab, "Build Events..." button at lower right.)
In project A's Properties, find what its output path is. in this case, the path specified was bin\x86\Debug\. xcopy needs to be told this path, relative to project A inside the solution; this becomes $(SolutionDir)\A\bin\x86\Debug\. The resulting lines, to copy both the exe and its pdb for debugging:
xcopy /y "$(ProjectDir)$(OutDir)*" "$(SolutionDir)\A\bin\x86\Debug\"
This copies ALL files in "C"s output folder to "A"s output folder.
Before adding this line, examine "C"s output folder. Are there any files in it that should not be copied to A? If so, can they be deleted? (And will they STAY deleted, when you rebuild "C"?) If not, you will need to specify more carefully the source files, in xcopy line. Or use multiple xcopy lines, to specify the individual files.
Note the "A", which is the main project being copied to.
Repeat that line for "B".
As more variants of main project are created, add a line for them as well.
If one project specifies a different output path, then the lines need to be correspondingly changed.

Comparison of files of two folders

I want to compare files of two folders in vb.net with respect of date time and size and put the odd ones in two list boxes for each folder. I am not getting through the logic. Can any one of you help me with logic or code?
Thank you.
Get the FileInfo for each file in each of the directories.
Compare the required properties of each.
Consider what to do if there are files in one directory which are not in the other.
Question scope is too wide to be covered on StackOverflow.
However, there is an open source project called DirComp.NET.
It looks like a good starting point for you.
DirComp.NET is a simple command-line tool written in VB.NET, which compares two directories, and either lists differences or mirrors one directory onto another. For example, it can be used to backup large file repository by maintaining a mirror copy. It lives well with existing Windows infrastructure, which already provides file shares. And finally, DirComp.NET is based on .NET Framework 2.0, which is available from Windows 2000 and up.

Batch rename with MSBuild

I just joined a team that has no CI process in place (not even an overnight build) and some sketchy development practices. There's desire to change that, so I've now been tasked with creating an overnight build. I've followed along with this series of articles to: create a master solution that contains all our projects (some web apps, a web service, some Windows services, and couple off tools that compile to command line executables); created an MSBuild script to automatically build, package, and deploy our products; and created a .cmd file to do it all in one click. Here's a task that I'm trying to accomplish now as part of all this:
The team currently has a practice of keeping the web.config and app.config files outside of source control, and to put into source control files called web.template.config and app.template.config. The intention is that the developer will copy the .template.config file to .config in order to get all of the standard configuration values, and then be able to edit the values in the .config file to whatever he needs for local development/testing. For obvious reasons, I would like to automate the process of renaming the .template.config file to .config. What would be the best way to do this?
Is it possible to do this in the build script itself, without having to stipulate within the script every individual file that needs to be renamed (which would require maintenance to the script any time a new project is added to the solution)? Or might I have to write some batch file that I simply run from the script?
Furthermore, is there a better development solution that I can suggest that will make this entire process unnecessary?
After a lot of reading about Item Groups, Targets, and the Copy task, I've figured out how to do what I need.
<ItemGroup>
<FilesToCopy Include="..\**\app.template.config">
<NewFilename>app.config</NewFilename>
</FilesToCopy>
<FilesToCopy Include="..\**\web.template.config">
<NewFilename>web.config</NewFilename>
</FilesToCopy>
<FilesToCopy Include"..\Hibernate\hibernate.cfg.template.xml">
<NewFilename>hibernate.cfg.xml</NewFilename>
</FilesToCopy>
</ItemGroup>
<Target Name="CopyFiles"
Inputs="#(FilesToCopy)"
Outputs="#(FilesToCopy->'%(RootDir)%(Directory)%(NewFilename)')">
<Message Text="Copying *.template.config files to *.config"/>
<Copy SourceFiles="#(FilesToCopy)"
DestinationFiles="#(FilesToCopy->'%(RootDir)%(Directory)%(NewFilename)')"/>
I create an item group that contains the files that I want to copy. The ** operator tells it to recurse through the entire directory tree to find every file with the specified name. I then add a piece of metadata to each of those files called "NewFilename". This is what I will be renaming each file to.
This snippet adds every file in the directory structure named app.template.config and specifies that I will be naming the new file app.config:
<FilesToCopy Include="..\**\app.template.config">
<NewFilename>app.config</NewFilename>
</FilesToCopy>
I then create a target to copy all of the files. This target was initially very simple, only calling the Copy task in order to always copy and overwrite the files. I pass the FilesToCopy item group as the source of the copy operation. I use transforms in order to specify the output filenames, as well as my NewFilename metadata and the well-known item metadata.
The following snippet will e.g. transform the file c:\Project\Subdir\app.template.config to c:\Project\Subdir\app.config and copy the former to the latter:
<Target Name="CopyFiles">
<Copy SourceFiles="#(FilesToCopy)"
DestinationFiles="#(FilesToCopy->'%(RootDir)%(Directory)%(NewFileName)')"/>
</Target>
But then I noticed that a developer might not appreciate having his customized web.config file being over-written every time the script is run. However, the developer probably should get his local file over-written if the repository's web.template.config has been modified, and now has new values in it that the code needs. I tried doing this a number of different ways--setting the Copy attribute "SkipUnchangedFiles" to true, using the "Exist()" function--to no avail.
The solution to this was building incrementally. This ensures that files will only be over-written if the app.template.config is newer. I pass the names of the files as the target input, and I specify the new file names as the target output:
<Target Name="CopyFiles"
Input="#(FilesToCopy)"
Output="#(FilesToCopy->'%(RootDir)%(Directory)%(NewFileName)')">
...
</Target>
This has the target check to see if the current output is up-to-date with respect to the input. If it isn't, i.e. the particular .template.config file has more recent changes than its corresponding .config file, then it will copy the web.template.config over the existing web.config. Otherwise, it will leave the developer's web.config file alone and unmodified. If none of the specified files needs to be copied, then the target is skipped altogether. Immediately after a clean repository clone, every file will be copied.
The above turned out be a satisfying solution, as I've only started using MSBuild and I'm surprised by its powerful capabilities. The only thing I don't like about it is that I had to repeat the exact same transform in two places. I hate duplicating any kind of code, but I couldn't figure out how to avoid this. If anyone has a tip, it'd be greatly appreciated. Also, while I think the development practice that necessitates this totally sucks, this does help in mitigating that suck factor.
Short answer:
Yes, you can (and should) automate this. You should be able to use MSBuild Move task to rename files.
Long answer:
It is great that there is a desire to change from a manual process to an automatic one. There are usually very few real reasons not to automate. Your build script will act as living documentation of how build and deployment actually works. In my humble opinion, a good build script is worth a lot more than static documentation (although I am not saying you should not have documentation - they are not mutually exclusive after all). Let's address your questions individually.
What would be the best way to do this?
I don't have a full understanding of what configuration you are storing in those files, but I suspect a lot of that configuration can be shared across the development team.
I would suggest raising the following questions:
Which of the settings are developer-specific?
Is there any way to standardise local developer machines so that settings could be shared?
Is it possible to do this in the build script itself, without having to stipulate within the script every individual file that needs to be renamed?
Yes, have a look at MSBuild Move task. You should be able to use it to rename files.
...which would require maintenance to the script any time a new project is added to the solution?
This is inevitable - your build scripts must evolve together with your solution. Accept this as a fact and include in your estimates time to make changes to your build scripts.
Furthermore, is there a better development solution that I can suggest that will make this entire process unnecessary?
I am not aware of all the requirements, so it is hard to recommend something very specific. I can say suggest this:
Create a shared build script for your solution
Automate manual tasks as much as possible (within reason)
If you are struggling to automate something - it could be an indicator of an area that needs to be rethought/redesigned
Make sure your team mates understand how the build works and are able to make changes to it themselves - don't "own" the build and become a bottleneck
Bear in mind that going from no build script to full automation is not an overnight process. Be patient and first focus on automating areas that are causing the most pain.
If I have misinterpreted any of your questions, please let me know and I will update the answer.

Dividing work of MSBuild into projects

I'm starting to create a MSBuild scripts for my products, and I've encounter a dilema.
The code is divided into around 25 projects, some wll require obfuscation, some will require strong-name signing; others will require linking into a single file.
All these projects should result in 3 products, with 3 setups.
The question at hand is as follow: How do I divide the MSBuild scripts to make most sense?
Do I create a script for each product? do I create a script for each project? Do I have one script for building, another for obfuscation and so on?
I think this is good idea to have script per product.
To minimaze dublication create reusable "sub-scripts" and import them to main script (this could be done with Import directive).
<Import Project="..\Steps\Step1.proj" />
Script per product sounds like the way to go. You may want to consider having any number of shared or base scripts too, to import common build steps. Like Mike Chaliy already mentioned you can then use Import in your product's build script:
<Import Project="..\Shared\Base.proj" />
Another thing you might also want to take advantage of is target and property overriding. It's akin to overriding virtual methods in a .Net class. See the documentation and the MSBuild Team Blog for more details. I know I've taken advantage of this quite often by setting defaults in the included scripts then overriding them as necessary in the product build script in order to customize build behaviour. For instance I often have generated files that are required before the build so I hook those targets into the BuildDependsOn property group. This way my generated files are generated whenever I do an F5 from the IDE, call the build target from the command line or otherwise build the project or solution. Obviously if you have any build steps that run long or only need to be run in special circumstances (like building installers), you'll want to take care about exactly what gets hooked in.