How do you reference the solution folder in .targets? - visual-studio

I have a custom tool that I run on certain file types using the .targets mechanism in Visual Studio 2015.
Projects exist at many levels, and I want to reference this tool when the code is pulled to ANY drive/folder, including the TFS CI agent.
I tried using a relative path, but because the files are at different levels, it doesn't work for all projects.
I tried using a registry setting and environment variable, but that doesn't bode well for the CI machine which might build in a different folder each time.
Is it possible to get the solution folder of which the project/file is in, then I can use a relative path to the tool directory?
All I can find is these properties, which do not seem to help:
General MSBuild properties:
https://msdn.microsoft.com/en-us/library/bb629394.aspx
https://msdn.microsoft.com/en-us/library/ms164309.aspx
Registry and environment variables:
https://msdn.microsoft.com/en-us/library/ms171458.aspx

You can use all the standard tokens within .targets.
Just use
$(SolutionDir)
See https://msdn.microsoft.com/en-us/library/c02as0cs.aspx

Self-service answer: use visual studio's property editing for a vcxproj in preprocessor macros or include paths or such to look at a bunch of available variables. You can usually find what you need in there, by name, or by example, including the one you need here. Better than any documentation.

Related

VS2010, MSBuild and Environment Variables

I have a question about MSBuild.exe and Environment Variables. First, the development environment:
The code is mostly C++ with some C#. There are over 5,000 classes, 340 projects and 200 solutions arranged in a deep source tree. There is a solution at the root of the tree and at various other points in the tree. We use TFS and maintain multiple active branches for a series of future releases. Each developer uses a local view to modify and test code. Some developers work on multiple branches concurrently. Sometimes developers build solutions from different TFS branches concurrently.
We use about 70 environment variables for locating shared header files, libraries, etc. In VS2008 we used a file with the same base name as the solution file and an extension of .slnenv for defining the environment variables. All variables are defined relative to the base of the source tree. This .slnenv file is read by a custom VS2008 AddIn that creates Environment Variables in the VS2008 process space, i.e.
MyProjectDir=$(SolutionDir)\..\..
MyRoot=$(MyProjectDir)\..\..
MyInstallDir=$(MyRoot)\Install
MySourceDir=$(MyRoot)\Source
MyUnmanagedSourceDir=$(MySourceDir)\My\Unmanaged
MyIncludeDirs=$(MyProjectDir);$(MyUnmanagedSourceDir)
This AddIn does not work correctly with VS2010 because MSBuild does not inherit the environment variables that are created after the solution is loaded.
My question is how do I get these environment variables to MSBuild? I have found two methods that work, but are not as convenient as the AddIn we had been using.
VS2010 can be started with a command script that first sets Environment Variables in the process space and then starts VS2010. MSBuild does inherit these Environment Variables. This is unsatisfactory because the scripts would need to be customized for the various points where a solution can be loaded.
The second method I have tried is defining the Environment Variables as Properties in property sheets and .vcxproj files. We have a base property sheet that all .vcxproj files load. In that property sheet:
<PropertyGroup>
<MyRoot>$(MyProjectDir)\..\..</MyRoot>
<MyInstallDir>$(MyRoot)\Install</MyInstallDir>
<MySourceDir>$(MyRoot)\Source</MySourceDir>
<MyUnmanagedSourceDir>$(MySourceDir)\My\Unmanaged</MyUnmanagedSourceDir>
<MyIncludeDirs>$(MyProjectDir);$(MyUnmanagedSourceDir)</MyIncludeDirs>
</PropertyGroup>
Then I can define the base directory in each .vcxproj file:
<PropertyGroup>
<MyProjectDir>..\..</MyProjectDir>
</PropertyGroup>
This method uses relative path names where the AddIn resolved all Environment Variables to absolute path names. I'd rather not have to edit 340 .vcxproj files where the definition of "MyProjectDir" would vary depending on how far from the source root the project file exists. So far I've tried this method in just one project.
I have tried to modify the AddIn to create Properties rather than Environment Variables. I tried using ENV2.get_Properties(), but that seems to work only for Properties that are defined in VS2010, not for Properties I've defined.
Thank you,
Dan Kary
After discussion via email, we figured what were Dan needs and following message helps him with question issue. He agrees that this could be helpfull to other SO members:
Aha, I get it now.
As I did understand from that topic - you really need all that stuff only for build step (paths for include clause, paths to built tool to resolve dll\exe dependency), and no regular developer activities depends on it.
It's good, cause we don't need to fight VStudio about not reinitializing env vars after first load. We just need somehow edit and implement your solution for huge amount of proj files ;) Task become much easier :D
Small disclaimer - I'm writing this on the road and have no access to fully fledged dev env to check and bulletproof all further recomendations, so take my apologizes in advance for any mistakes I will make ;)
I would like to bring your attention to some msbuild features:
http://msdn.microsoft.com/en-us/library/ms164309.aspx
You have predefined properties, that available for all scripts and projects, it looks like this will help you solve first issue - absolute paths instead of relative. This is small excerpt from the link for further reference
$(MSBuildProjectDirectory) -
The absolute path of the directory where the project file is located, for example, C:\MyCompany\MyProduct.
$(MSBuildProjectFile) -
The complete file name of the project file, including the file name extension, for example,
MyApp.proj.
$(MSBuildProjectExtension)
The file name extension of the project file, including the period, for example, .proj.
$(MSBuildProjectFullPath)
The absolute path and complete file name of the project file, for example, C:\MyCompany\MyProduct\MyApp.proj.
$(MSBuildProjectName)
The file name of the project file without the file name extension, for example, MyApp.
So you should be able to use this defined properties in your Property sheets.
Now, with msbuild 4.0 toolset (toolset defined by attribute ToolsVersion="" on the root script element) there is interesting trick
Microsoft.Common.Targets define 2 variables to conditionally import custom msbuild script before or after itself
CustomBeforeMicrosoftCommonTargets and CustomAfterMicrosoftCommonTargets
You can use it the following way ( I think it will possible help you to avoid editing of all proj files or at least - reduce editing to very simplified copy-paste part)
Define it using relative path to include your property sheet files
<CustomBeforeMicrosoftCommonTargets>$(MSBuildProjectFile)\ConcreteProjectCustomProperties.propz</CustomBeforeMicrosoftCommonTargets>
Take a note that CustomBeforeMicrosoftCommonTargets should be global scoped property to be inherited by all secondary
And if you properly define your ConcreteProjectCustomProperties.propz I think you could achieve what you need.
"Properly" is that you will include there your global properties file (with relative or absolute path) and then define all your project-level properties.
NB:
Also take a note that later defined properties not available for reference to early defined
Your example defines $(MyIncludeDirs) with reference to $(MyProjectDir), but $(MyProjectDir) declared and defined a bit later.
It could be due to "exampling" and fast churning that code, but if you define your vars same way in production - it could lead to subtle error.
I hope I got your problem properly and my explanations will help you to quickly move all your properties from custom add-on to simpler and native msbuild script ;)

