I wanted to use two configurations in my Visual Studio 2019 C++ project, lets say I wanted to rename Debug to Debug-A and add a new configuration called Debug-B based on Debug-A.
Debug-A and Debug-B differ only by one define symbol, lets say one has SYMBOL_A and another has SYMBOL_B.
Currently, I don't care about Release and anything other than x64.
It works corretly until I add a NuGet package (for example fmt). Then, when I try to compile, I get undefined symbol linker errors. Just like fmtd.lib was not included, if I include it manually (or change the project configuration name to Debug), the issue is gone.
I know the reason why NuGet includes it if my project configuration is named Debug. Look at the nuget package targets file (packages/fmt.7.0.1/build/fmt.targets) - lib files are hardcoded to $(Configuration) being either Debug or Release.
As far as I know, all NuGet C++ packages are built this way.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- ... -->
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<Link>
<AdditionalDependencies>fmtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>fmtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<Link>
<AdditionalDependencies>fmt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Lib>
<AdditionalDependencies>fmt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Lib>
</ItemDefinitionGroup>
<!-- ... -->
</Project>
I know I can manually link the libs, but fmt was just an example, I use a lot of packages and manually linking will become an issue quite fast.
Is there any way to use the quick selection of preprocessor symbols provided by active solution configuration (the toolbar dropdown) and still be able to use NuGet properly? For example parsing the nuget files with fake $(Configuration) variable. Using $(Platform) (x86, x64 etc.) is impossible, library include path is depending on it's hardcoded value too.
Sample project with this bug.
If, as you mentioned, the NuGet Packages’ lib files are hardcoded, then we may need to find solutions from other sides, for example .vcxproj file or MSBuild.
I didn’t find any directly properties/parameters/ways which meet your requirements. But, is it possible to keep the Debug name, I mean, leave it named Debug not Debug-A or Debug-B, and then switch to use different Configurations by using other methods. Imagine there is a Debug configuration file(DebugB)(maybe DebugB related things are set in this file) excluded in the project and the project currently use another Debug configuration(DebugA), and during the build, the project will exactly use DebugA configuration. To switch, do something, or add a code line in .vcxproj file to include the file which contains DebugB configuration, and then let the DebugB configuration cover the DebugA configuration.
So, for covering the properties/items of .vcproj file. Perhaps customize build works.
Hope above could give you a little help.
Related
We have an internal NuGet Package that consists of some .NET Code and a TypeScript Definition File (*.d.ts). This is the content of the package:
After installing the package into a new .NET Core project, the folder structure in the solution explorer looks like this.
So far, everything went as expected. But note the small arrow symbols on the "i18n" folder and the "Index.d.ts" file. It looks like they're just links to the actual file. When I click on the d.ts file the content seems correct. But Visual Studio fails to recognize the declarations in it, so I can't use it in my TypeScripts.
One idea was to include the path to the packages in the tsconfig.json, but that can't be the solution... Any other ideas how to do that?
How to add TypeScript Definitions to .NET Core Nuget Packages
As far as I know, Definitely Typed packages are not compatible with .NET Core projects. That because the script files should be included in <contentFiles> element. You can refer to the Including content files for more detail info.
Besides, just as Martin comment, npm is the recommended method of installing Definitely Typed packages:
https://github.com/DefinitelyTyped/DefinitelyTyped#how-do-i-get-them
So, after seeing the replies here and not giving up, I have to put in my approach to this.
As I'm working on a large solution with over 100 subprojects - many of them fast moving NuGets, I couldn't give up. I wanted to have my .NET object models including their interface/class representations in TS, being able to have both imported by including one NuGet (and thereby reduce dependency hell a little bit). I have to mention, I tested this only with my own object model, which has no external dependencies - and I tested only on VS2022.
But in this restricted scenario it works without any issues.
On the project containing the TS definitions
Set the build action for the ts definitions you need to be included in the NuGet to "content". This will include them into the NuGet package.
On the consumer side
Adjust your package reference, add the following property/value:
<GeneratePathProperty>True</GeneratePathProperty>
This will create an MsBuild property/variable referencing the path to the local presence of the restored NuGet file (important if your building on multiple, different machines - like on CI pipelines, build servers etc.) and allowing you to avoid any hardcoded absolute paths.
The generated property has the following format
$(Pkg<PackageNameWithDotsBeingReplacedByUnderlines>)
So a package named "MyPackage.Hello" would result in the variable $(PkgMyPackage_Hello)
Now we create a new build target to copy the files from the restored package's contentfiles folder (as it's restored, and we have the restored and thereby extracted path, we can finally target them).
<Target Name="CopyImportedTypes" BeforeTargets="Build">
<ItemGroup>
<TsTypesToCopy Include="$(PkgMyPackage_Hello)\contentFiles\any\net6.0-windows10.0.20348\*.ts" />
</ItemGroup>
<Copy SourceFiles="#(TsTypesToCopy)" DestinationFolder="$(MSBuildProjectDirectory)\AnyProjectSubFolderIfDesired" SkipUnchangedFiles="true" OverwriteReadOnlyFiles="true" />
</Target>
Make sure to adjust the "Include" path to your package (TFM, Platform etc.). An easy way to get the relative path is to open up the solution explorer, expand your consuming project, expand dependencies and then packages, expand the package with your ts definitions and open up the properties of the contentfiles.
This target is being executed before the actual build (so we can use the imported types on the build being happening right this moment) (BeforeTargets property). The ItemGroup is nothing else than a definition of items (in our case, source files) we want to use, being stored into #(TsTypesToCopy) which is being used by the copy task.
Thankfully, VS does automatically set new files to the right build action (in most cases), so the brand new ts files should be in the right mode automatically - so we don't have to tweak anything manually.
I see the following in many *.vcxproj files
<PropertyGroup Label="Globals">
<ProjectGuid>{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>yadayada</RootNamespace>
</PropertyGroup>
What does the <Keyword>Win32Proj</Keyword> segment mean? Does it affect any behavior during the build?
In general, where can I find documentation on the tags in a project file?
<Keyword>Win32Proj</Keyword> tells Visual Studio which of the Windows-specific dependencies you are going to use.
It depends on type of the project selected during its creation.
For already present project you may view this setting in the Project Properties --> General.
Now, up to your question.
Does it affect any behavior during the build?
Win32Proj means that AdditionalIncludeDirectories will contain paths to WinAPI libraries. If it were MFCProj, then paths to MFC headers would have been present there in addition to WinAPI. For instance, see in this project file
As for the documentation, try to check this article from MSDN blog, it explains the meaning of some of the tags. And here are the guidelines for working with project properties.
Note that those properties are supposed to be viewed and edited from the UI, so a structure of a real *.vcxproj file may not seem to you extremely friendly or human-readable.
Is there a way to define the project file path in a solution using a user macro/environment variable? I can't seem to do that.
Kind of like an environment variable is used to define the additional include directories in a C++ project, except I want to do the same for the location of a project file in a solution.
I've tried editing the solution in a text editor to change the path to start with %MyMacroName% or $(MyMacroName) but neither of them seems to parse just right. The project files can't be located when the solution is opened.
In .sln file use syntax "%ENV_VAR%rest_of_the_path\test.csproj"
In .csproj file use syntax "$(ENV_VAR)rest_of_the_path\test.dll"
That works for me, ENV_VAR is custom environment variable defined for operating system like ENV_VAR = "C:\MyPath\"
MSBuild allows you use to environment variables,
http://msdn.microsoft.com/en-US/library/ms171459(v=VS.80).aspx
So that you should be able to define environment variables as you wish, and then modify vCxproj files to make use of them.
I am not sure if that tip works for sln files, as sln files are not MSBuild scripts.
From Microsoft Docs .vcxproj and .props file structure:
We recommend you only create and modify .vcxproj projects in the IDE, and avoid manual editing as much as possible. In most cases, you never need to manually edit the project file. Whenever possible you should use the Visual Studio property pages to modify project settings.
If you need customizations that aren't possible in the IDE, we recommend you add custom props or targets.
In props file, use syntax <ENV_VAR>your_local_path</ENV_VAR>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Label="PropertySheets" />
<PropertyGroup Label="UserMacros">
<ENV_VAR>your_local_path</ENV_VAR>
Up to VS2008, you set your native C++ project dependencies up in the solution file (Project Dependencies ...) and if (by default) the Linker Option
Properties -> Linker -> General : Link Library Dependencies = Yes
is set, the Visual Studio Build will automatically link in the .lib files of all projects (DLLs, LIBs) that this project is dependent on will be "statically" linked in.
Side Note: Microsoft changed how the dependencies worked in VS2010 and you are now supposed to add the dependency directly to the project
Common Properties -> Framework and References : (List of depenencies)
(each lib/dll has a separate option:
Project Reference Properties -> Link Library Dependencies : True|False
I'm fine with that. This is not what this question is about.
(One explanation here: Flexible Project-to-Project References.)
It is still possible however to define project dependencies on the Solution level and the General Linker option is also still there. However it doesn't work. See:
Link Library Dependencies not working?
Did Visual Studio 2010 break "Project Dependencies" between C++ projects?
Visual Studio 2010 not autolinking static libraries from projects that are dependencies as it should be supposed to
"but oddly enough, without removing the old UI, or in any way indicating that it no longer works"
and especially see here (acutal question follows)
MS Connect Bug 586113: "Link Library Dependencies" does not work
Where Microsoft confirms that the Linker Option doesn't do what the rest of the world's population expects it to do, and adds the following explanation:
Thanks for reporting this feedback. The issue you are experiencing is
by design. "Link Library Dependency" is a flag that only dictates
whether or not to pass the library as an input to the linker. It does
not find the dependency automatically. As a customer you will have to
define the depedency manually as you suggest.
Can anyone explain what that means, or more to the point: What does the "Link Library Dependency" linker option actually do in Visual Studio 2010?
What is an "input to the linker" that isn't actually linked supposed to be?
You have to give the setting the proper value to bring clarity:
2017 Re-Run. Yay.
TL;DR
This Option sets the default value(a) for the actual Link Library Dependecies on each project reference. If each project reference has LinkLibraryDependecies set, then it is in effect meaningless.
However, when adding a new reference, by default (in VS2010 and 2015) the new <ProjectReference> element in the vcxproj file does not have the setting set, so this option is relevant in that it provides the default for all newly added references, as long as their value isn't modified.
(a): It really should be the same for all Configurations (Debug/Release) and Platforms (Win32/x64) or things get really complicated.
Gory details
Hans pointed out that it appears to not do anything in VS2010 as such. However, this doesn't mean that it actually ain't used by VS/MSBuild.
The crux is how this option is inserted into the vcxprj file and how the defaults work for the <ProjectReference> setting in the msbuild file.
The setting on the Linker dialog as shown above is inserted as:
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
<ItemDefinitionGroup>
<ClCompile>
...
</ClCompile>
<Link>
...
</Link>
<ProjectReference>
<LinkLibraryDependencies>This option is not used by VS 2010!</LinkLibraryDependencies>
</ProjectReference>
...
</ItemDefinitionGroup>
</Project>
And while it appears to be somehow grouped together with the Link Option, that's just there to confuse you.
What this actually does in a given vcxproj file (or when coming from a .propsfile), is to set the default value of the Link Library Dependencies Value for each project dependency on the Frameworks and References section in a VS2010 VC settings dialog --
-- or in the subtree of the VS2015 References --
And this is relevant, because when you add a new project reference, the default entry in your vcxproj file will look like this:
...
<ItemGroup>
<ProjectReference Include="..\W32DynLib1\W32DynLib1.vcxproj">
<Project>{96be134d-acb5-....-....-....bb6fe4a7}</Project>
</ProjectReference>
</ItemGroup>
You'll notice that the <LinkLibraryDependecies>true|false</..> sub element is missing here: This means you "global" setting will actually be used to set the default value.
If your global setting is false (or No), the project reference won't link in anything. If it's true, it will link in.
What's more:
If this setting, LinkLibraryDependency, is completely missing from your settings, it will default to true (from the Microsoft.Cpp[.Common].propsfile in the MSBuild folder).
If you happen to have the value This is not used in your global setting, this will be interpreted as true.
If you have the value False is the new truth!, or maybe No way in this setting, it will also be interpreted as true by the build.
The VS2015 GUI will display a warning if it cannot interpret the string here:
The VS2010 GUI will display False for ALL values, except false, even though this is then interpreted as true when building the project.
What's even more:
It seems that when converting old Solutions with vcproj files, the converter will take the old dependencies that were specified in the sln and the value of the vcproj project's Linker option, and actually set the LinkLibraryDependency for each ProjectReference it inserts into the new vcxproj - thats one reason I thought that this is a dead option for so long - most of our projects have a conversion history dating back to VS2005.
Here the thing is you have to go to, project properties -> common properties -> framework and references and then add new reference to your projects. Then only it will work in VS 2010 not like in early versions of VS
This has to be set in the Properties / Common / Frameworks and References
Alternatively you can add something like the thing below in your vcxproj file, of course use the actual project you're referencing and the uuid of that project.
<ItemGroup>
<ProjectReference Include="..\Cpp\Cpp.vcxproj">
<Project>{c58574bf-9dd8-4cf8-b5b6-6551f2f3eece}</Project>
</ProjectReference>
</ItemGroup>
It seems like you also have to set
<IgnoreImportLibrary>false</IgnoreImportLibrary>
in the REFERENCED project.
I am maintaining a large codebase and some vcproj files are used in different solutions. Due to some horrendous configuration and dependencies it seems the best way to handle some build issues is to #ifdef the code but in order to do that I need to set a preprocessor definition at the solution file level and not at the vcproj level.
Is that possible?
How to do it?
I believe what you may want to do is create a project property sheet with the VS Project Manager that all the projects could inherit from. This would allow you to set any common project settings, including preprocessor macros, in a single location and inherit them as you need.
Select all the projects in your solution. Project + Properties, C/C++, Preprocessor, Preprocessor Definitions. Add
/DSOLUTION=$(SolutionName)
You can now test the SOLUTION macro value in your source code.
I finally find somethings that suits me
in my "C:\Users\\AppData\Local\Microsoft\MSBuild\v4.0"
I change it a little bit for:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)\$(SolutionName).props" Condition="Exists('$(SolutionDir)\$(SolutionName).props')"/>
</Project>
So now, if a "mysolution.props" lays beside of "mysolution.sln" then I get a property sheet for the entire solution without changing anything inside my projects. It becomes a new features for my Visual Environement
2008 sln's are really dumb, they only have lists of projects/files to put in the solution explorer and project dependencies, so I don't think that's an option.
My gut instinct is to do something with relative paths. For example, in your stdafx.h's you could #include "....\project_configuration.h", then for building sln a, you'd check things out to one dir, and sln b another. Each would have its separate project_configuration.h.
I believe you could do something similar with vsprops files, which are essentially #includes for vcproj files, though I've found them a bit annoying to maintain over time.
Another approch:
Edit your vcxproj (or your vcxproj.user) with something like this
<PreprocessorDefinitions Condition="'$(SolutionName)'=='NameOfYourSolution'">
YOUR_DEFINITION;%(PreprocessorDefinitions)
</PreprocessorDefinitions>
it's not perfect as it depends on your sln filename.
It would be great if we use a $(SolutionConfiguration) variable instead.
Unfortunatly, I only see variable for project configuration: $(Configuration).
In any case, it does the trick...
Well, I also looked at
Define a preprocessor value from command line using MSBuild
and
msbuild, defining Conditional Compilation Symbols
and those might work as well, however our build system is pretty brittle right now and I am not in a position to change it all.
The solution I came up with was to clone the build configuration for the project in a solution and give it a different name. Then I added a macros/preprocessor definition to that new configuration.
It appears to work as desired. So one solution uses the old "release" configuration and the other solution uses a clone "ReleaseSpecial" (not the name I really used) configuration with different preprocessor defs.
Not ideal, but it works.
It would be nice if the propoerties were easier to deal with or SLN files could pass in preprocessor defs.