WIx CustomAction missing localization folders - dll

I have a problem with a custom action that has a reference to a localized dll(let's call it X.dll). When I build the CA project I can see in bin folder several language folders(de, fr, ...) as it should, each containing X.resources.dll corresponding to that language. The problem appears when I run the setup. When it extracts the CA binaries the language folders are not there; instead I can see only one X.resources.dll file.
I need all of those resources dll to be present or my action will fail.
Does anyone has a clue why this happens and why what's extracted is not the same with what's in the bin folder of the CA project?
The setup is configured to build all cultures.

There is a way to include more files into the cmd line for makesfxCA.exe. I think you need to define $(CustomActionContents) msbuild property which is a ;-delimited list of paths to files to include.
You can see the msbuild target that is run at the end of your custom action project in the wix.ca.targets file.
I don't know if this will try to put all the included files in the root path or not so this may cause issues with duplicate name dlls. Alternatively you can figure out how to create one localization dll for all supported languages but this may be difficult (although I'm sure it must be possible).

As Brian Sutherland writes, to make the final CA dll include additional files you can add them to the semicolon separated string defined by $(CustomActionContents).
The build action used to pack the CA dll is called PackCustomAction. The additional files needs to be added to the property before that build action.
As noted in this blog post, to create a target folder structure for the unpacked CA dll you need to add the files with this syntax:
<TargetDir>\<TargetFileName>=<SourceDir>\<SourceFileName>
Solution example using a separate build target in the custom action project file, that will execute before the PackCustomAction and add a ru\resources.dll file to the CA dll.
<Target Name="AddFilesToCA" BeforeTargets="PackCustomAction">
<PropertyGroup>
<CustomActionContents>$(CustomActionContents);ru\resources.dll=$(ProjectDir)..\ResourceProject\bin\$(ConfigurationName)\ru\resources.dll</CustomActionContents>
</PropertyGroup>
</Target>

Related

MSBuild wildcard matching of files for deployment

I am hoping to be able to use MSBuild to capture a subtree of files produced during the build of a project using Microsoft.NET.Sdk.Web and include them in deployment. So far, I have found that if I simply create the files inside the project folder before deployment, then it works but only for certain filetypes. DLLs, for instance, are excluded, presumably assumed to be non-content items. I have been poking around how the deployment stuff works, and have found the <ResolvedFileToPublish> element that I can put into <ItemGroup>, but I haven't figured out how it might be possible to employ this with wildcards. Specifically, I have a post-build step that places files into a folder deployment within the project, and I want all files in that subtree to be included in the package that is produced by /p:DeployOnBuild=true. How can I tack my files onto the deployment stage so that they're included in the ZIP even if they don't look like content items?
I have found a solution, in the form of adding a new <Task> set to run immediately after the internal tasks which collect files for publishing. This is not suitable for a long-term solution, since it ties to internal state, but this is a temporary fix and as such I think it's alright.
By adding this to the .csproj:
<Target Name="__CopyDeploymentToPublish" AfterTargets="_CopyResolvedFilesToPublishAlways">
<Exec Command="PowerShell.exe -Version 3.0 -ExecutionPolicy Unrestricted $(SolutionDir)deploy_webapp.ps1 -Source $(SolutionDir)src\IQ.Auth.OAuth2.Web -Target $(PublishDir)" />
</Target>
...my PowerShell script runs right after the standard deployment logic aggregates the files it intends to package up. I can at that point do whatever I want to the files and the way they're left is what'll end up in the ZIP file.

Binary files copied to a wrong folder on build

