ResolveAssemblyReference cannot find dll and I cannot force it to look where it is - c++

I have solution with n csharp projects and cpp project on top, this cpp provides interfaces and headers so those csharp ones can be used in other cpp solutions.
The build machine is configured to build csharp project with anyCPU architecture so it provides single assembly per build in Solution\bin\Release. For cpp the anyCpu is not available so I build project twice and store assemblies in Solution\bin\Release\x86 and x64 folders.
This is all to get it packaged in nuget as a single package with .targets file to ease consumption in other cpp projects.
Issue is that cpp project is looking for csharp asseblies using ResolveAssemblyReference and cannot find it, giving missleading message:
ResolveAssemblyReferences:
Primary reference "Implementation".
Could not find dependent files. Expected file "C:\Jenkins\Workspace\Solution\bin\Release\x86\Implementation.dll" does not exist.
Could not find dependent files. Expected file "C:\Jenkins\Workspace\Solution\bin\Release\x86\Implementation.dll" does not exist.
Resolved file path is "C:\Jenkins\Workspace\Solution\bin\Release\x86\Implementation.dll".
Reference found at search path location "".
I tried to alternate ResolveAssemblyReferences behaviour using command line properties, custom targets/properties, but without any luck. The parameters described in https://learn.microsoft.com/en-us/visualstudio/msbuild/resolveassemblyreference-task?view=vs-2017 seem to be computed during the build process and I cannot inject any value, which should be in this case something like $(OutDir)..
The one feasable solution seems to be copy c# dlls into each cpp folder, but I dont think it is the way to solve it properly.
Closes I got is by using /p:ReferencePath like below:
"C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MsBuild\15.0\bin\MsBuild.exe" /p:BuildProjectReferences=false /p:Configuration=Release /p:DebugType=full /p:DebugSymbols=true /p:PlatformToolset=v120 /p:WindowsTargetPlatformVersion=8.1 /p:ForceImportBeforeCppTargets="C:\Jenkins\Workspace\Solution\Cpp.props" /p:OutDir="C:\Jenkins\Workspace\Solution\bin\Release\x86\" /p:Platform=Win32 /t:Build Interface\Interface.vcxproj /p:ReferencePath="C:\jenkins\workspace\Solution\bin\Release"
My custom Cpp.props does:
<Target Name="Output" BeforeTargets="ResolveAssemblyReferences">
<Message Text="AssemblySearchPaths: $(AssemblySearchPaths)" />
</Target>
and by adding /p:ReferencePath it got added to AssemblySearchPaths as second record, after {CandidateAssemblyFiles}; but it is still not finding those dlls

Related

Where exactly is macro $(VCTargetsPath) defined?

