Change a reference path based on the build target - msbuild

I have a unit test projects which requires some external dependencies. Those dependendies come in 2 flavors: i386 (........\External\EA\i386\Core.dll) and amd64 (........\External\EA\amd64\Core.dll).
<ItemGroup>
<Reference Include="Core">
<HintPath>..\..\..\..\External\EA\amd64\Core.dll</HintPath>
</Reference>
<Reference Include="Util">
<HintPath>..\..\..\..\External\EA\amd64\Util.dll</HintPath>
</Reference>
MsTest is 32bits and I want the path of those assemblies to be ........\External\EA**i386**\Core.dll. In other words, how to I tell msbuild to pick the right build target.
Thanks

Just put a condition on the references, or as shown below, on the ItemGroup containing them,
<ItemGroup
Condition="'$(Platform)' == 'x64'">
<Reference Include="Core">
<HintPath>..\..\..\..\External\EA\amd64\Core.dll</HintPath>
</Reference>
<Reference Include="Util">
<HintPath>..\..\..\..\External\EA\amd64\Util.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup
Condition="'$(Platform)' == 'Win32'">
<Reference Include="Core">
<HintPath>..\..\..\..\External\EA\i386\Core.dll</HintPath>
</Reference>
<Reference Include="Util">
<HintPath>..\..\..\..\External\EA\i386\Util.dll</HintPath>
</Reference>
</ItemGroup>
You'll have to discovere exactly which values for $(Platform) your project is using, which a simple examination of the XML of the projects will show.

Related

Targeting C++/CLI project to .Net 6.0 - Error : Project does not reference "native,Version=v0.0" framework

Am upgrading my WPF project to .Net 6 which internally refers C++/CLI project. So am trying to upgrade my C++ project also to .Net6.0.
Changed the below properties in .vcxproj
<CLRSupport>NetCore</CLRSupport>
<TargetFramework>net6.0-windows</TargetFramework>
While building the project, it fails with the below error.
Your project does not reference "native,Version=v0.0" framework. Add a reference to "native,Version=v0.0" in the "TargetFrameworks" property of your project file and then re-run NuGet restore.
Anyone please guide me on - referring a C++ project (.Net Framework 4.6) in .Net 6.0
Do we need to upgrade c++ library to .Net 6.0 to consume it in .Net6.0 apps
How can we add reference to “native, Version0.0” in TargetFrameworks
When I made my project changes to get a C++/CLI-based assembly to build using .NET 6, I had the CLRSupport and TargetFramework changes like you show. But, I also had to change the <ItemGroup> containing the Reference elements to instead use a single <FrameworkReference> element. Note that my code called into WPF so there's a lot of those assemblies there.
Old:
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
New:
<ItemGroup>
<FrameworkReference Include="Microsoft.WindowsDesktop.App" />
</ItemGroup>

Msbuild add reference path

I am using TeamCity to build my solutions remotely and one of my projects has a reference path which is different from the build server. Is there anyway that I can change or add the reference hintpath in the command line of the Visual Studio Runner before the project builds so that the dependency is resolved?
<ItemGroup>
<Reference Include="FeedOSAPI_vc10, Version=2.5.2.1, Culture=neutral>
<SpecificVersion>False</SpecificVersion>
<HintPath>path</HintPath>
</Reference>
<Reference Include="FeedOS_Managed_vc10, Version=0.0.0.0, Culture=neutral>
<SpecificVersion>False</SpecificVersion>
<HintPath>"path</HintPath>
</Reference>
</ItemGroup>
One way you can accomplish this is to use environment variables, both on Build Servers and developer workstations. You can create a binary repository, it can be VCS repository or simlpe file server, and after that you can have one source code for projects which will be able to build anywhere, where the prerequisite conditions are met.
Create environment variable, for example BinRepo, both on Build Server and on dev machine.
Use it in the project like this:
<Reference Include="FeedOSAPI_vc10, Version=2.5.2.1, Culture=neutral>
<SpecificVersion>False</SpecificVersion>
<HintPath>$(BinRepo)FeedOS\FeedOSAPI_vc10.dll</HintPath>
</Reference>
<Reference Include="FeedOS_Managed_vc10, Version=0.0.0.0, Culture=neutral>
<SpecificVersion>False</SpecificVersion>
<HintPath>$(BinRepo)FeedOS\FeedOS_Managed_vc10.dll</HintPath>
</Reference>
Thus you have single code base, and you do not need to specify it elsewhere in the project or from the command line (but you have the ability to do so).

