setting external library /bin folder in cmake in windows - cmake

My question is related to PCL installation and here it goes:
I compiled PCL using MSVC2013 and I updated manually my PCLConfig.cmake to point to all its dependences (3rdParty libs). The problem is that I want also to set its binaries /bin folders in the same cmake file (instead of adding /bin to environment variables). Is there a way to do that ?
What makes this question pertinent is that I'am testing different versions of PCL (1.6, 1.7, 1.8...), so won't adding the /bin folders to path of all these versions will create some kind of confict between them afterwards (in the retreive step) ?
*To be clear, what i succeded to do is that, using cmake, I can choose the PCLConfig of the PCL version I want, but when running my program (succefully compiled), it throws a missing pcl_****.dll missing, which is logic because the PCL/bin folder is not added to the system environement. so, my question is "can I add the PCL/bin floder to the project environment using the CMakeLists of my project? (instead of adding it to system environment)"
Thanks in advance.

A temporary fix i found is generating my project with the specified PCL version wanted and then Adding the bin folders manually in MSVC 2013 :
Debugging->Environment << PATH=path_to_lib\lib;%PATH%
Tsyvarev, I'm looking for your proposition and I'll let you know if it worked. Looks like a cleaner way...
Cheers,

That PATH information for debugging is stored in the "user" project file. I've written a CMake function to generate such a file automatically. This worked for VC10. I assume it'll still work, or it may need some modification. The principal is to keep an empty user project file as a template, and CONFIGURE_FILE the contents to set whatever you want. I've set mine up so that you first do: SET(USERFILE_ENVIRONMENT "PATH=${directory}") then call the macro CREATE_USER_FILE with the name of the CMake project.
CreateUserFile.cmake:
get_filename_component(MY_DIRECTORY ${CMAKE_CURRENT_LIST_FILE} PATH)
SET(USERFILE_TEMPLATE ${MY_DIRECTORY}/msdev.vcxprojuser.template CACHE STRING "" FORCE)
MACRO(CREATE_USER_FILE _projectName)
# Find user and system name
SET(SYSTEM_NAME $ENV{USERDOMAIN} CACHE STRING SystemName)
SET(USER_NAME $ENV{USERNAME} CACHE STRING UserName)
IF (${CMAKE_SIZEOF_VOID_P} MATCHES 4)
SET(USERFILE_ARCH Win32)
ELSE()
SET(USERFILE_ARCH x64)
ENDIF()
SET(USER_FILE ${_projectName}.vcxproj.user)
SET(USERFILE_TEMPLATE ${USERFILE_TEMPLATE})
SET(OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}/${USER_FILE})
# Configure the template file
CONFIGURE_FILE(${USERFILE_TEMPLATE} ${USER_FILE} #ONLY)
ENDMACRO(CREATE_USER_FILE)
And here's the template file.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|#USERFILE_ARCH#'">
<LocalDebuggerEnvironment>#USERFILE_ENVIRONMENT#</LocalDebuggerEnvironment>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|#USERFILE_ARCH#'">
<LocalDebuggerEnvironment>#USERFILE_ENVIRONMENT#</LocalDebuggerEnvironment>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='MinSizeRel|#USERFILE_ARCH#'">
<LocalDebuggerEnvironment>#USERFILE_ENVIRONMENT#</LocalDebuggerEnvironment>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='RelWithDebInfo|#USERFILE_ARCH#'">
<LocalDebuggerEnvironment>#USERFILE_ENVIRONMENT#</LocalDebuggerEnvironment>
<DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>
ProTip: do something similar to generate a .bat file to launch your program after setting the PATH that CMake computes, so you can run it from outside Visual Studio.

Related

How to add Property to Affect Code Analysis in CMake

Based on this answer, I've added the below code to my .vcxproj file, which nicely eliminates most warnings that I was getting from Qt include files, and its autogenerated moc files.
<PropertyGroup Condition="'$(Language)'=='C++'">
<CAExcludePath>C:\Qt\5.13.1\msvc2017_64\include;.\ProjectName_autogen;$(CAExcludePath)</CAExcludePath>
</PropertyGroup>
This is great for one specific project, but since I use CMake, on multiple computers, this would get overwritten each time the CMake file changes, and I would have to add it to each Project I need it for (there's 3 projects that use Qt). Of course, the issue is also if I change the Qt path, then I would need to update the project files as well.
I am wondering, is there a CMake property I can set in order to add this to the projects? I know how to get Qt's include directory, just looking for the CMake property to actually set the CAExcludePath. If it helps, I'm using VS 2019, and this is to disable the warnings from Code Analysis.
I did check the cmake-properties page, but didn't find any predefined properties which seem to fit.
I figured it out. For anyone interested, I created a file called user.props which looks like the following:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition="'$(Language)'=='C++'">
<CAExcludePath>#QWT_INCLUDE_DIR#;#QT_INCLUDE_DIR#;.;$(CAExcludePath)</CAExcludePath>
</PropertyGroup>
</Project>
Then, in the CMakeList.txt root file, I have
SET( RULESET_PATH ${CMAKE_CURRENT_BINARY_DIR}/user.props )
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/user.props ${RULESET_PATH} #ONLY)
Then for each project I want to apply it to, I have
SET_PROPERTY( TARGET "ProjectName" PROPERTY VS_USER_PROPS "${RULESET_PATH}" )
There might be a more direct way, but this works.

Get solution directory in VS publish profile file

I have a solution composed with different projects from different path. We use foundation projects from a vanilla folder and then project specific projects from specific directory. Example:
specific project directory: c:\proj\specific
vanilla project directory: c:\proj\vanilla
vanilla project x path: c:\proj\vanilla\repo\src\project\x\code\
In each vanilla project we have a publish profile that points to the root directory and includes a publishsettings.targets file that has the actual target where the project should be published. By using this structure we can have a lot of projects and publish them using a single target so we don't need to change that target in all projects.
We discovered now that we have a problem when using these vanilla projects as the path used in publish profile is relative to vanilla directory and actually we need it to be relative to the specific project directory (solution directory).
In our publish profile we have:
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\..\..\publishsettings.targets" />
<PropertyGroup>
...
</PropertyGroup>
</Project>
So we need a way to specify the actual sln directory to this path so we can include the correct target so when we do the publish from visual studio it will publish to the specific project and not vanilla one.
I tried finding a "MSBuildSolutionDirectory" but it only seems to be a "MSBuildProjectDirectory" variable that can be used.
Does anyone knows a way I could get the path
Project="c:\proj\vanilla\publishsettings.targets"
to actually be
Project="c:\proj\specific\publishsettings.targets"
by using some msbuild or custom variable and not hardcoding it?
I need it to work both with vanilla (as I have a vanilla.sln) and also with specific project (as I have a X.sln).
Here is a way to make your own version of the MSBuildSolutionDirectory you were hoping to see built in:
<PropertyGroup>
<SolutionDirectory>$([MSBuild]::GetDirectoryNameOfFileAbove(`$(MSBuildProjectDirectory)`, `YOUR_SOLUTION_NAME.sln`))\</SolutionDirectory>
</PropertyGroup>
Notes on the GetDirectoryNameOfFileAbove MSBuild property function:
From http://blogs.msdn.com/b/visualstudio/archive/2010/04/02/msbuild-property-functions.aspx:
$([MSBuild]::GetDirectoryNameOfFileAbove(directory, filename) Looks
in the designated directory, then progressively in the parent
directories until it finds the file provided or hits the root. Then it
returns the path to that root.
From my own testing:
The returned path does not include a trailing backslash.
If the filename is not found, an empty string is returned.

Can't deploy from VS2013 (or TeamCity) after TypeScript 1.6 upgrade

Today I updated to TypeScript 1.6, and now the CopyPipelineFiles task is failing when trying to deploy web applications.
I redirect TypeScript's compiled JavaScript output directory to scripts\compiled\ in the Web project's settings (via TypeScriptOutDir entry in the csproj file) and this has always worked fine up until now. Typescript files are being compiled correctly, and to the correct directory (ie scripts\compiled\ under the web project directory).
However, the CopyPipelineFiles task seems to be getting confused about the root directory of TypeScript files, and now always assumes that it is the root directory of the web project. As a result, it's adding folders to the path that don't exist, for example, after compiling scripts\foo.ts, instead of grabbing scripts\compiled\foo.js MSBuild is looking for \scripts\compiled\scripts\foo.js. I have tried using $(ProjectDir) in the output directory, and I've also tried setting the TypeScriptRootDir to the appropriate root directory (e.g. scripts\) but the problem persists.
The only way I've been able to remedy the situation is to remove the output directory and allow the compiled JavaScript files to be output to the same locations at their TypeScript counterparts.
The following is an example of a real error I've received, where the path of the TypeScript file would be scripts\account\app.ts:
Copying file scripts\compiled\scripts\account\app.js to obj\Release\AspnetCompileMerge\Source\scripts\compiled\scripts\account\app.js failed. Could not find a part of the path 'scripts\compiled\scripts\account\app.js'
This happens on all local machines with Typescript 1.6 installed, as well as our TeamCity build server with 1.6 installed.
Filed a bug report with the TypeScript team and they've confirmed it. Seems to be an issue with how the compiler is computing absolute paths. The workaround right now is to explicitly set relative paths for both the outDir and the rootDir, so something along the lines of this:
<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
<TypeScriptOutDir>Scripts\compiled</TypeScriptOutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<TypeScriptOutDir>Scripts\compiled</TypeScriptOutDir>
</PropertyGroup>
<PropertyGroup>
<TypeScriptRootDir>Scripts\typescript</TypeScriptRootDir>
</PropertyGroup>
In my case I've just moved both the outDir and rootDir to the property group with no condition since I don't need to switch the outputDir between builds, something like so:
<PropertyGroup>
<TypeScriptRootDir>Scripts\typescript</TypeScriptRootDir>
<TypeScriptOutDir>Scripts\compiled</TypeScriptOutDir>
</PropertyGroup>

MSBuild - Project-specific targets for solution does not work

I have a solution that has multiple projects in it, including a web application. I want MSBuild to execute "WebPublish" target against the web application project and "default target" for all other projects in the solution.
This MSDN article says that I can do it specifying the command line
msbuild SlnFolders.sln /t:NotInSlnfolder:Rebuild;NewFolder\InSolutionFolder:Clean
But I never could make it work - MSBuild return an error, something like "NotInSlnFolder:Rebuild" target does not exist. It does not matter what target to specify, Build, Rebuild or Clean - it does not work in any case.
How can I achieve my goal of specifying project-specific targets for a solution?
The MSDN documentation does not work. Or have I missed something?
NOTE: This workaround is not officially supported by Microsoft, so there is no guarantee that it will work forever.
Short Answer
In folder with the SLN file, create the file before.{YourSolution}.sln.targets, with the following content: (Replace what in curly brackets to whatever you need.)
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="{MyCompany_MyProduct_WebApp:WebPublish}">
<MSBuild
Condition="'%(ProjectReference.Identity)' == '{$(SolutionDir)MyCompany.MyProduct.WebApp\MyCompany.MyProduct.WebApp.csproj}'"
Projects="#(ProjectReference)"
Targets="{WebPublish}"
BuildInParallel="True"
ToolsVersion="4.0"
Properties="BuildingSolutionFile=true; CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)"
SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" />
</Target>
</Project>
After that you can execute the command line:
msbuild {YourSolution}.sln /t:{MyCompany_MyProduct_WebApp:WebPublish}
Long Answer
If you add environment variable MSBUILDEMITSOLUTION, setting its value to 1, MSBuild will not delete temporary files generated for the solution and projects.
This will allow you to find {YourSolution}.sln.metaproj and {YourSolution}.sln.metaproj.tmp files generated in the solution folder, which are just standard MSBuild project files.
For MSBuild 3.5, the generated file is {YourSolution}.sln.cache and is retained regardless of environment variables. Analyzing those files, you will understand low-level details of the process and to see the customization opportunities available.
After executing MSBuild with some project-specific target in the .Metaproj file you will find out that the list of project-specific targets is hardcoded and only standard targets are supported (Build, Rebuild, Clean, Compile, Publish; note: Publish and WebPublish are not the same). MSBuild 3.5 only generates Clean, Rebuild and Publish targets as well as a target with just the project's name that means "Build".
You also can see that NotInSlnfolder:Rebuild is just a name of an autogenerated target. In reality MSBuild does not parse it and does not care about project names and location. Also note that the autogenerated target names specify the project name with solution folders hierarchy if it's in one, e.g. SolFolder\SolSubfolder\ProjectName:Publish.
One more critically important thing you will find: The MSBuild Target Name does not support dots. All dots in project names are replaced with underscores. For example, for a project named MyCompany.MyProduct.Components you will have to specify in the command line:
/t:MyCompany_MyProduct_Components:Rebuild
That's why even standard project-specific target Build didn't work - my project name contained dots.
Analyzing file {YourSolution}.sln.metaproj.tmp, you will find out that at runtime it tries to import targets from file named before.{YourSolution}.sln.targets and after.{YourSolution}.sln.targets, if those files exist. This has a key to the workaround for this MSBuild limitation/bug.
You can open your solution file in text editor and check whether following line is exist or not if not then you can add
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> inside the <Project> tag.
Hope this help you.

MSBuild - Determine a solution's _PublishedWebsites

I am writing a web development targets file and would like to programmatically determine the name of the directory that appears beneath "_PublishedWebsites".
I currently have to use this:
$(BinariesRoot)\%(ConfigurationToBuild.FlavorToBuild)\_PublishedWebsites\ MyWebApplication
Any ideas?
(I am not using this for solutions with more than one website to publish)
The new Web Publishing Pipeline (WPP) in .NET 4.0 has a method for controlling the output location.
First, you need to opt-in to WPP during the execution of the CopyWebApplication target. Set the following MSBuild properties, either at command line or in the MSBuild project file:
<PropertyGroup>
<UseWPP_CopyWebApplication>True</UseWPP_CopyWebApplication>
<PipelineDependsOnBuild>False</PipelineDependsOnBuild>
</PropertyGroup>
The command line-variant is:
/p:UseWPP_CopyWebApplication=True /p:PipelineDependsOnBuild=False
Next, create a new MSBuild targets file in the same directory as your project and name it "ProjectName.wpp.targets" where "ProjectName" is the filename of your project, minus the extension. In other words, if you have "MyWebsite.csproj" you need to create "MyWebsite.wpp.targets". I find it helps to add the targets file to the project as well. It's not required, but it makes it easier to edit.
In the new targets file, you will need to override the WebProjectOutputDir property. Only do this when CopyWebApplication will be called - in other words, when the "OutDir" is redirected away from the "OutputPath":
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebProjectOutputDir Condition="'$(OutDir)' != '$(OutputPath)'">$(OutDir)Websites\MyCustomFolderName</WebProjectOutputDir>
</PropertyGroup>
</Project>
That's it - you should be good to go. You can test it locally by setting the OutDir property. Don't forget the trailing backslash:
msbuild MyWebsite.csproj /p:OutDir=C:\Development\WebOutputTest\