4

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.

David Krider
  • 886
  • 12
  • 27
  • We add configurations to the projects for each environment and setup transforms to set the connection strings and other settings. Also look into using SlowCheetah. – JerryM Apr 22 '16 at 18:54
  • You should use a web.config or app.config to store environment-specific variables like connection strings. – Daniel Mann Apr 23 '16 at 04:48
  • I appreciate the suggestions to use app.config, but then I have a similar problem: how do I set the values in THAT file via msbuild.exe? – David Krider Apr 23 '16 at 11:36

1 Answers1

0

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:

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.

Community
  • 1
  • 1
jessehouwing
  • 106,458
  • 22
  • 256
  • 341
  • I tried SlowCheetah when I started this project almost 2 years ago. I just tried it again, with the latest VS, and I still get the same problem: I don't see the created files within VS. They get created on the file system, but the VS file explorer ignores them. That makes working with them sub-optimal. – David Krider Apr 23 '16 at 20:06
  • I really like the thought of using the ReplaceTokens VSTS task. That seems like a clean approach. However, I just installed it, and added it to my build definition, but I can't work out how to define the tokens and replacement strings. There seems to be no UI for this; just prefix and suffix. – David Krider Apr 23 '16 at 20:10
  • I can define the variables, but where do you define what to replace them with? Can I feed that from msbuild.exe? – David Krider Apr 24 '16 at 01:17
  • Yes, I can add them, but they still won't show up in the explorer, which makes me think it's not working right. The project thinks I have them included, which makes them sort of invisible. That puts me off of it. I mean, if I'm going to use a GUI, then I want to use a GUI. One or the other. – David Krider Apr 24 '16 at 01:30
  • You define the values in the build variables screen as well. I'm beginning to think you want something else and that we're not on the same wavelength... – jessehouwing Apr 24 '16 at 06:36
  • Not sure what issues you have with slow cheetah, that's probably worth a question on its own. – jessehouwing Apr 24 '16 at 08:47
  • 1
    As for the replacement, I think I understand now: I need to put bracketed (`#{}#`) tokens in the code, and the build variables screen is the place to define the substitution. And, yeah, maybe I should post another question about SlowCheetah. – David Krider Apr 24 '16 at 12:28