Recent libgit2sharp Nuget uses a new Nuget feature that allows you to include a piece of a build script in your NuGet. The purpose it to copy a native dll to a subfolder of the bin folder, like that:
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)\..\..\lib\net40\NativeBinaries\amd64\git2-e0902fb.dll">
<Link>NativeBinaries\amd64\git2-e0902fb.dll</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>...
Now, it was all nice and beautiful locally, but when I deployed it to AppHarbor, the native dlls appeared in the /bin folder (in addition to the target subfolder), which caused my app to fail.
The problem lies in the _CopyWebApplicationLegacy target, which does not execute locally (it's run only if you have a non-default output dir), thus I don't have this problem on my dev machine. Namely, it executes the following piece of code:
<!-- Copy items that have been marked to be copied to the bin folder -->
<Copy SourceFiles="#(_SourceItemsToCopyToOutputDirectory)"
DestinationFolder="$(WebProjectOutputDir)\bin"
SkipUnchangedFiles="true"
Retries="$(CopyRetryCount)"
RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)"/>
You can see that the target folder is always /bin -- I believe it's a bug in the Microsoft.WebApplication.targets file (I can't control it on the target machine).
Is there a simple fix, or should I revert to a script in the PostBuild event (which I'll have to update with each new version)?
As mentioned here: https://github.com/libgit2/libgit2sharp/issues/1089#issuecomment-111000722
the way AppHarbor is building your project, it's triggering the old _CopyWebApplicationLegacy target, and that is basically broken. It messes up all files that are using the Copy to Output Directory property by putting them directly into the output directory instead of respecting the relative folder structure. It also doesn't run any web.config transforms you may have.
You can make your project use the newer _CopyWebApplication target instead by adding the following to your project file:
<UseWPP_CopyWebApplication>True</UseWPP_CopyWebApplication>
<PipelineDependsOnBuild>False</PipelineDependsOnBuild>
The thing I'm not sure about is if AppHarbor has some reason why they wouldn't want you to use the newer copy target instead of the old broken one.

MSBuild: Is it possible to specify projects build dependencies in actual project file?

I have a number of different MFC language resource files in an MSBuild system, and I'm trying to build different dlls from each. I have a project file for each in the same directory.
What I'd like to do is specify in the main project file the other project files of the resources to be built.
If I use the
<Import Project="lang_de-DE.xml"/>
construction, the main dll will contain the code from the imported projects (according to MSDN MSBuild documentation).
I don't want to use the
<CreateItem Include=.../>
construction either, I have them all in one directory.
I have lang_main.xml, lang_en-GB.xml, lang_fi-FI.xml, etc. in on directory, and the .rc files for these in a different directory.
What I need to do is have the lang_main.xml project file build the others first, and then build itself, and have the same number of dlls in the end.
Is it possible to solve this?
Thanks
Solved.
What I did was create directories for all languages on the same level with the main language, and put a project file for each of them into the corresponding directory.
Then, I put all the resource files into the main language directory, and in the separate project files I reference their own rc file, by relative path.
Then I used the other language "modules" as dependencies in the main language module, in its project file.

Invoke Custom MSBuild Target on Solution File

I have a solution file (MySolution.sln) with a single project in it (MyProject.vcxproj). I would like to execute a custom target (MyCustomTarget) on my project through the solution. It would look something like this:
msbuild MySolution.sln /t:MyCustomTarget
When I execute the command, I'll get an error message:
MySolution.sln.metaproj : error MSB4057: The target "MyCustomTarget" does not exist in the project. [MySolution.sln]
You can replace MyCustomTarget with any standard targets from Microsoft.Cpp.Win32.targets (e.g.: ClCompile, Link) or any other target of your choice you include from .targets files in MyProject.vcxproj. None of them would work.
When the environment variable msbuildemitsolution is set to 1, I can inspect the generated MySolution.sln.metaproj file. At the bottom 4 targets are specified: Build, Rebuild, Clean, and Publish. Using these targets instead of MyCustomTarget, the project builds ok. Also, if I specify the project file instead of the solution file, it builds too:
msbuild MyProject.vcxproj /t:MyCustomTarget
But using this format, I will lose the OutDir property, manually have to set the Configuration and Platform, so I just lose the benefits of having a solution file.
Is there any way I can use my custom target with the solution file I originally intended?
As far as I understand the problem is that msbuild generates this intermediate project file (mysolution.sln.metproj) but that will won have the imports from MyProject.vcxproj, including the .targets files. No wonder MyCustomTarget is not recognized.
My current workaround is using the project file with msbuild and trying not to miss anything from the solution file:
msbuild MyProject.vcxproj /t:MyCustomTarget /p:Configuration=MyConfig;Platform=MyPlatform;OutDir=MySolution\Platform_MyConfig\
But this is not a proper solution, inflexible, prone to error and does not automatically adapt changes in the solution file.
MSBuild 15 now raises custom targets automatically into the solution metaproj so your initial approach of running the target directly on the solution is now supposed to work.
I think you already answered your question. The answer is NO. There is no target called "MyCustomTarget" inside the .sln.metaproj file, so MSBuild gives you that error message.
Now, to resolve your problem with passing extra parameters on command line. Passing platform and configuration won't be required, if you set defaults in your .vcxproj file. Add this somewhere in your project file, before any of the standard target files are imported:
<Platform Condition="'$(Platform)'==''">MyPlatform</Platform>
<Configuration Condition="'$(Configuration)'==''">MyConfiguration</Configuration>
Configuring OutDir, which is shared across all projects in solution can be done like this. I will assume your solution is structured so that .sln file is in root folder, and all projects are in sub-folders (arbitrarily deep) under the root, or in the same folder as the solution. If this is not the case, you will have to tweak the code a little to adjust to your situation.
Right after you defined Platform and Configuration in your project, add this property group:
<PropertyGroup>
<RootFolder>$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory),MySolutionName.sln))</RootFolder>
<OutDir Condition="'$(OutDir)'==''">$(RootFolder)\$(Platform)_$(Configuration)</OutDir>
</PropertyGroup>
The code above follows your convention of setting OutDir to MySolution\MyPlatform_MyConfiguration.
The downside of all this approach is that you have to manually modify all projects in your solution. However it will give you lots of flexibility in the future. For example, any common settings shared across all projects, could be extracted into single .props file that you can <Import> into every project, so changes to configuration could be done in one place.
In order to use the custom target that exists in your project file while building using the solution file, use the following format:
msbuild MySolution.sln /t:MyProject:MyCustomTarget
Note that if the project is in a sub folder (solution folder) you need to add the folder name:
msbuild MySolution.sln /t:src\MyProject:MyCustomTarget
and if the project name contains spaces or dots they are replaced with underscores.

