I have solution TestName.sln(Class Library) which is built on Target Framework .Net Standard 2.0.
TestName.sln includes some dependencies 1,2 (includes its own packages), 3, from NuGet, look like a tree.
When I packed TestName.sln I got TestName.dll and I will want to use TestName.dll in SSIS 2017.
I added TestName.dll to Global Assembly Cache without any problem.
But when I try to use TestName.dll I see the classes only from TestName.sln and don't see the classes and can't use dependencies 1,2,3,4 which I included in TestName.sln.
What will I need to do in order to have all dependencies in one .dll file if possible?
Or how I should link all dependencies to one for use in SSIS 2017?
You can use ILMerge to centralize multiple dependencies into a single assembly.
Firstly, to install ILMerge.MSBuild.Tasks package from nuget.
Secondly, edit the *.csproj file of the project that you want to merge by adding the code below.
<UsingTask TaskName="ILMerge.MSBuild.Tasks.ILMerge" AssemblyFile="$(SolutionDir)\packages\ILMerge.MSBuild.Tasks.1.0.0.3\tools\ILMerge.MSBuild.Tasks.dll" />
<Target Name="AfterBuild">
<ItemGroup>
<MergeAsm Include="$(OutputPath)$(TargetFileName)" />
<MergeAsm Include="$(OutputPath)LIB1_To_MERGE.dll" />
<MergeAsm Include="$(OutputPath)LIB2_To_MERGE.dll" />
</ItemGroup>
<PropertyGroup>
<MergedAssembly>$(ProjectDir)$(OutDir)MERGED_ASSEMBLY_NAME.exe</MergedAssembly>
</PropertyGroup>
<Message Text="ILMerge #(MergeAsm) -> $(MergedAssembly)" Importance="high" />
<ILMerge InputAssemblies="#(MergeAsm)" OutputFile="$(MergedAssembly)" TargetKind="SameAsPrimaryAssembly" />
</Target>
Lastly, build your project as usual.
Related
I want to merge one .NET DLL assembly and one C# Class Library project referenced by a VB.NET Console Application project into one command-line console executable.
I can do this with ILMerge from the command-line, but I want to integrate this merging of reference assemblies and projects into the Visual Studio project. From my reading, I understand that I can do this through a MSBuild Task or a Target and just add it to a C#/VB.NET Project file, but I can find no specific example since MSBuild is large topic. Moreover, I find some references that add the ILMerge command to the Post-build event.
How do I integrate ILMerge into a Visual Studio (C#/VB.NET) project, which are just MSBuild projects, to merge all referenced assemblies (copy-local=true) into one assembly?
How does this tie into a possible ILMerge.Targets file?
Is it better to use the Post-build event?
The "MSBuild ILMerge task" (or MSBuild.ILMerge.Task) NuGet package makes this process quite simple. It defaults to merging any "copy local" references into your main assembly.
Note: Although the packages have similar names, this one is different from ILMerge.MSBuild.Tasks that Davide Icardi mentioned in his answer. The one I'm suggesting here was first published in August 2014.
Here an alternative solution:
1) Install ILMerge.MSBuild.Tasks package from nuget
PM> Install-Package ILMerge.MSBuild.Tasks
2) Edit the *.csproj file of the project that you want to merge by adding the code below:
<!-- Code to merge the assemblies into one:setup.exe -->
<UsingTask TaskName="ILMerge.MSBuild.Tasks.ILMerge" AssemblyFile="$(SolutionDir)\packages\ILMerge.MSBuild.Tasks.1.0.0.3\tools\ILMerge.MSBuild.Tasks.dll" />
<Target Name="AfterBuild">
<ItemGroup>
<MergeAsm Include="$(OutputPath)$(TargetFileName)" />
<MergeAsm Include="$(OutputPath)LIB1_To_MERGE.dll" />
<MergeAsm Include="$(OutputPath)LIB2_To_MERGE.dll" />
</ItemGroup>
<PropertyGroup>
<MergedAssembly>$(ProjectDir)$(OutDir)MERGED_ASSEMBLY_NAME.exe</MergedAssembly>
</PropertyGroup>
<Message Text="ILMerge #(MergeAsm) -> $(MergedAssembly)" Importance="high" />
<ILMerge InputAssemblies="#(MergeAsm)" OutputFile="$(MergedAssembly)" TargetKind="SameAsPrimaryAssembly" />
</Target>
3) Build your project as usual.
Some more information that might be useful to some people implementing Scott Hanselman's solution.
When I first set this up it would complain about not being able to resolve references to System.Core, etc.
It is something to do with .NET 4 support. Including a /lib argument pointing to the .NET 4 Framework directory fixes it (in fact just include the $(MSBuildBinPath)).
/lib:$(MSBuildBinPath)
I then found that IlMerge would hang while merging. It was using a bit of CPU and a lot of RAM but wasn't outputting anything. I found the fix on stackoverflow of course.
/targetplatform:v4
I also found that some of the MSBuild properties used in Scott's blog article relied on executing MsBuild from the project's directory, so I tweaked them a bit.
I then moved the targets & ilmerge.exe to the tools folder of our source tree which required another small tweak to the paths...
I finally ended up with the following Exec element to replace the one in Scott's original article:
<Exec Command=""$(MSBuildThisFileDirectory)Ilmerge.exe" /lib:$(MSBuildBinPath) /targetplatform:v4 /out:#(MainAssembly) "$(MSBuildProjectDirectory)\#(IntermediateAssembly)" #(IlmergeAssemblies->'"%(FullPath)"', ' ')" />
UPDATE
I also found Logic Labs answer about keeping the CopyLocal behaviour and just excluding ilMerged assemblies from CopyLocal essential if you are using Nuget packages. Otherwise you need to specify a /lib argument for each package directory of referenced assemblies that aren't being merged.
The article Mixing Languages in a Single Assembly in Visual Studio seamlessly with ILMerge and MSBuild at http://www.hanselman.com/blog/MixingLanguagesInASingleAssemblyInVisualStudioSeamlesslyWithILMergeAndMSBuild.aspx demonstrates how to use ILMerge and MSBuild within a Visual Studio Project.
One issue I found with the article at: http://www.hanselman.com/blog/MixingLanguagesInASingleAssemblyInVisualStudioSeamlesslyWithILMergeAndMSBuild.aspx.
If you have any references that you do not wish to ILMerge then the code in the article fails because it overrides the default CopyLocal behaviour to do nothing.
To fix this - Instead of:
<Target Name="_CopyFilesMarkedCopyLocal"/>
Add this entry to the targets file instead (.NET 3.5 only) (to filter out the non-ilmerge copylocal files, and treat them as normal)
<Target Name="AfterResolveReferences">
<Message Text="Filtering out ilmerge assemblies from ReferenceCopyLocalPaths" Importance="High" />
<ItemGroup>
<ReferenceCopyLocalPaths Remove="#(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.IlMerge)'=='true'" />
</ItemGroup>
</Target>
This is a great article that will show you how to merge your referenced assemblies into the output assembly. It shows exactly how to merge assemblies using msbuild.
My 2 cents - I picked up #Jason's response and made it work for my solution where I wanted to generate the *.exe in the bin/Debug folder with all *.dlls inside the same folder.
<Exec Command=""$(SolutionDir)packages\ILMerge.2.13.0307\Ilmerge.exe" /wildcards /out:"$(SolutionDir)..\$(TargetFileName)" "$(TargetPath)" $(OutDir)*.dll" />
Note: This solution is obviously hardcoded into the ILMerge nuget package version. Please let me know if you have some suggestions to improve.
Edit the *.csproj file of the project that you want to merge by adding the code below:
<Target Name="AfterBuild" Condition=" '$(ConfigurationName)' == 'Release' " BeforeTargets="PostBuildEvent">
<CreateItem Include="#(ReferenceCopyLocalPaths)" Condition="'%(Extension)'=='.dll'">
<Output ItemName="AssembliesToMerge" TaskParameter="Include" />
</CreateItem>
<Exec Command=""$(SolutionDir)packages\ILMerge.3.0.29\tools\net452\ILMerge.exe" /internalize:"$(MSBuildProjectPath)ilmerge.exclude" /ndebug /out:#(MainAssembly) "#(IntermediateAssembly)" #(AssembliesToMerge->'"%(FullPath)"', ' ')" />
<Delete Files="#(ReferenceCopyLocalPaths->'$(OutDir)%(DestinationSubDirectory)%(Filename)%(Extension)')" />
</Target>
Notes:
Replace $(SolutionDir)packages\ILMerge.3.0.29\tools\net452\ILMerge.exe with whatever path you have the ILMerge.exe in.
You can remove the Condition in the target to also merge on Debug but then the Debugger might not work
If you are not excluding anything you can remove: /internalize:"$(MSBuildProjectPath)ilmerge.exclude"
Check out this article by Jomo. He has a quick process to hack ILMerge into the msbuild system
http://blogs.msdn.com/jomo_fisher/archive/2006/03/05/544144.aspx
I would like to support backward compatibility in my application.
Simply saying - one app needs to work using different versions of a dll depending on a flag which the app get's during runtime.
I've simplified everything and created a test solution with 2 projects in it.
Each project has it's own version of the same nuget package.
I picked System.Drawing.Common cause it has no dependencies.
ClassLibrary1 contains System.Drawing.Common of version 4.5.0.
ClassLibrary2 contains System.Drawing.Common of version 6.0.0.
Both projects have same output path:
<OutputPath>..\DEBUG\</OutputPath>
When I build my solution I get just one System.Drawing.Common.dll in my output folder:
Cause both dlls have one name and only version is different.
The desired behavior on the pictures below:
Distribute the nuget package dependencies into different folders according to versions.
Add suffix to the nuget package dependencies according to versions.
The idea is in controlling output of the nuget package dependencies.
Do you have any idea how I can achieve that ?
P.S. all other logic - resolving dependencies according versions etc is out of scope of this question.
It's possible.
First you need to add GeneratePathProperty to PackageReference element in csproj file
<ItemGroup>
<PackageReference Include="System.Drawing.Common">
<Version>4.5.0</Version>
<GeneratePathProperty>true</GeneratePathProperty>
</PackageReference>
</ItemGroup>
It allows us using $(PkgSystem_Drawing_Common) variable which contains a path to the nuget package.
Then we need to create a msbuild targets file
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopyNugetDll" BeforeTargets="BeforeCompile" Outputs="System.Drawing.Common.dll">
<XmlPeek XmlInputPath="$(ProjectPath)" Query="Project/ItemGroup/PackageReference[#Include='System.Drawing.Common']/Version/text()">
<Output TaskParameter="Result" PropertyName="NugetPackageVersion" />
</XmlPeek>
<ItemGroup>
<NugetrDll Include="$(PkgSystem_Drawing_Common)\lib\net461\System.Drawing.Common.dll" />
</ItemGroup>
<Message Text="Copying #(NugetrDll) to $(OutDir)" Importance="high" />
<Exec Command="copy $(PkgSystem_Drawing_Common)\lib\net461\System.Drawing.Common.dll $(OutDir)\System.Drawing.Common.$(NugetPackageVersion).dll" />
</Target>
</Project>
Here using xpath we select version from project.assets.json file and save it in NugetPackageVersion variable. Exec copy is used to copy the dll to a specific location with a specific prefix which contains a value from NugetPackageVersion variable.
Lastly you need to include msbuild targets file to a project
<Import Project="CopyDll.targets" />
This just isn't how package resolution works in .NET, you get one version of each package which is decided at restore time.
There may be some funky options if you have a very niche problem, but it sounds like maybe you're trying to solve a common problem in an uncommon way which is generally a bad idea.
Typically for the problem of backwards compatibility the onus is on the publisher of the library rather than the consumer of the library to make sure it all works by not making breaking API changes.
I have a class library which I just migrated to the new csproj format that has a dependency on SQLite.Core.
SQLite has native dependencies x64/SQLite.Interop.dll and x86/SQLite.Interoop.dll which used to get copied to the output folder for any project adding my package. This was taken care of by NuGet by automatically including the following import in the old csproj file:
<Import Project="..\packages\System.Data.SQLite.Core.1.0.106\build\net451\System.Data.SQLite.Core.targets" Condition="Exists('..\packages\System.Data.SQLite.Core.1.0.106\build\net451\System.Data.SQLite.Core.targets')" />
After migrating to the new csproj I can build my library as a NuGet package which correctly includes SQLite.Core as a dependency however whoever consumes my NuGet library does not get the native dependencies coppied to the output folder.
On the other hand, if the consumer first adds the SQLite.Core then add my package everything works correctly.
Any idea how I can restore the behaviour before the migration? Below is the migrated csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PackageId>My.Library</PackageId>
<Description>Some Library.</Description>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net452</TargetFramework>
<AssemblyName>My Library</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.106" />
</ItemGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
</Project>
Since System.Data.SQLite.Core uses build-time logic to include the package to the content, the default dependency settings for PackageReference aren't a good fit if you build a library referencing that package. By default, PackageReference will not forward contentFiles, build assets and analyzers for transitive references.
To change that behavior, edit your PackageReference to:
<PackageReference Include="System.Data.SQLite.Core" Version="1.0.106" PrivateAssets="none"/>
This will ensure that the package will behave exactly the same when reference directly or transitively via your library. In your specific case, PrivateAssets could also be contentFiles;analyzers since only build would need to be forwarded but since the package only contains build assets, it doesn't matter.
See Controlling dependency assets for more details.
I am building a modular .NET core application that can load extensions at runtime using MEF. I have 2 projects, one is a library that I want to be able to load at runtime, then I have my main application that will do the loading.
My library project has some Nuget dependencies. In order to load my library at runtime, I need those Nuget dependencies to be available next to the library at runtime, but building using VS2017 does not include these Nuget DLLs as part of the output.
How do I get Nuget DLLs included when I build my library?
Edit: I have tried dotnet publish and dotnet pack, but both of those make me a nupkg file only containing my DLL and not the nuget DLLs I need with it. Also, I can't load a nupkg file at runtime very easily, which is why I'd like to just get the resulting assemblies themselves on their own.
For what it's worth, this is what my csproj looks like:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<AssemblyName>JSON.plugin</AssemblyName>
<IncludeBuiltProjectOutputGroup>true</IncludeBuiltProjectOutputGroup>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Composition" Version="1.0.31" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\BDDGen.Types\BDDGen.Types.csproj" />
</ItemGroup>
</Project>
In order to make the build process copy all referenced dll files from NuGet packages from the cache folder into the build output, set this property inside a <PropertyGroup>:
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
I have a solution with two projects in it. First project is called Library1, which references project two called Referencelibrary. I am trying to embed the DLLs for ReferenceLibrary inside Library1's nuget package so that I don't have to publish 2 separate nuget packages. I've been able to embed ReferenceLibrary's DLL into the nuget package (so it seems) by adding the entries below into my csproj file:
<ItemGroup>
<ProjectReference Include="..\ReferenceLibrary\ReferenceLibrary.csproj">
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
<IncludeAssets>ReferenceLibrary.dll</IncludeAssets>
<IncludeAssets>ReferenceLibrary.pdp</IncludeAssets>
</ProjectReference>
</ItemGroup>
But when I import the nuget package and try to run my test app, I get the following exception:
I assumed that the DLLs had been embedded because prior to adding the "IncludeAssets" to the csproj, I wasn't able to import the nuget package because it was trying to reference the ReferenceLibrary nuget package. But after adding those entries, it allowed me to import it. But now it bombs at run-time. Any help would be greatly appreciated. Thanks!
;)
There is now a more up-to-date workaround described here. Simply add the TargetsForTfmSpecificBuildOutput and Target nodes to your .csproj file as shown below.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
</ItemGroup>
</Target>
</Project>
Official documentation for this extension point in the pack target can be found here.
You might also then want to add the attribute PrivateAssets="All" to the ProjectReference element to suppress that project from appearing as a NuGet dependency in the generated package, e.g.:
<ProjectReference Include="MyNonNugetDependentProject.csproj" PrivateAssets="All" />
This appears to be a known limitation with the built-in NuGet packaging for core projects in Visual Studio 2017 and is discussed here:
https://github.com/NuGet/Home/issues/3891
In that discussion thread there is a hack that worked for me:
https://github.com/NuGet/Home/issues/3891#issuecomment-309792369
<ItemGroup>
<_PackageFiles Include="$(OutputPath)\ReferencedProjectDll.dll">
<BuildAction>None</BuildAction>
<PackagePath>lib\net45\</PackagePath>
</_PackageFiles>
</ItemGroup>
Note that you need to change the assembly name AND you might need to also change the package path to match the version of .NET Framework you are using. Above example is for 4.5 but you might be on the more current 4.6.
For placing DLLs within a folder of my choice I used the other customization point as described at Microsoft.
So I ended up with the following:
<PropertyGroup>
<IncludeBuildOutput>false</IncludeBuildOutput> <!-- omit the package creating library itself -->
<PackProject>true</PackProject>
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);CreatePackNupkg</TargetsForTfmSpecificContentInPackage>
</PropertyGroup>
<Target Name="CreatePackNupkg">
<ItemGroup>
<TfmSpecificPackageFile Include="$(OutputPath)\<whatever>.*.dll">
<PackagePath>folder/subfolder</PackagePath>
</TfmSpecificPackageFile>
</ItemGroup>
</Target>
The NuGet.Pack package is created the same way (have a look).
We could not include referenced project DLLs with three or above projects.
For example, the ReferenceLibrary.dll will be added to References for Library1 when project Library1 reference to project ReferenceLibrary. But only the Library1.dll will be add to the References of test app project when you reference project Library1 to project test app. The referenced project DLLs “Referencelibrary” will be omitted . See Flexible Project-to-Project References for more detail.
If you want to embed the DLLs for ReferenceLibrary inside Library1's nuget package and reference it to the test app project, you can add the ReferenceLibrary project reference to test app project after add the reference project Library1
or set ReferenceLibrary.dll as a dependence of Library1 project, you can add the below entries into Library1.csproj, then package the Library1 and install this package to test app via NuGet:
<ItemGroup>
<Reference Include="ReferenceLibrary, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ReferenceLibrary.1.0.0\lib\net461\ReferenceLibrary.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
Update:
If we want to embed the DLLs for ReferenceLibrary inside Library1's nuget package, we should make sure the ReferenceLibrary.dll include in the Library1 package, No matter how we embed the DLLS. So you can add ReferenceLibrary.dll to the Library1.nuspec as file when we pack the Library1 package and set target to the lib folder. Below is my Library1.nuspec:
<?xml version="1.0"?>
<package >
<metadata>
<id>Library1</id>
<version>1.0.0</version>
<authors>xxxx</authors>
<owners>xxxx</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Package description</description>
<releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
<copyright>Copyright 2017</copyright>
<tags>Test</tags>
</metadata>
<files>
<file src="..\Library1\bin\Debug\Referencelibrary.dll" target="\lib\net461" />
<file src="..\Library1\bin\Debug\Library1.dll" target="\lib\net461" />
</files>
</package>
Note that: You also need include the Library1.dll in the Library1.nuspec.
Some extended version of Neo answer. Additionally instruct MSBuild to pack all pdb symbols to chosen symbols format. (built in pdb or snupkg)
<PropertyGroup>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
<TargetsForTfmSpecificDebugSymbolsInPackage>$(TargetsForTfmSpecificDebugSymbolsInPackage);CopyProjectReferencesSymbolsToPackage</TargetsForTfmSpecificDebugSymbolsInPackage>
</PropertyGroup>
<Target Name="CopyProjectReferencesToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
</ItemGroup>
</Target>
<Target Name="CopyProjectReferencesSymbolsToPackage" DependsOnTargets="ResolveReferences">
<ItemGroup>
<SupposedSymbolFiles Include="#(ReferenceCopyLocalPaths->'%(RootDir)%(Directory)%(Filename).pdb')"/>
<TfmSpecificDebugSymbolsFile Include="#(SupposedSymbolFiles)" Condition="Exists('%(Identity)')">
<TargetFramework>$(TargetFramework)</TargetFramework>
</TfmSpecificDebugSymbolsFile>
</ItemGroup>
</Target>