We had a discussion before.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/e04e7791-c0c4-4598-b900-310878f5af45/how-can-i-locate-and-change-the-vctargetspath-variable?forum=msbuild
After hours searching .props files, all the $(VCTargetsPath) in .prop and .targets are deleted, but Visual Studio can still recognize this macro/variable correctly.
By modifing Microsoft.Cpp.ToolsetLocation.props, $(VCTargetsPath) can be modified and in consequence VS can no longer create new project.
So, where exactly this macro defined?
I believed that this macro is not and shouldn't be hardcoded.
Where exactly is macro $(VCTargetsPath) defined?
Actually, VCTargetsPath property is defined in the MSBuild system props or targets files under MSBuild folder(C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild). And the files are nested and related to each other. Therefore, the values of various properties are referred to each other in the level-by-level files, and the value of VCTargetsPath is no exception.
==========================================================================
MSBuild is actually equivalent to soft coding. Once MSBuild is installed, it exists as a bunch of dlls and various established props and targets files. You can modify its properties or extend its functions in various ways in the props or targets file. The MSBuild itself is an open source build tool. It is not as unchangeable as you think.
==========================================================================
The VCTargetsPath is actually defined in the msbuild props file called Microsoft.Cpp.ToolsetLocation.props as you said.
It's just that the various targets and props files in MSBuild are nested and interrelated. VCTargetsPath is set as the value of _VCTargetsPathForToolset property while _VCTargetsPathForToolset is defined in another associated targets or props file.
In general, use <import projects="xxxx\xxx.props or targets" /> to embed another file in the current targets or props file.
And there are so many files in MSBuild that if you want to search layer by layer until you find the final specific value, it might seem a little complicated.
Besides, VCTargetsPath is the path of some tools used by MSBuild to compile c++ projects.
The default value in VS2019 is C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160.
You can write a custom target in the xxx.vcxproj file to output its value.
1) write this in xxx.vcxproj file:
<Target Name="test123" AfterTargets="Build">
<Message Importance="high" Text="$(VCTargetsPath)"></Message>
</Target>
2) Then rebuild your project and you can see this:
It is the MSBuild system defined path. If you change it easily, it will cause the project to fail to compile, unless you have a set of self-defined system similar to MSBuild to build C++ projects, and then put it in new path, let the new path overwrite VCTargetsPath property.
In addition, I am curious why you want to find the lowest VCTargetPath value. If you want to know the principle of it, the above answer explains it.
And if you want to overwrite its value, you can just create an environment system variable called VCTargetsPath directly, and then set its value to a new one.
Restart VS to enable such new value. It is much easier than modifying the targets file.

C++ Windows Driver MSB3030 could not copy the file '' because it was not found

VS2017, SDK/WDK, C++ project
we have a c++ solution (driver) that is shared across developers via Team Foundation Services - visualstudio.com (now called azure devops?).
When I perform a get latest source code, and want to rebuild the solution I get two MSB3030 errors:
"Could not copy the file "C:\path of my colleague his file" because it was not found."
I found it strange that I saw on one of the two errors a path of my colleague his pc. He works on C:\ I'm working on E:\
Unloading the project, I saw he path being set here:
<ItemGroup>
<FilesToPackage Include="C:\path of my colleague\foo.xml" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<PackageRelativeDirectory>
</PackageRelativeDirectory>
</FilesToPackage>
We cannot get this solution to build because of the MSB3030. First we have to clean the specific projects individually, rebuild it, then build another project etc.. a few steps to perform manually in the correct order , trial and error, drinking coffee, throwing bananas to the pc and praying that a monkey outputs the code correctly.
Has anyone seen somehting similar regarding MSB3030 errors?
On my pc I see the path of my colleague, but he doesn't see my path (strange!).
C:\Program Files (x86)\Windows
Kits\10\build\WindowsDriver.common.targets(1699,5): error MSB3030:
Could not copy the file 'C:...' because it was not found.
I've set the Any CPU to x64 because it doesn't make any sense for c++.
C++ Windows Driver MSB3030 could not copy the file '' because it was not found
The reason for this issue is that the path of the ItemGroup is an absolute path in the project file:
<ItemGroup>
<FilesToPackage Include="C:\path of my colleague\foo.xml" ...>
...
</FilesToPackage>
</ItemGroup>
Regardless of whether your colleague has added this file to source control, when you pull the code from the TFS server to your local and put the code in a different local folder, the absolute path will bring you a lot of trouble, you need to manually check the code on the TFS server for this file and you need to modify the absolute path of this file in your project. But this problem will reappear after your colleague updates after you submit your code. Because an absolute path cannot be assigned to two different paths C:\ and E:\.
To resolve this issue, you need to change the absolute path to a relative path in the source code. Generally, we prefer to add this file to the Solution/Project folder, then use the MSBuild Macros $(SolutionDir)/$(ProjectDir) to specify it.
Check Common macros for build commands and properties for some more details.
Hope this helps.

x64 build: error MSB8013: This project doesn't contain the Configuration and Platform combination of Debug|Win32