Custom macros for configuring a build in Visual C++

Hopefully this isn't a duplicate. I tried to search for an answer to my question, but the word 'macro' just has too many different applications to filter the search results very effectively.
Anyway, I recently noticed in another Visual C++ (VS2010) project that custom macros were used to set up VC++ directories (include, lib) or link libraries. Something like this: "libjpeg-$(JPEG_LIB_VERSION)-static.lib", etc.
How does one go about doing that? I can't remember in what project I saw that technique, so I haven't been able to find it again to investigate, but this would be very useful when building against local builds or particular versions of widely-distributed software (say, Boost, for instance).
There's this (Visual C++ Express and setting env variables solution wide), which I suppose kinda-sorta answers the question, but not really.
The macro being used may be an environment variable, or it may indeed be a custom macro.
If it is an environment variable, you could follow the documentation as mentioned in this MSDN document How to: Use Environment Variables in a Build
However, if it is not an environment variable and you want to create your own, there is another MSDN document for that How to: Add New Property Sheets to C++ Projects
For additional references on creating a custom macro, please see How to add environmental variable to VS solution (.sln )
For additional references on using an environment, please see Macros/Environment variable in .sln and .vcproj files for Visual studio

How/Where are the environment variables in a Visual Studio C++ project set?

There a lots of environment variables in my project properties that I do not understand. Clicking on macros (Is there a list of Visual Studio environment variables?) gives me a list of their values, but I am unable to figure out where some of these are set.
For example, I am trying to figure out where the variable $(IntDir) is being set.
What file is responsible for setting these variables? How can I modify them?
These are not environment variables.
They're just macros defined by the build system that you can use for setting build properties for your project. They automatically expand to things like the target platform ($(Platform)), the path to store intermediate files for your project ($(IntDir)), and the name of your project ($(ProjectName)).
You can't change them directly, but you can change them by modifying your project's properties. The project file (created automatically by Visual Studio when you create a new project) is responsible for setting them.
You already found a link to the big list of 'em, which is helpful in explaining what they are and what they do. As the documentation says, you can use them anywhere in your project's property pages that string values are accepted. They keep you from having to hard-code paths and other information, which is exceptionally useful.
Unlike environment variables, they do not persist or have any meaning independent of your build system. Once your project has been built, they go away. They're not used during debugging or deployment.
.If you want to see actual values for a specific VS instance for both 'standard' and 'custom', see if this answer helps. (Basically, you can use Process Explorer to find that out.)

