2

With Makefiles I'm used to being able to write things like:

includedir=$(shell pg_config --includedir)/server

to run an external program, pg_config, with argument(s) --includedir, and include the result in a variable or as part of a variable. So if pg_config --includedir output /usr/include to stdout, the value of includedir would become:

includedir=/usr/include/server

Is there any way to do the equivalent with a Visual Studio project? Run a command, get the result, and substitute it into a property?

I find myself having to edit properties pages all over the place - changing the include directories and library directories for both the x86 and x64 configurations of a project whenever I want to build an extension against a different PostgreSQL version. It is intensely frustrating.

I want to be able to put something like this into Configuration Properties -> C/C++ -> General -> Additional Include Directories:

%(shell pg_config --includedir)

or even better:

%(shell %(PG_CONFIG) --includedir)

where %(PG_CONFIG)'s location is defined in a single place for each platform in the project.

So I'm looking for at least user-defined macros, and preferably the ability to invoke a command line tool and replace the macro with the resulting standard output.

(Preferably in a way that doesn't involve delving into semi-documented UI elements that move and get renamed in every VS version, and that appear and disappear from the various Express editions).

This has been possible in Makefiles for 20 years, there must be a way to do it in VS, right? Or do "Real Windows Developers" generate their VS projects with scripts and build them using MSBuild?

I've looked at some similar questions without finding much of use, e.g.:

In particular, I'm aware of property sheets (View -> Other Windows -> Property Manager), but they don't seem to provide a way to set a value in just one place, they're still per-configuration and per-architecture, so if you have four configurations and two architectures it gets awkward. Unlike with the normal project property editor you can't even apply a change across a group of architectures/configurations, either.

I could use a VS extension, but they require installation into the user's VS, can be version-specific, and seem like a very big hammer for a small problem.

Community
  • 1
  • 1
Craig Ringer
  • 307,061
  • 76
  • 688
  • 778
  • It already works that way, VS readily supports arbitrarily named configurations with settings defined in one place. Very hard to guess at the hangup, sounds like you want to apply a Unix hammer to a Windows nail. There's plenty of documentation available, but geared towards users that are new to this. Well, like you. Maybe you need to struggle through this for another few days before you "get it". It is very gettable. Consider CMake, it is probably more compatible with what you're used to, typing text into a file rather than a text box. – Hans Passant Sep 12 '14 at 10:05
  • @HansPassant I'm familar with CMake, and it works, but for this purpose I'm hoping to keep it simple. I'm aware that you can define configurations, and use that feature. I also know you can apply a setting to all configurations - which copies it, rather than adding it by inheritance. Settings can also be inherited from the parent project/solution. None of that, though, explains how to set a value in *one place* and reference it throughout. Is there some "custom user macros" properties page/dialog that's just well hidden? Do you rely on environment variables? What? – Craig Ringer Sep 12 '14 at 11:30
  • @HansPassant It's also entirely possible it's just crippled out of VS Express 2012. I've gone looking for user-defined macros, and while I found "Property Sheets", they're still per-target-platform and per-configuration, so you still land up having to set something in 4 or 8 different places. It also requires extracting info from a command and copying it into the project each time, if the authoritative source of the information is a command-line program. Can't you use VBScript/VBA or something in VS properties? *something* to interact with the outside world? – Craig Ringer Sep 12 '14 at 11:31
  • @HansPassant You might be interested in the answer jdlugosz has provided, and my notes there. In short, it's possible with "property functions" and most of what's needed is provided by property sheets when used properly. – Craig Ringer Sep 16 '14 at 05:13

2 Answers2

1

I find myself having to edit properties pages all over the place

That bugged me to no end as well. Property sheets to the rescue! When setting up a major solution in VS10, for example, I had every project pull in a settings.props that contained the common settings, made in only one place. Then go through all the generated or imported projects and kill any explicit value (even if blank) for everything possible. That way things will inherit from property sheets. Select "all configurations" and on each properly use the drop-down to "inherit from...".

I have property sheets for each special library too, just defining the proper #define, include paths, lib paths, etc. Projects that use that particular external lib simply use that property sheet. Users are told, in the worst case, to “edit the XML to change the path to where you have Boost”.

As for setting such a properly to a dynamic determined value, you can do that too. There are property functions you can use.

It sounds like you're going down the same path as I did.

More notes: “prop sheets are per configuration/platform”: If you include a prop sheet at the top-level node for the project itself (not the Debug|Win32, etc. child nodes) it will include it into all current configurations at once. If you edit the properly page, you can choose Multiple or All configurations on the Property dialog box, just as with the usual project use of the Property dialog.

“Custom user macros are well hidden” A property page shows up for that when in a property sheet you created, but not when opening property dialog on a proj file as in the normal File View. The macro will be set in one place (the prop page) and usable as a $(name) in all projects that include it, and even in other property pages that come later in the evaluation sequence.

Let me know how it goes. You should be able to do everything you asked.

—John

snipsnipsnip
  • 2,268
  • 2
  • 33
  • 34
JDługosz
  • 5,592
  • 3
  • 24
  • 45
  • That all makes sense, except that at least on VS 2012 Express I don't seem to be able to edit the property sheet for all configurations / platforms at once. The configuration and platform pulldowns are greyed out when editing the property sheet. frustrating. – Craig Ringer Sep 16 '14 at 02:24
  • Property functions look like what I was looking for. That was ... well hidden. – Craig Ringer Sep 16 '14 at 02:26
  • More refs on property functions: http://blogs.msdn.com/b/visualstudio/archive/2010/05/05/msbuild-property-functions-2.aspx, http://blogs.msdn.com/b/visualstudio/archive/2010/04/02/msbuild-property-functions.aspx . Overall they look to be a bit limited, a bit ugly, somewhat incomplete, but probably useful. – Craig Ringer Sep 16 '14 at 02:30
  • ... and posted a second answer covering what seems to be the accepted way to do this before the advent of property functions. All in all, "what a mess". It's like they didn't learn anything from the mistakes of Apache Ant when designing MSBuild. – Craig Ringer Sep 16 '14 at 02:44
0

In addition to @jdlugosz's answer:

It looks like the traditional way to do this with Visual Studio, before the advent of property functions, was to write a new MSBuild Task. The task can potentially do things like modify a property sheet.

MSBuild supports "inline tasks" where the task code is in the MSBuild project file, rather than a separate assembly, so it might not be neccessary to create a new subproject just for the task.

There are a bunch of built-in tasks, like Exec and CreateProperty that may be useful.

The docs say that:

[The Exec task] is useful when a specific MSBuild task for the job that you want to perform is not available. However, the Exec task, unlike a more specific task, cannot gather output from the tool or command that it runs.

... but that seems to be outdated/wrong so you don't need horrible workarounds.

So, prior to .NET 4.5 I'd probably have to write a custom task for this simple job, because there's no way to feed the command stdout/stderr into the CreateProperty task or have Exec create a property directly. But in 4.5 it looks like I can do it directly. At least in VS Express support for tasks etc is very limited so you'll probably land up editing the XML.

Community
  • 1
  • 1
Craig Ringer
  • 307,061
  • 76
  • 688
  • 778
  • I think in .NET 4.0 (VS2010) the Exec task can take output parameters. I'm not sure what you feed to the output and how, so maybe it's incomplete. Since it has regular expressions to control which lines of output are Warnings/Errors and returns the encoding of the captured stream, you'd think there's a way to refer to the captured stream itself. – JDługosz Sep 16 '14 at 03:20