Setup Library Project for Variable definitions - visual-studio

I'm currently trying to make a library with several variable definitions I can use in different Setup Projects but without success.
I have several huge *.wxs files which are build into a library where the File/#Source should depend on project settings. The reason for that is our internal directory structur which looks like any\path\Redistributables\In-Test\X64\productA or any\path\Redistributables\RC\X86\productA so the last 2 parts of my path should change e.g. on x64 and x86 build.
Now I have several Setup Projects too which uses the same path but working in another subdirectory e.g Redistributables\In-Test\X64\AnyOtherProductB.
I dont want to pass preprocessor variables with the same values to each project because the path could change later and I would have to edit too many project settings then.
So my question:
Is it possible to build something like a "Variable/property Container" with public variables/properties so I could just reference that library to use the variables defined in it for my <component><file source...>?
I've tried to achieve that with prepocessor variables before $(var.sourcedir)$(var.compilemode)$(var.platform) with no luck. These 3 are the parts I need to define once to use them in nearly all projects.

Small edit here, I do this a lot where I read the question quickly and make an answer then reread the question and think my answer doesn't quite fit the question but in this case I hope it is still helpful and maybe one of the other resources referenced here will help as well.
Ah I did something like this and banged my head against the wall for a while trying to figure it out.
The problem is you can't use variables like $(var.sourceDir) in the wixlib and then change it later on when you use the lib in a project. What happens is the compiler replaces $(var.sourceDir) in your wixlib with the actual value of this variable when you build it. You can verify this by opening up your obj file and looking for a component or something that uses this directory and see that it has the value of the sourceDir variable not $(var.sourceDir).
To solve the issue we are going to use bind/wix variables (not sure of the terminology) which get evaluated at linking time.
This was the "how to" that I eventually got to which helped me a lot
So, update your variables to something like this !(bindpath.SourceDir)
<Fragment>
<util:RegistrySearchRef Id="MSVCPPRedist_x64_12"/>
<PackageGroup Id="MSVCPPRedist_x64_12">
<ExePackage
Id="MSVCPPRedist_x64_12_0_21005"
Cache="no"
Description="Visual C++ 2013 Redistributable needed for [WixBundleName]"
DetectCondition="MSVCPPRedist_x64_12 OR NOT VersionNT64"
DisplayName="Prerequisite - Visual C++ 2013 Redistributable (x64)"
InstallCommand="/install /quiet /norestart /log vc12log.txt"
PerMachine="yes"
Permanent="yes"
SourceFile="!(bindpath.PrerequisitesDir)VC++\vcredist_x64_12.exe"
UninstallCommand="/uninstall"
Vital="yes"/>
</PackageGroup>
</Fragment>
And then in some props file or project file on the build machine you can include something like this:
<LinkerAdditionalOptions>-b "PrerequisitesDir=$(PrerequisitesDir)\"</LinkerAdditionalOptions>
Where the $(PrerequisitesDir) property was set based on a relative path in a msbuild file on the build machine like so (or use absolute path is also fine):
<!-- Directories -->
<PrerequisitesDir>$(MSBuildThisFileDirectory)..\..\..\Installers\Prerequisites\</PrerequisitesDir>
Now when you build the linker phase will use the build time defined bindPath to find the source files.
In regards to the platform and configuration vars, I think you can use WixVariables referenced as !(wix.VariableName) but I'm not sure off the top of my head and without more experimenting myself. You can take a look at this answer here to help get more acquainted with the different types of variables. If WixVariables don't work you can just build several flavours of yoru wixlib and reference the appropriate one in your wixproj themselves by using the MSBuild properties $(configuration), ect.. in the hintpath of the wixlib file.

Related

How do you reference the solution folder in .targets?

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.

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 ;)

How to emulate /p msbuild parameter in Visual Studio build?

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.

Visual Studio 2008, MSBuild: "replacement" projects

My solution has a library project which needs a special environment to be built (lots of external libraries and tools)... but it is not vital to our application. We'd like to avoid installing these tools when not necessary (most of our developers work on other parts of code).
We have created another project which has the same API, but has an empty implementation and is compilable without those external tools. I'd like to be able to easily switch between those projects and still get all the references in other projects correct.
I don't know VS/MSBuild very well, but willing to learn whatever is necessary. Is it possible? I am looking for ideas... We're using Subversion, and solutions involving some hacks inside VCS are also welcome.
It sounds as if your library project is one that can be separated from your primary solution, taking the tool baggage with it. Doing that, you could build the speciality solution separately, an link the compiled assembly from the main solution.
Create another build-configuration for your project.
So you will have at least 2 build-configurations e.g. Debug_SpecialNeeds and Debug.
For discussion, I'll assume you have a project directory containing your solution file, a "RealLibrary\RealLibrary.csproj" project file (your "real" library, with the dependencies), and a "MockLibrary\MockLibrary.csproj" file (your "mock" library, with the empty implementations).
If I understand correctly, you want to easily "swap" the MockLibrary for the RealLibrary in your solution, and vice-versa.
The easiest/hackiest way to do this, assuming your solution (and dependent projects) are configured to look for the "RealLibrary.csproj" project, is to rename the "RealLibrary" directory (it doesn't matter to what), and rename the "MockLibrary" directory to "RealLibrary" and rename "MockLibrary.csproj" to "RealLibrary.csproj". This will effectively "trick" your solution and dependent projects into loading the "mock library" even though they are referencing the "real library".
A slightly more complex (and perhaps cleaner) solution is to actually modify your "sln" and "csproj" files to reference "MockLibrary.csproj" instead of "RealLibrary.csproj". In the "sln" file, you'll need to change the path to the project in the section near the top:
Microsoft Visual Studio Solution File, Format Version 10.00
# Visual Studio 2008
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RealLibrary", "RealLibrary\RealLibrary.csproj", "{E1714F9A-E1D9-4132-A561-AE2B4919391C}"
EndProject
You need to change that path "RealLibrary\RealLibrary.csproj" to "MockLibrary\MockLibrary.csproj". If you're going for completeness, you can change the name as well (or perhaps just use a generic name like "Library" for the name).
Likewise, in the dependent csproj files, you'll need to find all instances of the "ProjectReference" node where you reference "RealLibrary.csproj" and modify the path. These sections look like this:
<ProjectReference Include="..\RealLibrary\RealLibrary.csproj">
<Project>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</Project>
<Name>RealLibrary</Name>
</ProjectReference>
You could relatively easily write some scripts to perform this swap. However, I think there's a deeper problem here that can be addressed more directly. I'll post that as a separate answer, but I wanted you to have the actual answer you were looking for first.
The deeper problem I see here is that your library "needs a special environment to be built", specifically because it depends on "lots of external libraries and tools". I would suggest that you NOT go down the path of creating the mock library, but instead focus on getting the library to build correctly without a special environment. You can achieve this by including all of those dependencies in source control along with your project, and reference those dependencies via relative paths inside your working copy. In my build environments, I try to avoid static environmental dependencies as much as possible (ideally limiting it just to the .NET framework itself).
To get the dependencies into source control, you can either check them directly into the project itself, or you can check them into a different location and then "reference" them in your project via svn:external definitions. In my environment, I have a separate "bin" repository used just for these kind of third party library dependencies, and then many dependent projects can pull them in via externals.
If you can eliminate your library's build-time environmental dependencies, your build will be much more robust and it will be much easier for developers to work with the project.

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