I've built a NuGet package that packages managed and unmanaged dependencies, some of which are runtime dependencies, and so I have several Targets in my .targets file for the NuGet file which copy those dependencies to the bin folder of the project consuming the NuGet package, and I'd like for the consumer to not have to insert the CallTarget nodes in their .csproj file manually.
My package's .targets file looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CopyDeviceIntegrationNativeDependencies">
<Message Text="Copying Device Integration native dependencies to $(OutDir)..." Importance="high" />
<ItemGroup>
<TDDeviceIntegrationNativeDependencies Include="$(MSBuildProjectDirectory)\..\Packages\TDDeviceIntegration*\lib\native\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(TDDeviceIntegrationNativeDependencies)" DestinationFiles="#(TDDeviceIntegrationNativeDependencies->'$(OutDir)\%(Filename)%(Extension)')" ></Copy>
</Target>
<Target Name="CopyDeviceIntegrationContentDependencies">
<Message Text="Copying Device Integration content dependencies to $(OutDir)..." Importance="high" />
<ItemGroup>
<TDDeviceIntegrationContentDependencies Include="$(MSBuildProjectDirectory)\..\Packages\TDDeviceIntegration*\content\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(TDDeviceIntegrationContentDependencies)" DestinationFiles="#(TDDeviceIntegrationContentDependencies->'$(OutDir)\%(Filename)%(Extension)')" ></Copy>
</Target>
<Target Name="CopyDeviceIntegrationManagedDependencies">
<Message Text="Copying Device Integration managed dependencies to $(OutDir)..." Importance="high" />
<ItemGroup>
<TDDeviceIntegrationManagedDependencies Include="$(MSBuildProjectDirectory)\..\Packages\TDDeviceIntegration*\lib\*.*" />
</ItemGroup>
<Copy SourceFiles="#(TDDeviceIntegrationManagedDependencies)" DestinationFiles="#(TDDeviceIntegrationManagedDependencies->'$(OutDir)\%(Filename)%(Extension)')"></Copy>
</Target>
</Project>
My consuming .csproj file needs to have this in it for those copies to happen:
<Target Name="AfterBuild">
<CallTarget Targets="CopyDeviceIntegrationNativeDependencies" />
<CallTarget Targets="CopyDeviceIntegrationContentDependencies" />
<CallTarget Targets="CopyDeviceIntegrationManagedDependencies" />
</Target>
Considering the fact that there can only be one AfterBuild target in this .csproj file (as far as I know), is there anyway that I can insert those CallTarget nodes for the consumer upon installation of the NuGet package so that the consumer doesn't have to add those in manually? I've never run across a NuGet package that forces the consumer to have to make changes to their .csproj file after installation.
Thanks!
You can make all of the targets hook into the build themselves, not requiring any changes by project authors:
<Target Name="Foo" AfterTargets="AfterBuild"> …
Note that it looks like you just want to include files to the output directory, which can be done without targets as static item groups:
<ItemGroup>
<Content Include="$(MSBuildThisFileDirectory)path\relativetotargetsfile\somecontent\**"
Link="\%(Filename)%(Extension)"
Visible="false"
CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
This will support incremental build. Change the Link metadata if you need the content to be put into subdirectories.
Related
I need some help writing a post build event that would work cross platform. The following in my csproj file will work on windows but not Unix. Thanks.
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="copy /Y "$(TargetDir)bin\*.dll" "$(TargetDir)*.dll"" />
</Target>
For this specific case, it might be easier to use the MSBuild Copy Task.
In your csproj file:
<ItemGroup>
<MySourceFiles Include=$(TargetDir)\bin\*.dll"/>
</ItemGroup>
<Target Name="CopyFiles">
<Copy
SourceFiles="#(MySourceFiles)"
DestinationFolder="$(TargetDir)"
/>
</Target>
I have a .NET Standard 2.0 project that is being packed into a NuGet package upon build. (Using the .csproj approach to generate a package on build) This project contains a file ApiClientSettings.json, which I want to copy to the output directory of the consuming project.
I have the following in my .csproj:
<ItemGroup>
<Content Include="ApiClientSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
</Content>
</ItemGroup>
According to MS documentation, the PackageCopyToOutput attribute should do exactly what I want to do, but it does not seem to work when I install the package into a .NET Framework project. (Works perfectly for .NET Core project)
Am I missing anything else?
Here is my entire .csproj file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IsPackable>true</IsPackable>
<TargetsForTfmSpecificBuildOutput>$(TargetsForTfmSpecificBuildOutput);CopyProjectReferencesToPackage</TargetsForTfmSpecificBuildOutput>
<AllowedOutputExtensionsInPackageBuildOutputFolder>
$(AllowedOutputExtensionsInPackageBuildOutputFolder);.json
</AllowedOutputExtensionsInPackageBuildOutputFolder>
<Version>1.0.0</Version>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<FileVersion>1.0.0.0</FileVersion>
<IncludeContentInPack>true</IncludeContentInPack>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<OutputPath></OutputPath>
</PropertyGroup>
<ItemGroup>
<Content Include="ApiClientSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.7" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.5" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DbModelLibrary\DbModelLibrary.csproj">
<ReferenceOutputAssembly>true</ReferenceOutputAssembly>
<IncludeAssets>DbModelLibrary.dll</IncludeAssets>
</ProjectReference>
</ItemGroup>
<Target DependsOnTargets="ResolveReferences" Name="CopyProjectReferencesToPackage">
<ItemGroup>
<BuildOutputInPackage Include="#(ReferenceCopyLocalPaths->WithMetadataValue('ReferenceSourceTarget', 'ProjectReference'))" />
</ItemGroup>
</Target>
</Project>
Here is my generated .nuspec file aswell:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>Iso17025ApiClient</id>
<version>1.0.0</version>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<dependencies>
<group targetFramework=".NETStandard2.0">
<dependency id="Microsoft.AspNet.WebApi.Client" version="5.2.7" exclude="Build,Analyzers" />
<dependency id="Microsoft.Extensions.Configuration" version="3.1.5" exclude="Build,Analyzers" />
<dependency id="Microsoft.Extensions.Configuration.Json" version="3.1.5" exclude="Build,Analyzers" />
<dependency id="Microsoft.Extensions.Http" version="3.1.5" exclude="Build,Analyzers" />
<dependency id="Newtonsoft.Json" version="12.0.3" exclude="Build,Analyzers" />
</group>
</dependencies>
<contentFiles>
<files include="any/netstandard2.0/appsettings.json" buildAction="Content" copyToOutput="true" />
</contentFiles>
</metadata>
</package>
I am adding the file structure of the consuming .NET Framework apps:
When the NuGet package reference is used, the file gets copied to the output directory, from the global package directory, but does not get added to the solution.
If the packages.config is used, the file gets copied to the solution but does not get marked as "Copy to Output" even though it is specified to do so in the NuGet package.
This probably has to do with the changes between pacakges.config and PackageReference NuGet package management? The difference between contentFiles and content probably.
What still confuses me, is that the .NET Core apps use PackageReference and the behaviour is different as with the .NET Framework apps.
In the .NET Core apps the appsettings.json is added to the solution AND marked as "Copy to Output".
I would like the users to have roughly the same experience with the package no matter which target framework they are using. (I do not want them to manually add an appsettings.json JUST because they are using .NET Framework. Is this avoidable in some way?
I have tried this in plain projects with these lines from your sample and it works.
<ItemGroup>
<Content Include="ApiClientSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
<PackagePath>contentFiles\any\any\;content</PackagePath>
</Content>
</ItemGroup>
However, there is something wrong with your generated NuSpec. This line in the contentFiles tag is wrong.
<files include="any/netstandard2.0/ApiClientSettings.json" buildAction="Content" copyToOutput="Always" />
According to your project file, instead of netstandard2.0 it should be any and the attribute for copyToOutput allows for true or false, but not Always. The files tag should look like this.
<files include="any/any/ApiClientSettings.json" buildAction="Content" copyToOutput="true" />
Maybe there are leftovers from testing your package and your caches are corrupted, because your NuSpec does not match the given project file. In Visual Studio you can clear the caches with Tools > Options > NuGet Package Manager > General > Clear all caches or delete your global .nuget folder contents.
Just for you to compare, this is the library project.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<ItemGroup>
<None Remove="ApiClientSettings.json" />
</ItemGroup>
<ItemGroup>
<Content Include="ApiClientSettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
<PackageCopyToOutput>true</PackageCopyToOutput>
<PackagePath>contentFiles\any\any\;content</PackagePath>
</Content>
</ItemGroup>
</Project>
This is the PackageReference in both .NET Framework and .NET Core projects.
<ItemGroup>
<PackageReference Include="ClassLibrary" Version="1.0.0" />
</ItemGroup>
The behavior you recognize between PackageReference and packages.config is because the latter does not support contentFiles. You will have to resort to using the content folder and a custom MS Build .targets file, like this.
I would like the users to have roughly the same experience with the package no matter which target framework they are using.
NuGet packages behave very different not only in terms of PackageReference and packages.config but also whether you use the old-style project format or the new SDK-style project format in your libraries and applications. Consequently, it is hard to provide the same experience in all cases. I recommend you to use SDK-style projects with PackageReference to gain access to the latest features.
Last time I had to find out how to extract some files from a Nuget package in took me at least 6 months but I finally managed to find the solution.
The thing is that, this solution assumes I have a .nupkg file and manually add a .targets file to perform the extraction process.
Now, things are different:
I don't have any .nupgk file, we generate one automatically on our VSTS server using the dotnet pack command. Then we consume the package from our Nuget server
We can't afford to take another 6 months to find the solution
Here is my ProjectName.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<Authors>Jérôme MEVEL</Authors>
<Version>1.0.3</Version>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<!-- This answer got many votes on a Github thread so I tried just in case -->
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<!-- Just trying that too-->
<IncludeBuildOutput>true</IncludeBuildOutput>
<IncludeContentInPack>true</IncludeContentInPack>
<!-- I wanted to see the full generated Nuget package -->
<IncludeSource>true</IncludeSource>
<!-- desperate attempt -->
<TargetsForTfmSpecificBuildOutput>GetMyPackageFiles</TargetsForTfmSpecificBuildOutput>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="1.50.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
<PackageReference Include="NLog" Version="4.5.8">
<!-- Let's try this too just in case-->
<IncludeAssets>all</IncludeAssets>
</PackageReference>
<PackageReference Include="NLog.Extensions.Logging" Version="1.2.1">
<IncludeAssets>all</IncludeAssets>
</PackageReference>
<PackageReference Include="NLog.Web.AspNetCore" Version="4.6.0">
<IncludeAssets>all</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.5.1" />
</ItemGroup>
<ItemGroup>
<!-- Added the file to the root folder in case <IncludeAssets>all</IncludeAssets> is looking there -->
<Content Include="NLog.config">
<Pack>true</Pack>
<PackagePath>NLog;;</PackagePath>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<!-- My desired path to look for the file -->
<Content Include="NLog\NLog.config">
<Pack>true</Pack>
<PackagePath>NLog;;</PackagePath>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<!-- This is the default setting, perfectly working when I reference the project instead of installing the Nuget package -->
<ItemGroup>
<None Update="NLog\NLog.config">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<!-- desperate attempt -->
<Target Name="GetMyPackageFiles">
<ItemGroup>
<BuildOutputInPackage Include="NLog/Nlog.config">
<TargetPath>NLog</TargetPath>
</BuildOutputInPackage>
</ItemGroup>
</Target>
</Project>
As you can see I tried several different settings. This MsBuild results in a NLog.config file included in a NLog folder at the root of the Nuget package file.
During my different tries, depending of the configuration I set, I was able to end-up with the NLog.config file at src/ProjectName.Logging/NLog/NLog.config or even at lib/netstandard2.0/Nlog.config.
So my file is definitely included in my Nuget package file but isn't copied in the output directory of the project that consumes the package.
I tried to specify a .nuspec file when generating my package with dotnet pack like described here but I was never able to get a desired result (either only my NLog.config was included in the Nuget package or all the source files). Moreover, this has several downsides like overriding the configuration in the .csproj file or adding useless complexity. I believe what I want to achieve could be done without using a .nuspec file (maybe I'm wrong).
I noticed the build/ProjectName.targets file is missing in my package and this is probably the missing piece. So how to add this .targets file without manually modifying the package?
Is there another way to copy my config file out of the Nuget package to the output directory?
I really hope someone could help me solve this issue. This is the 2nd time I want to perform the same operation but with a slight difference and once again this is hard to do.
Thanks a lot
It is possible to copy files without the .nuspec file, if you create your nuget from the .csproj file as described here.
And to copy files from nuget to output directory, create a ProjectName.targets file with the following content:
<ItemGroup>
<LogFiles Include="$(MSBuildThisFileDirectory)\..\contentFiles\LogFiles\*.config" />
</ItemGroup>
<Target Name="CopyLogFiles" BeforeTargets="Build">
<Copy SourceFiles="#(LogFiles)" DestinationFolder="$(TargetDir)CopiedLogFiles\" />
</Target>
In your .csproj file add:
<ItemGroup Label="FilesToCopy">
<Content Include="ProjectName.targets" PackagePath="build/ProjectName.targets" />
<Content Include="LogFiles\*.config" Pack="true" PackagePath="contentFiles\LogFiles">
<PackageCopyToOutput>true</PackageCopyToOutput>
</Content>
</ItemGroup>
The paths and names can of course be freely chosen.
This should copy all .config files to a folder called CopiedLogFiles in the output directory!
Ok I finally found the solution and that includes a .nuspec file and a .targets file as well.
The ProjectName.csproj just needs to include this
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
<NuspecFile>ProjectName.Logging.nuspec</NuspecFile>
</PropertyGroup>
<!-- This is just for some projects referencing this project directly instead of the Nuget package -->
<ItemGroup>
<Content Include="NLog\NLog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Dapper" Version="1.50.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
<PackageReference Include="NLog" Version="4.5.8" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.2.1" />
<PackageReference Include="NLog.Web.AspNetCore" Version="4.6.0" />
<PackageReference Include="System.Data.Common" Version="4.3.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.5.1" />
</ItemGroup>
</Project>
In the ProjectName.nuspec you put everything related to the Nuget package
<?xml version="1.0"?>
<package >
<metadata>
<id>ProjectName.Logging</id>
<version>1.1.0</version>
<authors>Jérôme MEVEL</authors>
<description>Just a logging component</description>
<releaseNotes>Extract the NLog.config file automatically</releaseNotes>
<dependencies>
<dependency id="Dapper" version="1.50.5" />
<dependency id="Microsoft.Extensions.DependencyInjection" version="2.1.1" />
<dependency id="Microsoft.Extensions.Logging" version="2.1.1" />
<dependency id="Microsoft.Extensions.Logging.Abstractions" version="2.1.1" />
<dependency id="NLog" version="4.5.8" />
<dependency id="NLog.Extensions.Logging" version="1.2.1" />
<dependency id="NLog.Web.AspNetCore" version="4.6.0" />
<dependency id="System.Data.Common" version="4.3.0" />
<dependency id="System.Data.SqlClient" version="4.5.1" />
</dependencies>
</metadata>
<files>
<file src="bin\Release\netstandard2.0\ProjectName.Logging.dll" target="lib/netstandard2.0/ProjectName.Logging.dll" />
<file src="ProjectName.Logging.targets" target="build/ProjectName.Logging.targets" />
<file src="NLog/Nlog.config" target="content/Nlog.config" />
</files>
</package>
And finally the ProjectName.targets. Careful! The file is located in the Nuget cache of the machine. You will be able to see it at the root of your project in Visual Studio but not in the Windows Explorer (in Windows at least). So if you modify the file in Visual Studio, it will modify it for ALL other projects on this machine referencing the same Nuget package (and same version).
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Content Include="$(MSBuildThisFileDirectory)\..\content/NLog.config">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Project>
I generate the Nuget package using the dotnet pack nuget pack command (now that I've more experience I know that dotnet pack doesn't work well with a .nuspec file, there are several bugs)
Here is the result:
Finally I can just install my package and during the build process, the Nlog.config file will be copied out of the Nuget cache to the output directory of my project.
I think that How do you set nuget contentFiles CopyToOutput value to true when using a .Net Standard library .csproj? provides a better answer to this question.
https://github.com/NuGet/NuGet.Client/pull/1450
You can set PackageCopyToOutput to true in the .csproj to declare the nuget content file as "CopyToOutput=true". That way any project referencing the nuget will have the content file marked with <CopyToOutput>true</CopyToOutput> in the referring csproj file, instructing msbuild to copy the content file to the ouput directory:
In the .csproj of the nuget project:
<Content Include="...">
<PackageCopyToOutput>true</PackageCopyToOutput>
</Content>
How can I include a file in the list of files in solution explorer without including it as a dependency for compilation?
I have a .targets file that generates .cs files, similar to the examples in this answer.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CoreCompileDependsOn>$(CoreCompileDependsOn);GenerateCode</CoreCompileDependsOn>
</PropertyGroup>
<ItemGroup>
<Sources Include="..\sources\*.txt" />
</ItemGroup>
<Target Name="GenerateCode" Inputs="#(Sources)" Outputs="#(Sources->'generated\%(Filename).cs')">
<!-- run executable that generates files -->
<ItemGroup>
<Compile Include="generated\*.cs" />
</ItemGroup>
</Target>
</Project>
This builds correctly and consecutive builds don't rebuild the project unnecessarily. The resulting .cs files are not visible in the solution explorer. The generated code also isn't found by intellisense.
If I add the files with ItemGroups in the .csproj, the generated files are visible in the solution explorer, but subsequent builds result in rebuilding the project unnecessarily. The genereated code still isn't found by intellisense.
<ItemGroup>
<Sources Include="..\sources\*.txt">
<Link>sources\%(Filename)%(Extension)</Link>
</Sources>
<!-- using None instead of Compile on the next line makes no difference -->
<Compile Include="#(Sources->'generated\%(Filename).cs')">
<Generator>MSBuild:Compile</Generator>
<Link></Link>
</Compile>
</ItemGroup>
How can I tell msbuild that the .cs files included in the project are inconsequential to the build and therefore shouldn't trigger rebuilding the entire project?
Move the code generation to BeforeCompile instead of CoreCompileDependsOn. this will keep the generation of the files from tirggering the subsequent builds.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="BeforeCompile" DependsOnTargets="GenerateCode">
</Target>
<Target Name="GenerateCode" Inputs="#(Sources)" Outputs="#(Sources->'generated\%(Filename).cs')">
<!-- run executable that generates files -->
</Target>
</Project>
If you include all of the generated files in the .csproj, the visual studio intellisense will work.
<ItemGroup>
<Sources Include="..\sources\*.txt">
<Link>sources\%(Filename)%(Extension)</Link>
<LastGenOutput>generated\%(Filename).cs</LastGenOutput>
</Sources >
<Compile Include="#(Sources->'generated\%(Filename).cs')">
<Link></Link>
</Compile>
</ItemGroup>
Due to the extreme amount of .resx files in our application, I have created the following MSBuild script to compile all language .resx files into .resource, then embed them into satellite resource assemblies.
<Project DefaultTargets="Main" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Res Include = "Dialog\*.ja-JP.resx">
<Culture>ja-JP</Culture>
<Project>Dialog</Project>
</Res>
</ItemGroup>
<Target Name="Main">
<Message Text="$(destination)"/>
<CallTarget Targets="CompileResources" />
<CallTarget Targets="BuildSatelliteAssemblies" />
<CallTarget Targets="CopyToFolder" Condition="$(destination)!=''"/>
<CallTarget Targets="CleanUp" />
</Target>
<Target Name="CompileResources">
<GenerateResource Sources="#(Res)" PublicClass="true" >
<Output ItemName="Resources" TaskParameter="OutputResources"/>
</GenerateResource>
</Target>
<Target Name="BuildSatelliteAssemblies" DependsOnTargets="CompileResources">
<MakeDir Directories="%(Res.Culture)"/>
<AL OutputAssembly="%(Culture)\%(Project).resources.dll"
Version="0.0.0.0"
Culture="%(Culture)"
ProductName="%(Project)"
Title="%(Project)"
EmbedResources="#(Resources)"/>
</Target>
<Target Name="CopyToFolder" DependsOnTargets="BuildSatelliteAssemblies">
<MakeDir Directories="$(destination)\%(Res.Culture)"/>
<CreateItem Include="%(Res.Culture)\*.dll" AdditionalMetadata="Culture=%(Res.Culture)">
<Output ItemName="SatelliteAssemblies" TaskParameter="Include"/>
</CreateItem>
<Copy DestinationFolder="$(destination)\%(Culture)"
SourceFiles="#(SatelliteAssemblies)" />
</Target>
<Target Name="CleanUp">
<Delete Files="#(Resources)"/>
</Target>
</Project>
The satellite assemblies seem to compile and embed correctly however when I place them with my application, they are not recognized and it defaults back to the default culture resources. If I build the project with Visual Studio and use the assemblies it creates with that, they load fine.
I must be missing something in causing the application to recognize my externally built assemblies. They are all named the same and the sizes are nearly the same.
In the AL target, you can set an internal namespace to be used by each resource file in the assembly. Setting the correct namespace allowed the application to correctly find the resources in the assemblies.