I want to create a nuget package that will add a custom msbuild property before PostBuildEvent property (because I need to use it inside post build event) and after imports (so that I can use TargetDir property) without the user being asked to reload the solution. Is this possible?
I tried using Set-MSBuildProperty from NuGet Power Tools, but that adds property in first property group; I also tried Get-MSBuildProject and "$buildProject.Xml.AddPropertyGroup().AddProperty()", but that adds new property group after immediately after last existing property group (but that may still be before imports). What's worse, both these methods result in an ugly dialog that asks whether changes to project files should be discarded or overwritten (both terms sound equally bad).
MSBuild does have a PreBuildEvent, which executes after linking is complete but before the actual build begins. Would that work?
Thanks,
Clay
Related
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.:
Visual Studio - Where to define custom path macros?
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.
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
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.
I know very little about MSBuild.
Recently I read that setting 'copy local' to false for large solution can significantly decrease compilation time.
I found a way to set the default setting to be false by setting a machine wide file called CustomAfterMicrosoftCommon.targets in the MSBuild extension path.
This worked well, however, there are a few problems with that approach.
-This default value setting is system wide and I would like to it to be only for a specific solution. I don't it at the project level because I have lots of them in several different solutions.
-I want this default value to be applied on to "Non Output projects". By this I mean that class library projects should not have copy local set to true. However, the main application project needs copy local to true for class libraries dependencies since it needs to run.
Ideally, for those output projects, I'd like to fallback to the more intelligent out of the box default value that is smart enough to determine whether a reference should be private or not. I guess I could have the default be Copy Local false, and then explicitely set Copy Local to true wherever it is necessary. That's not too bad given that you typically don't have many output projects but I'd like to see if some people have come up with better solution.
Any ideas ?
Msbuild finds the target extensions like this
%MSBuildExtensionsPath%\v4.0\CustomAfterMicrosoftCommon.targets
So one way to solve your first problem is to somehow only set MSBuildExtensionsPath for the solution where you want to apply your own defaults.
I solved the second problem in another way. I kept copylocal=false also on the "main application project". Instead I added the other project's output path to the DEVPATH environment variable.
SET DEVPATH=C:\Proj1\Output;C:\Proj2\Output
(and doing the necessary changes to machine.config as specified here)
Edit:
Here is nice page about how to make settings with project or solution scope
That the logical follow-up for the my previous question: "How to check all projects in solution for some criteria?"
I was given quite a good answer to use CustomAfterMicrosoftCommonTargets, CustomBeforeMicrosoftCommonTargets. They do work, so I decided not to stop in the middle.
Issue is that I don't want machine-wide tasks. It's not a good idea neither for me (it will affect other builds. sure, this can be handled, but still), nor for my teammates (I don't want to let them put something in system folders... ), nor for build server.
What is needed: solution to be built from scratch out of source control on clean machine with either Visual Studio or MSBuild.
It appeared that Custom*MicrosoftCommonTargets are regular properties.
So, how to specify this property? It works pretty fine when to set it from command line.
That's strange, but it appears that bit of magic present here: property passed as command line parameter to one build is transitively passed to all nested builds!
That's fine for build server. But this won't work with Visual Studio build. And even declaring solution-level property won't help: neither static, nor dynamic properties are transfer to nested builds.
...I have a hacky idea to set environment variable on before solution build and erase it on after. But I don't like it. Any better ideas?
I use a bit different technique then #Spider M9. I want that all projects in solution tree/all subdirectories from current directory use extended build throw Custom*MicrosoftCommonTargets. I don't like to be forced to change every new project to import custom targets/props.
I place special file, let's say msbuild.include, in the root directory and my custom targets loader for every project tries to find it in ., ..\, ..\..\, and so on. msbuild.include contains flags that triggers execution of custom actions. If loader can't find this file it disables loading all custom targets and stoppes. This gives me ability to use my build extensions with projects from work repositories and to not use with opensource projects.
If you are interested in I can publish loader. It's a pretty simple and elegant solution.
For example I can sign any assembly in all projects in all subfolders with my key.
I always set up every project to import a standard .props file. Use the GetDirectoryNameOfFileAbove property function (see MSDN) to find it. Do this as the first line of every project file. Once established, you can redirect from that file to other imports. Another trick is to have that standard import (that would obviously be under version control) import conditionally another .props file only if it exists. This optional file would not be in version control, but is available for any developer to create and modify with their own private/temporary properties or other behavior.
In VS, it is possible to set a custom build rule at the individual file level. However, unless you change the description of the build step (which is what msbuild outputs when running the external tool), all you get is the default, which says "Performing Custom Build Step," followed by the error output of the tool.
I recently got burned by this in an inherited solution, where the build rule for a .asm file buried deep in a project was failing, and NASM didn't provide any useful debug information. Since I didn't know what file was failing, it took me forever to find the problem. One thing I tried was changing the description for all the projects in the solution (by selecting them all at once and opening the properties panel) and adding macros to show the project and file affected. Since the build rule was attached to a file, however, this naturally didn't work.
I also tried searching for the default description string in all the project files, but the project files don't store this information unless you set the value. So clearly, there's got to be a default somewhere. Anyone know how you can set this?
I doubt you can change the default for everything, but what you can do is create a property sheet in which you set a custom description, and than add that property sheet to all projects needing it.
If you use Visual Studio 2008 and have many project files within solutions how do you keep them in sync? In other words, if you change a property in one project, how do you ensure that this property is automatically changed in other projects?
Given that enough contributors are mystified about the notion of nested solutions, I'll just work from the assumption you meant "solution with multiple projects". You give them common settings by using a project property sheet. Start with View + Other Windows + Property Manager. Open one of the nodes, right-click a configuration and choose Add New. Choose a location that makes sense for the solution, the solution directory for example. Configure the settings the way you want them.
Repeat this procedure for all other projects in your solution, now using Add Existing. Every project will inherit the settings you configured in the sheet, unless it overrides them explicitly. You may have to go back to the project properties and change an override back to "inherit".
IDE support for project property sheets is a bit flaky, be sure to save them explicitly when you make a change.
I have to say, I've not heard of "nested solutions", and I'd need a pretty compelling reason to do anything of this sort. Especially considering your question really centers on "how do I maintain duplication?" since you say the solutions will share properties. It's a cardinal rule in programming "do not duplicate thyself".
You could put the required options into a compiler response file, and use the same response file in each of your .vcproj files.
See here: http://msdn.microsoft.com/en-us/library/3te4xt0y(VS.71).aspx
Basically, you create a text file like SharedOptions.rsp, and on each line of the file specify a different command-line compiler option. Like /I.\include or /DDEFINE or whatever.
Then in the c++ command-line property page of each project, you add this in the additional options box: #"SharedOptions.rsp".
Then when you edit the options in the text file, they will be picked up by all projects. It is possible that the property manager solution provided by nobugz is just a gui for this - I don't know, I am more of a command-line kinda guy.
I guess you've already done something about this in the last 2 months, but this answer is more for the googlers...
I ended up using global variables available within Visual Studio. These were variables like $ProjectName and the like. There are many available already within VS, they can be user-defined as well.