Relative Referencing in Visual Studio 2013 (vb.net)

Problem: So, my issue is that I have 3 .dll files I want to include in my software. However, whenever I transfer the .exe to another computer, it looks for my hard coded absolute path in my computer. I want the dlls to be included or embedded such that I can distribute it to others.
Setup:
I have done so by going to the solution explorer and doing Add->Exisiting Item, and add the three dlls into the solution explorer.
Then I went to each .dll's properties and changed them so that they are considered embedded resources. Like so:
Then I went to the references tab of the project property, and added a reference to the three dlls in my code. I originally had them in my /bin/ folder of my project, but I still had this problem. so I added them here in the same folder as my project.
Great, so I compile, copy to another computer, and get an exception (I threw it to a message box for ease) saying it can't find the dll and/or its dependencies. I even put a copy of the dll in the same .exe folder on the separate computer, but the same problem.
What I've Tried: So I tried messing around with the project file. But it looked ok to me. Here's a snippet:
<ItemGroup>
<Reference Include="PcapDotNet.Base, Version=0.10.0.20588, Culture=neutral, PublicKeyToken=4b6f3e583145a652, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>.\PcapDotNet.Base.dll</HintPath>
</Reference>
<Reference Include="PcapDotNet.Core, Version=0.10.0.20632, Culture=neutral, PublicKeyToken=4b6f3e583145a652, processorArchitecture=x86">
<SpecificVersion>False</SpecificVersion>
<HintPath>.\PcapDotNet.Core.dll</HintPath>
</Reference>
<Reference Include="PcapDotNet.Packets, Version=0.10.0.20603, Culture=neutral, PublicKeyToken=4b6f3e583145a652, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>.\PcapDotNet.Packets.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
and later ...
<ItemGroup>
<EmbeddedResource Include="PcapDotNet.Base.dll" />
<EmbeddedResource Include="PcapDotNet.Core.dll" />
<EmbeddedResource Include="PcapDotNet.Packets.dll" />
</ItemGroup>
Question: So, why is it saying it can't find the dll? Where could it still be looking for it in the wrong place?
Thanks everyone!
Thanks for the help guys! I found the solution was a problem with Pcap dot net (pcap.net). The issue was that the supporting computer didn't have the Microsoft Redistributable C++ package. It says that is needed on their website. I thought that was for development only, but turns out it is needed for running as well. I might try to include those dll's in the embedded resources as well to clean it up for the user.

MSBuild, CreateItem to ItemGroup to binplace a target to multiple folders

Short-story: I have a list of with an attribute called true. I want to copy all of these files, to a list of folders, say defined by ...
someFolder
To that end, here's what I'm doing today:
<CreateItem Include="%(Reference.HintPath)"
Condition="'%(Reference.Binplace)' == 'true'"
AdditionalMetadata="DestinationFolder=$(DestinationForReferences)\%(Reference.BinplaceFolder)">
<Output ItemName="Binplace" TaskParameter="Include" />
</CreateItem>
I already have a target called Binplace which internally calls Copy. The problem is that is a single element, and I don't know how I can call Copy on multiple of these items
And in my CSPROJ file, I do this:
<Reference Include="MyCompany.Something.Something">
<HintPath>$(LocalLibraryFolder)\MyCompany.Something.Something.dll</HintPath>
<Binplace>true</Binplace>
</Reference>
<ItemGroup>
<Reference Include="1">
<HintPath>$(LocalLibraryFolder)\1.dll</HintPath>
<Binplace>true</Binplace>
<BinplaceFolder>SubFolder1\SubFolder12</BinplaceFolder>
</Reference>
<Reference Include="2">
<HintPath>$(LocalLibraryFolder)\2.dll</HintPath>
<Binplace>true</Binplace>
<BinplaceFolder>SubFolder2\SubFolder22</BinplaceFolder>
</Reference>
</ItemGroup>
<PropertyGroup>
<LocalLibraryFolder>.</LocalLibraryFolder>
<DestinationForReferences>.</DestinationForReferences>
</PropertyGroup>
<Target Name="CopyReferencedBinaries"
Outputs="%(Reference.Identity)">
<ItemGroup>
<SourceBinaryFullPath Include="%(Reference.HintPath)" />
</ItemGroup>
<PropertyGroup>
<SourceBinaryDir>$(DestinationForReferences)\%(Reference.BinplaceFolder)</SourceBinaryDir>
</PropertyGroup>
<MakeDir Directories="$(SourceBinaryDir)"
Condition="!Exists('$(SourceBinaryDir)')"/>
<Copy SourceFiles="#(SourceBinaryFullPath)"
DestinationFiles="#(SourceBinaryFullPath->'$(SourceBinaryDir)\%(Filename)%(Extension)')" />
</Target>