MSBuild imported script directory

In Visual Studio 2010 we have MSBuild for C++ project. Also we can add additional custom properties files "*.props" to projects, which are just MSBuild scripts.
Is it possible in imported "some.props" file know its directory?
for example there is "project.vcxproj" file and "common.props" file.
I would like to write something:
<IncludeDir>$( [and something for common.props file directory here] )\include</IncludeDir>
What should I write there?
%programfiles%\msbuild, which is accessible with $(MSBuildExtensionsPath), is the recommended place to put .props and .targets files that you would install and leave static. For example, many Microsoft teams that ship build process put their .targets files there.
If you plan to check-in those .props files for your team to use, or modify them, or maybe have different ones for different sets of source code, it's not such a good location; it isn't next to your source code and it requires admin rights to modify. In such cases, I recommend you put the files near your source code, perhaps at the root of a tree or subtree that includes all the projects for which it is relevant.
If you can put them under %Program Files%/MSBuild/ then you can use the MSBuildExtensionsPath property. This resolves to %Program Files%\MSBuild. If you cannot put the files there then another option would be to create an environment variable. In MSBuild you can access env variables just like properties. For example you can do <Message Text="Path :$(Path)"/> to print out the current path.

Visual Studio msbuild

I have a question regarding the commandline options of msbuild. I am
currently using msbuild to build projects using the existing solution
files. These solution files have references to external dll which have
different paths on each machine. I am currently writing a build script
and passing the specific path to the project file via the /p: switch of
msbuild.
My current build line is:
msbuild test.sln /p:ReferencePath="c:\abc" /p:ReferencePath="c:\rca"
What i have noticed that Reference Path now contains only c:\rca and
not c:\abc. this is causing problems for me since, the external dlls
lie in two different directorys. I am allowed to keep multiple
reference paths via visual studio, but not via the commandline.
Is there any known way by which i can do this
I believe you can use this /p:ReferencePath="c:\abc;c:\rca"
At least that is what that link is hinting at, they are using %3B to encode the ";" within the build file.
Although the correct syntax for providing more the one reference path is listed above, I would suggest solving the root cause which in my opinion is the different locations of your referenced assembly. I would suggest you put all thirdparty dependencies, apart from the framework assemblies in your source code repository for the following reasons:
Relatitve paths are consistent across computers
The source code is always in sink with the correct version of your thirdparty assembly (if you for instance need to build an old version of your software 2 years from now).
Upgrading your thirdparty assembly is as easy as upgrading on one machine and then committing your changes to the repository. (In a previous project we even went as far as checking in the entire java runtime environment and were quite happy with the given setup.)
Try seperating your pathes with a semi-colon (;)
Like this:
c:\abc;c:\rca
You may be better off by synchronizing your libraries across machines. I have found that Visual Studio makes this easy. Simply add a solution folder, and add your libraries there. Then, in each project, reference the libraries from this common place. This way, each developer has them in the same place.
This will remove one of variables you have when trying to script out builds.
The command line options for setting the reference path will work just fine (assuming you escape the semi colon, it seems both %3B and ; will work). However, when the argument was passed in from nant (and I needed multiple paths), creating a 'Visual Studio Project User Options file' seemed to work better.
I just emit (echo) a file to the file system with the following format:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ReferencePath>
C:\abc;c:\rca
</ReferencePath>
</PropertyGroup>
I give the *.user file an appropriate name (given a project file MyProject.csproj, my user file would be MyProject.csproj.user)

Resources