I'm completely new to WiX and setting up custom installers in general, so I apologise for the topic of the question!
I have a internal business application (a diary), which builds and works well, so I followed tutorials/official documentation as to adding the WiX project and referencing the Diary's csproj to it.
After then building and running this most basic version of a WiX installer, the output directory has a lone exe file, which crashes moments after loading with a File Not Found Exception.
My guess is that it has not built in either Crystal Report or NLog, both of which are referenced in my CSProj.
My question is this, how do I get WIX to include and build those project references to the output???
Any help is greatly appreciated!
/Antony
Unfortunately you will have to do some manual labor in order to get your projects right. I would take either of the two following approaches which require you to edit the .wixproj file:
Use HeatProject task. You will have to do this for all referenced projects and it will give you separate .wxs files for all of them. After this reference the component groups in those files in a feature of your WIX based setup.
<Target Name="BeforeBuild">
<HeatProject ToolPath="$(WixToolPath)" AutogenerateGuids="true" OutputFile="OutputFile.wxs" SuppressFragments="true" Project="ReferencedProject.csproj" ProjectOutputGroups="Binaries" />
</Target>
Use HeatDirectory task. Following will pick up everything in the bin folder of your project, including any binaries for the referenced projects, and give you a single .wxs containing UniqueComponentGroupName which can be included in any feature.
<Target Name="BeforeBuild">
<PropertyGroup>
<DefineConstants>BINFOLDER=PATH\TO\YourProject\bin\$(Configuration)</DefineConstants>
</PropertyGroup>
<HeatDirectory OutputFile="OutputFile.wxs" Directory="PATH\TO\YourProject\bin\$(Configuration)" KeepEmptyDirectories="true" DirectoryRefId="INSTALLFOLDER" ComponentGroupName="UniqueComponentGroupName" SuppressCom="true" SuppressFragments="true" SuppressRegistry="true" SuppressRootDirectory="true" GenerateGuidsNow="true" ToolPath="$(WixToolPath)" PreprocessorVariable="var.BINFOLDER" />
</Target>
Unlike the (now defunct) Setup Project project in older versions of Visual Studio, WiX does not do automatic reference detection. You'll have to add each referenced project to the WiX project manually, just as you did for the main project.
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
Recently I have added all of our SSIS projects into a continuous integration pipeline. The projects are built using MSBuild in TeamCity, packaged and pushed to a nuget feed. We deploy them using Octopus and some hand cranked PowerShell built on the back os SQL server management objects (SMO). It all works, with the exception of one project. The project in question contains some script tasks which reference an external assembly. That assembly is built in the same pipeline and its assembly version numbers are updated by part of the process. The problem lies in the fact that the SSIS project now references a strong named dll in the GAC which does not exist because the version numbers have changed.
Does anyone know of a way to either updated the reference at build time on the CI server or override the version number at the point of deployment?
I know this post is quite old but it has been viewed a lot so here's a solution I have found.
You need to include a 'targets' file for the build.
Here's an example Targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<E10BOs>C:\Integrations\E10 Uplifts\Epicor 10.2.600.3</E10BOs>
</PropertyGroup>
<Target Name="BeforeResolveReferences">
<CreateProperty Value="$(E10BOs);$(AssemblySearchPaths)">
<Output TaskParameter="Value" PropertyName="AssemblySearchPaths" />
</CreateProperty>
</Target>
</Project>
The property group E10BOS (and there can be more than 1) then defines the path to the dll's of the version you want to build against.
It needs to be saved as myTargetsFile.targets
Then in a regular VS project you could add this line to the project file (outside of VS in notepad)
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), myTargetsFile.targets))\myTargetsFile.targets" />
</Project>
This uses GetDirectoryNameOfFileAbove to search up the folder tree until it finds you targets file and then imports it. Very useful if you have a lot of different projects all requiring a version change and you don't have to figure out any relative paths!
In SSIS however this doesn't seem to work.
What does is hard-wiring the path to the targets file in the package .dtsx file, again edit it in notepad and add the following line (you will probably see the csharp entry near the end of the project tag as before:
<Import Project="C:\Integrations\E10 Uplifts\Epicor 10.2.600.3\myTargetsFile.targets" />
This will pass through the information of the project references into the scripts.
Then with all of you projects using a targets file changing the version is done by changing the path to the version folder you want them to use.
Hope that helps?
I have many C# script source files in the project tree marked as Build Action: None
The problem is: if the source file is marked as Build Action: None then Go To Declaration, Go To Implementation and other navigation and refactoring functions of Visual Studio and Resharper doesn't work anymore.
The script files are eventually compiled by the CSharpCodeProvider once the app is running and generally behave 100% the same as if they were in the actual code base (the original executable assembly).
The difference here is that the source files are not compiled by MSBuild, but by the app itself.
How can I force Visual Studio/build process to analyze the files (as if they were going to be compiled) without including them into final executable assembly?
If you edit the main project file (.csproj) and add an AfterBuild target, you can invoke any custom action on post build. The following example calls compile.exe for all Build Action=None items in the project with extension of .proj.
<Target Name="AfterBuild">
<ItemGroup>
<Scripts Include="#(None)" Condition="'%(Extension)' == '.proj'" />
</ItemGroup>
<Message Text="Scripts files: #(Scripts)" Importance="high" />
<Exec Command="Compile.exe %22%(Scripts.FullPath)%22"
WorkingDirectory="$(MSBuildProjectDirectory)"
Condition="'#(Scripts)' != ''" />
</Target>
One possible option is to have different build configurations, with different settings for the files in question.
You can have a development configuration, where the files are set to 'compile', and a deployment configuration, where they're set to 'none'.
That way, you'll get nice Intellisense when working on your code, and no compilation happening when you flip to 'deployment' configuration before you build.
I'm totally stumped by this one. The ideas that I've found through google stack overflow don't work for me and I've no idea why.
We recently upgraded the project to Visual Studio 2012 and MVC 4 with .NET 4.5 and now it won't publish properly.
We have another branch that just has the project publishing in Visual Studio 2012 without the upgrade to MVC4 or .NET4.5 and that seems to work, so I'm guessing it isn't a Visual Studio issue. Just something with the way that MVC 4 is set up in our project. MVC 3 was added by referencing the DLLs directly from a lib folder we had created in the source control (but outside of any projects). MVC 4 is added via NuGet.
The issue is that System.Web.Helpers (amongst others) don't appear in the bin directory of the published application. This means that when it is put on the test server it won't run as the DLL is missing.
I've set Copy Local to be TRUE (actually, it already was, but I turned if off and on again). I also read somewhere that if the file exists in the GAC it won't matter what this setting is, it won't copy. However, I've checked and it isn't in the GAC.
I've ensured that the reference in the MVC application was pointing to the version of the file in the NuGet packages folder. (It wasn't originally, but I've manually edited the csproj file to do that as removing and readding the NuGet package didn't help)
I've added a post-build event to copy the relevant files (which doesn't affect the publish, although they are in the project's bin directory)
I've attempted to put a _bin_deployableAssemblies folder in place, as per Phil Haack's blog, but it seems this doesn't work in Visual Studio 2012.
I've tried modifying the csproj file (which is just an MSBuild file) to copy the relevant files for me, as per this SO answer. But for what ever reason that doesn't want to work either.
I've run out of things I can try. Well, I can always copy the file manually as some SO answers have suggested elsewhere, but that defeats the purpose.
Any ideas?
UPDATE
Added more things in the bullet points above for things I've tried that don't work for me.
How about this:
Right-click on the MVC project in solution explorer.
Add Deployable Dependencies
Tick MVC
I've found an answer. It is a bit of a hack because I couldn't get the MSBuild copy command to work, so I used the Exec command to get xcopy to do the copying for me.
First of all I added a folder called _bin_PublishAssemblies to the project and put in there the assemblies that I need to publish that the build process is not picking up already.
Then I added the following towards the end of the csproj file:
<Target Name="AfterBuild">
<Message Text="Build | Copying assemblies to output folder ($(OutputPath))" Importance="high" />
<Exec Command="ECHO Going to copy from '$(ProjectDir)\_bin_PublishAssemblies'" />
<Exec Command="xcopy $(ProjectDir)\_bin_PublishAssemblies\* $(OutputPath) /Y" />
</Target>
<Target Name="CopyBinFiles" AfterTargets="CopyAllFilesToSingleFolderForPackage" BeforeTargets="MSDeployPublish">
<Message Text="Deploy | Copying assemblies to output folder ($(_PackageTempDir)\bin\)" Importance="high" />
<Exec Command="ECHO Going to copy from '$(ProjectDir)\_bin_PublishAssemblies'" />
<Exec Command="xcopy $(ProjectDir)\_bin_PublishAssemblies\* $(_PackageTempDir)\bin\ /Y" />
</Target>
I just finished setting up an out-of-place build system for our existing C++ code using inherited property sheets, a feature that seems to be specific to the Visual C++ product. Building out-of-place requires that many of the project settings be changed, and the inherited property sheets allowed me to change all the necessary settings just by attaching a property sheet to the project. I am migrating our team from C++/MFC for UI to C# and WPF, but I need to provide the same out-of-place build functionality, hopefully with the same convenience. I cannot seem to find a way to do this with C# projects - I first looked to see if I could reference an MsBuild targets file, but could not find a way to do this. I know I could just use MsBuild for the whole thing, but that seems more complicated than necessary. Is there a way I can define a macro for a directory and use it in the output path, for example?
I'm not quite sure what an "out-of-place" build system is, but if you just need the ability to copy the compiled files (or other resources) to other directories you can do so by tying into the MSBuild build targets.
In our projects we move the compiled dlls into lib folders and put the files into the proper locations after a build is complete. To do this we've created a custom build .target file that creates the Target's, Property's, and ItemGroup's that we then use to populate our external output folder.
Our custom targets file looks a bit like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectName>TheProject</ProjectName>
<ProjectDepthPath>..\..\</ProjectDepthPath>
<ProjectsLibFolder>..\..\lib\</ProjectsLibFolder>
<LibFolder>$(ProjectsLibFolder)$(ProjectName)\$(Configuration)\</LibFolder>
</PropertyGroup>
<Target Name="DeleteLibFiles">
<Delete Files="#(LibFiles-> '$(ProjectDepthPath)$(LibFolder)%(filename)%(extension)')" TreatErrorsAsWarnings="true" />
</Target>
<Target Name="CopyLibFiles">
<Copy SourceFiles="#(LibFiles)" DestinationFolder="$(ProjectDepthPath)$(LibFolder)" SkipUnchangedFiles="True" />
</Target>
<ItemGroup>
<LibFiles Include=" ">
<Visible>false</Visible>
</LibFiles>
</ItemGroup>
</Project>
The .csproj file in Visual Studio then integrates with this custom target file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" ... >
...
<Import Project="..\..\..\..\build\OurBuildTargets.targets" />
<ItemGroup>
<LibFiles Include="$(OutputPath)$(AssemblyName).dll">
<Visible>false</Visible>
</LibFiles>
</ItemGroup>
<Target Name="BeforeClean" DependsOnTargets="DeleteLibFiles" />
<Target Name="AfterBuild" DependsOnTargets="CopyLibFiles" />
</Project>
In a nutshell, this build script first tells MSBuild to load our custom build script, then adds the compiled file to the LibFiles ItemGroup, and lastly ties our custom build targets, DeleteLibFiles and CopyLibFiles, into the build process. We set this up for each project in our solution so only the files that are updated get deleted/copied and each project is responsible for it's own files (dlls, images, etc).
I hope this helps. I apologize if I misunderstood what you mean by out-of-place build system and this is completely useless to you!
Is there a way I can define a macro for a directory and use it in the output path
Have you looked at the pre-build and post-build events of a project?
Actually, pre-build and post-build events seem to be solely a place to add batch-file type commands. This would not help me to set up standard build directories for our projects, unfortunately. And having these events create batch files seems like a very 1980's approach for a modern language like C#, IMO.
After digging some more, and experimenting, I have found that you can add an <Import> directive into your .csproj file. When you do this, the IDE pops up a warning dialog that there is an unsafe entry point in your project - but you can ignore this, and you can make it not appear at all by editing a registry entry, evidently. So this would give me a way to get the variables containing the directory paths I need into the .csproj file.
Now to get the Output Path to refer to it - unfortunately when you add a string like "$(MySpecialPath)/Debug" to the Output Path field, and save the project, the $ and () chars are converted to hex, and your file get's put in a Debug directory under a directory named "$(MySpecialPath)". Arrgghh. If you edit the .csproj file in a text editor, you can set this correctly however, and it seems to work as long as the <Import> tag appears before the <PropertyGroup> containing the Output Path.
So I think the solution for me will be to create a standard OurTeam.targets MsBuild file in a standard location, add an installer for changing the registry so it doesn't flag warnings, and then create custom project templates that <Import> this file, and also set the Output Path to use the properties defined in the OurTeam.targets file. Sadly, this is more work and a less elegant solution than the property sheet inheritance mechanism in C++.