ItemGroup With Custom Metadata regarding files

I’m trying to create a “Files” task item group with a metadata attribute called “TargetPath” populated with the relative path to a file.
Example:
For these paths:
D:\Test\Blah.exe
D:\Test\Config\fun.config
D:\Test\en-US\my.resources.dll
The output should be:
File Target = Blah.exe
File Target = Config\fun.config
File Target = en-US\my.resources.dll
Here is my best attempt... hopefully this makes my question clearer:
<ItemGroup>
<Files Include="d:\test\**\*" >
<TargetPath>%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</Files>
</ItemGroup>
<Message Text="File Target = #(Files->'%(TargetPath)')"/>
I'd like "TargetPath" populated correctly... currently it appears to be null or empty. Anyone know what I'm missing?
Edit:
Yes, I realize I can do this:
<Message Text="File Target = #(Files->'%(RecursiveDir)%(Filename)%(Extension)')"/>
However, I'm building up this ItemGroup to use the ResolveManifestFiles MSBuild task, which requires that I build up a TaskItem with the TargetPath metadata attribute to be able to customize that value.
You're trying to assign dynamic metadata to an itemgroup before it's created. In your example there's no need to create custom metadata since this information is already part of the well-known metadata, so you can just do:
<ItemGroup>
<Files Include="d:\test\**\*" ></Files>
</ItemGroup>
<Message Text="File Target = #(Files->'%(RecursiveDir)%(Filename)%(Extension)')"/>
Or:
<Message Text="File Target = %(Files.RecursiveDir)%(Files.Filename)%(Files.Extension)"/>
EDIT:
This example uses CreateItem task to dynamically update the itemgroup:
<ItemGroup>
<Files Include="d:\test\**\*" ></Files>
</ItemGroup>
<CreateItem
Include="#(Files)"
AdditionalMetadata="TargetPath=%(RecursiveDir)%(Filename)%(Extension)">
<Output TaskParameter="Include" ItemName="Files"/>
</CreateItem>
Modern MSBuild doesn't require CreateTask (since .NET 3.5).
You can do it like this:
<ItemGroup>
<Files Include="d:\test\**\*" />
<FilesWithMetadata Include="%(Files.Identity)" >
<TargetPath>%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</FilesWithMetadata>
</ItemGroup>
I am liking the CreateItem method to use like this:
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
...
</ItemGroup>
<CreateItem Include="#(Reference)" Condition="'%(Reference.Private)' == 'True'" AdditionalMetadata="TargetPath=$([System.IO.Path]::GetFileName(%(Reference.HintPath)))">
<Output TaskParameter="Include" ItemName="DLLFiles"/>
</CreateItem>
<Message Text="HintPaths: "#(DLLFiles->'$(OutputPath)%(TargetPath)')"" Importance="high" />
I am using Transforms to just get only the file name.
Output:
HintPaths:
"bin\Release\log4net.dll;bin\Release\Newtonsoft.Json.dll;bin\Release\RabbitMQ.Client.dll;bin\Release\ReflectSoftware.Insight.dll"