Deploy SL4 application with localized DLL imports

I have a VS2010 SL4 project which uses an external Silverlight DLL. The project is localized with multiple RESX files, and the DLL is, too. I usually include external DLLs in my solutions as follows:
1) create a set of virtual folders in my solution like (say the imported DLL is named Sample.dll):
/Lib/Sample/Debug
/Lib/Sample/Release
2) create the same folders structure in the file system and copy under Debug and Release the respective versions of the DLL, so that now I find the following files:
/Lib/Sample/Debug/Sample.dll
/Lib/Sample/Release/Sample.dll
3) add to all the client projects in the solution a reference to /Lib/Sample/Debug/Sample.dll.
4) open the .csproj file of each project with the added reference, and change the Debug part of the path with $(Configuration), so that the right Debug/Release version is picked during build.
Now the question is: in my SL4 solution I can follow the same procedure for importing the language-neutral DLL. But what about its satellite resources? For instance, the French version of the imported DLL is built under subfolder fr-FR and named Sample.resources.dll. How should I include it correctly? Even If I try to manually add it in the compiled XAP under folder fr, it is ignored and the application falls back to its neutral culture...
I think I found it, here's a recap for whom may be interested:
open the .csproj file and ensure you add all your desired languages (separated by semicolons) in . For instance, if you support fr-Fr add <SupportedCultures>fr-Fr</SupportedCultures>.
(had to do this manually, I supposed 1. should be enough): once compiled, open your xap (rename it to .zip and open) and add if not present an element like <AssemblyPart Source="fr-FR/Sample.resources.dll" /> for each imported satellite with resources.
Thanks anyway!