I'm trying to get a pure x64 build running for a complex build with many solutions (some for CI, some for devs) and hundreds of project and have run into "error MSB8013: This project doesn't contain the Configuration and Platform combination of Debug|Win32"
I've reduced it to a simple configuration:
Solution A contains Project X & Project Y. Project X has a project
reference to Project Y.
Solution B contains also contains Project X.
None of the projects or solutions contain a Win32 platform - it's been deleted.
When I build solution B (as x64), I receive
error MSB8013: This project doesn't contain the Configuration and
Platform combination of Debug|Win32
The error message sometimes contains additional information:
This error may also appear if some other project is trying to follow a
project-to-project reference to this project, this project has been
unloaded or is not included in the solution, and the referencing
project does not build using the same or an equivalent Configuration
or Platform.
The problem is the referencing project does "build using the same or an equivalent Configuration or Platform."
Looking at the msbuild diagnostic, project Y is being built because project X contains a project reference to it. But the configuration and platform properties are being deleted just before project Y is built:
1>Task "MSBuild" (TaskId:28)
1> Removing Properties: (TaskId:28)
1> Configuration (TaskId:28)
1> Platform (TaskId:28)
As a result Microsoft.Cpp.Default.props kicks in and sets these to Debug|Win32:
1>Task "Message" (TaskId:11)
1> Configuration=Debug (TaskId:11)
1>Done executing task "Message". (TaskId:11)
1>Task "Message" (TaskId:12)
1> Platform=Win32 (TaskId:12)
1>Done executing task "Message". (TaskId:12)
How can I prevent this and make msbuild pass the chosen configuration & platform?
(I understand the ultimate solution to use NuGet to manage package dependencies, but that isn't feasible in the short term)
I could resolve a similar issue by including all referenced projects into the solution - in this case including Project Y into solution B.
One fix is to hand edit the .vcxproj files to define the default platform:
Open the vcxproj file in a text editor
Add this line in the "Globals" PropertyGroup:
<Platform Condition="'$(Platform)' == ''">x64</Platform>
It seems it is a bug in Microsoft.CppBuild.targets file. On some machines I see two such files: one in C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\Microsoft.CppBuild.targets with ~120KB size, and C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Microsoft.CppBuild.targets with 77KB size. Second one is buggy and gets wrong $(Configuration)|$(Platform) value which yields mentioned error. This problem is probably directly connected with this topic, as VCTargetsPath in my projects pointed into wrong (77KB) Microsoft.CppBuild.targets directory (I use VS2015 so it should use the one from V140 directory). I found two workarounds: 1) replace buggy file or 2) modify HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\MSBuild\ToolsVersions\14.0\14.0\VCTargetsPath10 registry to point right file.
I couldn't add a comment because of lack of rep, but in the same vein of user1825216's answer, I'm running MSBuild from a command line so I resolved this error by adding -p:Platform=x64 to the build command instead of modifying the .vcxproj.
I find it difficult to see what you're trying to achieve through this. Do you always want to build X AND Y? Even in the solution that has no reference to Y? Would be fairly illogical to me. Though, I can offer you some solutions to fixing / supressing this issue, based on what you want.
If you want to build X&Y with A and X with B
In Solution A, set the other project as a build dependency, instead of referencing project Y in project X. You can't set it as long as it refers to the other project (I always remove the reference in the .vcxproj, it's somewhere at the bottom of the project), but once you removed the inner project reference, you can have the solution reference to it and then it should work just fine.
When you build Solution A with MSBuild, it'll build X AND Y, while building solution B with MSBuild
Else
If you want to build X&Y with either solution
Sorry, but that's not really possible within MSBuild at the moment. I'd recommend adding project Y to the solution and referencing it, but then, why would you? You already have a project that has them both included, and if you want different outputs, you can easily make different configurations.

Compiling C++Builder project on command line

Is there a way to compile a C++Builder project (a specific build configuration) from the command line?
Something like:
CommandToBuild ProjectNameToBuild BuildConfiguration ...
There are different ways for automating your builds in C++Builder (as of my experience, I'm speaking about old C++Builder versions like 5 and 6).
You can manually call compilers - bcc32.exe (also dcc32.exe, brcc32.exe and tasm32.exe if you have to compile Delphi units, resource files or assembly language lines of code in your sources) and linker - ilink32.exe.
In this case, you will need to manually provide the necessary input files, paths, and keys as arguments for each stage of compilation and linking.
All data necessary for compilation and linking is stored in project files and, hopefully there are special utilities, included in the C++Builder installation, which can automate this dirty work, provide necessary parameters to compilers and linker and run them. Their names are bpr2mak.exe and make.exe.
First you have to run bpr2mak.exe, passing your project *.bpr or *.bpk file as a parameter and then you will get a special *.mak file as output, which you can use to feed on make.exe, which finally will build your project.
Look at this simple cmd script:
#bpr2mak.exe YourProject.bpr
#ren YourProject.mak makefile
#make.exe
You can provide the real name of "YourProject.mak" as a parameter to make.exe, but the most straightforward way is to rename the *.mak file to "makefile", and then make.exe will find it.
To have different build options, you can do the following:
The first way: you can open your project in the IDE, edit options and save it with a different project name in the same folder (usually there are two project files for debug and release compile options). Then you can provide your building script with different *.bpr files. This way, it looks simple, because it doesn't involves scripting, but the user will have to manually maintain coherency of all project files if something changes (forms or units added and so on).
The second way is to make a script which edits the project file or make file. You will have to parse files, find compiler and linker related lines and put in the necessary keys. You can do it even in a cmd script, but surely a specialised scripting language like Python is preferable.
Use:
msbuild project.cbproj /p:config=[build configuration]
More specifics can be found in Building a Project Using an MSBuild Command.
A little detail not mentioned.
Suppose you have external dependencies and that the .dll file does not initially exist in your folder
You will need to include the external dependencies in the ILINK32.CFG file.
This file is usually in the folder
C:\Program Files (x86)\Borland\CBuilder6\Bin\ilink32.cfg
(consider your installation location)
In this file, place the note for your dependencies.
Example: A dependency for TeeChart, would look like this (consider the last parameter):
-L"C:\Program Files (x86)\Borland\CBuilder6\lib";"C:\Program Files (x86)\Borland\CBuilder6\lib\obj";"C:\Program Files (x86)\Borland\CBuilder6\lib\release";"C:\Program Files (x86)\Steema Software\TeeChart 805 for Builder 6\Builder6\Include\";"C:\Program Files (x86)\Steema Software\TeeChart 805 for Builder 6\Builder6\Lib\"
You will also need to include the -f command to compile.
In cmd, do:
//first generate the file.mak
1 - bpr2mak.exe MyProject.bpr
//then compile the .mak
2 - make.exe -f MyProject.mak
You can also generate a temporary mak file with another name, as the answer above says, directly with bpr2mak
bpr2mak.exe MyProject.bpr -oMyTempMak.mak

MSBuild: Custom.After.Microsoft.Common.targets for native C++ projects in VS2010

I've read about the use of "Custom.Before.Microsoft.Common.targets" and "Custom.After.Microsoft.Common.targets" in order to execute a custom target before/after every project build and I would like to use this technique in order to change version info while building on our TeamCity build server.
The problem is that although it works for C# projects, it doesn't seem to work for native C++ projects.
After some digging around in the Microsoft.Cpp.targets file I found out that for native C++ projects this seems to be implemented through setting $(ForceImportBeforeCppTargets) and $(ForceImportAfterCppTargets).
I can't seem to find a single piece of information on the web about this technique for native C++ apps though, so I'm asking if I'm looking in the right direction or not.
Any help is appreciated.
For VC++ projects it is a bit different. You define a file to be imported either at the beginning or at the end of the project. To use this approach you need to define values for the properties ForceImportBeforeCppTargets or ForceImportAfterCppTargets. For example if you want a file to be included at the beginning of the project you can pass in the value at the command line. For example I just created a dummy VC++ project named CppTets01. Then I created the two sample files below.
Before.proj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CustomTargetInBefore" AfterTargets="Build">
<Message Text="From CustomTargetInBefore" Importance="high"/>
</Target>
</Project>
After.proj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CustomTargetInAfter" AfterTargets="Build">
<Message Text="From CustomTargetInAfter" Importance="high"/>
</Target>
</Project>
Then I executed the following command:
msbuild CppTest01.vcxproj
/p:ForceImportBeforeCppTargets="C:\Temp\_NET\ThrowAway\CppTest01\CppTest01\Before.proj";
ForceImportAfterCppTargets="C:\Temp\_NET\ThrowAway\CppTest01\CppTest01\After.proj"
The result was
C:\Temp_NET\ThrowAway\CppTest01\CppTest01>msbuild CppTest01.vcxproj /p:ForceImportBeforeCppTargets="C:\Temp_NET\ThrowAway\CppTest01\C
ppTest01\Before.proj";ForceImportAfterCppTargets="C:\Temp_NET\ThrowAway\CppTest01\CppTest01\After.proj"
Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.1]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 10/18/2010 8:32:55 AM.
Project "C:\Temp\_NET\ThrowAway\CppTest01\CppTest01\CppTest01.vcxproj" on node 1 (default targets).
InitializeBuildStatus:
Creating "Debug\CppTest01.unsuccessfulbuild" because "AlwaysCreate" was specified.
ClCompile:
All outputs are up-to-date.
All outputs are up-to-date.
ManifestResourceCompile:
All outputs are up-to-date.
Link:
All outputs are up-to-date.
Manifest:
All outputs are up-to-date.
FinalizeBuildStatus:
Deleting file "Debug\CppTest01.unsuccessfulbuild".
Touching "Debug\CppTest01.lastbuildstate".
CustomTargetInBefore:
From CustomTargetInBefore
CustomTargetInAfter:
From CustomTargetInAfter
Done Building Project "C:\Temp\_NET\ThrowAway\CppTest01\CppTest01\CppTest01.vcxproj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.21
As you can see from the output the targets were successfully injected into the build process. If you want to relate this back to Custom.Before.Microsoft.Common.targets and Custom.Before.Microsoft.Common.targets then you should know that the technique used there is a bit different. Specifically if you create those files they are automatically imported into every C#/VB.NET project. In this case you have to set this property. You really have two options here:
You can set this property as an environment variable
You can use another technique, ImportBefore & ImportAfter which is specific to VC++
For #1 let me explain a bit. In MSBuild when you access a property with the syntax $(PropName) then if a property with the name PropName doesn't exist MSBuild will look up in the environment variables to see if such a value exists, if it does then that value is returned. So if you have a build server in which you want to include a file for each VC++ build, then just create those properties as environment variables. Now for the other technique.
ImportBefore/ImportAfter
In VC++ a new concept is introduced. In Microsoft.Cpp.Win32.targets you can see the declaration at the top of the .targets file.
<Import Project="$(VCTargetsPath)\Platforms\Win32\ImportBefore\*.targets"
Condition="Exists('$(VCTargetsPath)\Platforms\Win32\ImportBefore')" />
Then there is one towards the bottom
<Import Project="$(VCTargetsPath)\Platforms\Win32\ImportAfter\*.targets"
Condition="Exists('$(VCTargetsPath)\Platforms\Win32\ImportAfter')" />
A similar import declaration exists for the other target platforms as well. Take a look at the files at %ProgramFiles32%\MSBuild\Microsoft.Cpp\v4.0\Platforms\ for the specific names.
With this technique if you want a file to be imported then simply create a file that ends with .targets and place it into the appropriate folder. The advantage of this is that it will be imported into every VC++ build for that platform, and that you can create many different files. The drawback is that you have to place them in those specific folders. That's the main difference between both techniques. With this first technique you can specify the file location via property and its not automatically included for every build, but for the second approach it is but you cannot change the location
You can also add project content into one of *.props files from directory %LOCALAPPDATA%\Microsoft\MSBuild\v4.0\
It make same effect.