When & How to use ILMerge with Visual Studio Project / Solution - visual-studio

I'm developing a medium sized enterprise application.
There are many projects / solutions to this.
For example:
Company.Data
Company.Data.LinqToSql
Company.Entities (business objects)
Company.BLL
I then have some applications - for example a windows service:
MyWindowsService.
When i deploy this (by creating a setup project) it installs a load of DLL's from the output of the above mentioned projects.
Is this where i should be using ILMerge? to create one assembly.... Company.dll for example?
How would i go about integrating this into my build process?

The question ILMerge Best Practices
has good info on why.
When I use ILMerge, I use it to build a single DLL, to simplify deployment.
As to How, I define a separate, custom VS project, "Converged.csproj" if you like. In that .csproj file I define a custom Compile target. It is boilerplate code, that performs an ILMerge on all the referenced assemblies for the project.
It looks like this:
<Target Name="Compile">
<!-- Outputs="$(IntermediateOutputPath)$(TargetFileName)" -->
<!-- Outputs="$(TargetPath)" -->
<Message Text="Performing the Ilmerge." />
<!-- in this CreateItem stanza, we collect all the DLLs for the referenced projects -->
<CreateItem Include="#(_ResolvedProjectReferencePaths)">
<Output TaskParameter="Include" ItemName="AssembliesToMerge" />
</CreateItem>
<!-- This weird bit of hieroglyphics is the assemblies to merge, quoted, and separated by spaces -->
<!-- Example: "c:\foo\project1\bin\Debug\ProjectOne.dll" "c:\foo\project2\bin\Debug\ProjectTwo.dll" -->
<Message Text="AssembliesToMerge= #(AssembliesToMerge -> '"%(Fullpath)"', ' ')" />
<!-- Message Text="TargetPath= $(TargetPath)" / -->
<Message Text="TargetFileName= $(TargetFileName)" />
<!-- produce the merged assembly - putting the output in the "IntermediateOutputPath" eg obj\Debug. -->
<!-- it will be copied later by the CopyFilestoOutputDirectory task defined in Microsoft.Common.Targets -->
<Error
Text="ILMerge cannot be found. You need to download and install ILMerge in order to build DotNetZip."
Condition="!Exists('$(ProgramFiles)\Microsoft\Ilmerge\Ilmerge.exe')" />
<Exec Command=""$(ProgramFiles)\Microsoft\Ilmerge\Ilmerge.exe" /t:library /xmldocs /out:"$(IntermediateOutputPath)$(TargetFileName)" #(AssembliesToMerge -> '"%(Fullpath)"', ' ') " />
<!-- for some reason the XML doc file does not get copied automatically from obj\Debug to bin\Debug. -->
<!-- we do it here explicitly. -->
<Copy SourceFiles="$(IntermediateOutputPath)$(AssemblyName).XML" DestinationFolder="$(OutDir)" SkipUnchangedFiles="true" OverwriteReadOnlyFiles="$(OverwriteReadOnlyFiles)" />
</Target>

Related

Can SonarScanner for MsBuild scan TSQL

I have SonarQube installed and we are trying to run it on a product that contains the following code types
Javascript
VBScript
XML
C#
VB.net
T/SQL
Now we have got it running to scan all the code apart from the T/SQL code.
This TSQL code sits under the same directory as all the other code but doesn't have a specific visual studio project.
The only way we have been able to run a scan on the SQL is using the standard sonarqube runner, but that causes a new product to be created on our dashboard.
Any thoughts or suggestions.
Currently, if you want the TSQL files to be analyzed and appear under the same SonarQube project as the other code you will need to reference it from an MSBuild project.
There are a couple of ways you could do this:
1) include the TSQL files in one of your existing projects using a snippet like the following:
<ItemGroup>
<!-- Include additional files that should be analyzed by the SonarScanner for MSBuild -->
<None Include="*.tsql" >
<!-- Don't show the items in the Solution Explorer -->
<Visible>False</Visible>
</None>
</ItemGroup>
2) Create a separate dummy MSBuild project whose only purpose is to specify the additional files to be analyzed. This is slightly more complicated as the dummy project needs some additional content to make it work with the SonarScanner for MSBuild targets.
The following template works with v4.3 of the scanner, and should work with recent previous versions too.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- The only purpose of this project is to specify additional files to be analyzed by the SonarScanner for MSBuild -->
<!-- 1. Set a unique GUID id for the project -->
<PropertyGroup>
<ProjectGuid>{EA2BAA27-D799-4FBE-9430-7499ACF3E431}</ProjectGuid>
</PropertyGroup>
<!-- 2. Specify the files to be analysed -->
<ItemGroup>
<SonarQubeAnalysisFiles Include="**\*.js" />
</ItemGroup>
<!-- ******************************************************** -->
<!-- Boilerplate - no need to change anything below this line -->
<!-- ******************************************************** -->
<!-- Import the SQ targets (will only exist if the scanner "begin" step has been executed) -->
<PropertyGroup>
<SQImportBeforeTargets>$(localappdata)\Microsoft\MSBuild\$(MSBuildToolsVersion)\Microsoft.Common.targets\ImportBefore\SonarQube.Integration.ImportBefore.targets</SQImportBeforeTargets>
</PropertyGroup>
<Import Condition="Exists('$(SQImportBeforeTargets)')" Project="$(SQImportBeforeTargets)" />
<!-- Re-define the standard step of targets used in builds -->
<Target Name="Build" />
<Target Name="Clean" />
<Target Name="CoreCompile" />
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
<!-- Re-define one of the standard SQ targets as we have already set the list of files to analyze above -->
<Target Name="CalculateSonarQubeFilesToAnalyze" >
<PropertyGroup>
<!-- Set a property indicating whether there are any files to analyze -->
<AnalysisFilesExist Condition=" #(SonarQubeAnalysisFiles) != '' ">true</AnalysisFilesExist>
</PropertyGroup>
</Target>
</Project>

How-To Set and Get Build Version in TFS SQL Project

I'm using TFS in Visual Studio 2013 & SSDT to create various SQL Database scripts. i.e. I'm doing all my SQL DB development via VS not SSMS.
Want I'm now trying to achieve is to generate/retreive a version number from an external text file when the project is built/published, based on the functionality posted here:
http://www.codeproject.com/Articles/468855/Working-with-MSBuild-Part-2
So I've added the following to the MyProject.sqlproj xml file:
<PropertyGroup>
<WorkingFolder>C:\Source Control\MISTP\Main\DB\SSMS\MyProject</WorkingFolder>
</PropertyGroup>
<Target Name="GetVersion">
<Message Text="GetVersion: Reading version number from VersionInfo.txt" />
<Attrib Files="$(WorkingFolder)\VersionInfo.txt" Normal="true" />
<Version VersionFile="$(WorkingFolder)\Build\VersionInfo.txt">
<Output TaskParameter="Major" PropertyName="Major" />
<Output TaskParameter="Minor" PropertyName="Minor" />
<Output TaskParameter="Build" PropertyName="Build" />
<Output TaskParameter="Revision" PropertyName="Revision" />
</Version>
<Message Text="GetVersion: $(Major).$(Minor).$(Build).$(Revision)" />
</Target>
<Target Name="SetVersion" DependsOnTargets="GetVersion">
<Message Text="SetVersionInfo: Updating Versions in all files" />
<CreateItem Include="$(WorkingFolder)\**\*.*">
<Output TaskParameter="Include" ItemName="Files"/>
</CreateItem>
<Attrib Files="#(Files)" Normal="true" />
<FileUpdate Files="#(Files)" Regex="FileVersionAttribute\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText="FileVersionAttribute("$(Major).$(Minor).$(Build).$(Revision)")" />
<FileUpdate Files="#(Files)" Regex="FileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText=" FileVersion ("$(Major).$(Minor).$(Build).$(Revision)")" />
<FileUpdate Files="#(Files)" Regex="FileVersion\("(\d+)\.(\d+)\.(\d+)\.(\d+)"\)" ReplacementText="FileVersion("$(Major).$(Minor).$(Build).$(Revision)")" />
</Target>
I have a VersionInfo.txt file located in:
C:\Source Control\MISTP\Main\DB\SSMS\MyProject\Build
Which simply contains the string: 1.2.3.4
However, this doesn't seem to actually do anything when I Build and/or Publish the project within VS. What am I missing?!
I'm new to MSBuild, but the syntax appears correct - and is largely lifted from the codeproject article - and the path to the file are ok.
It feels like the xml is not being executed, but I'm assuming that it's very presence in the .sqlproj file will result in it being executed.
Thanks
The target isn't triggered during the build. Update "SetVersion" target as following:
<Target Name="SetVersion" DependsOnTargets="GetVersion" AfterTargets="PostBuildEvent">

How to include missing ProjectExtensions in VSTO csproj with SideWaffle

Following on from "How to handle VSTO prerequisites in SideWaffle project template" I've found that the new project created from the installed VSIX crashes Visual Studio when selecting the Publish tab in project properties. Doing a diff on the project files from the original project template and the newly generated project it appears that the ProjectExtensions node doesn't make it through to the new file.
Here's the original project file snippet:
<Project>
<!-- Include the build rules for a C# project. -->
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- Include additional build rules for an Office application add-in. -->
<Import Project="$(VSToolsPath)\OfficeTools\Microsoft.VisualStudio.Tools.Office.targets" Condition="'$(VSToolsPath)' != ''" />
<!-- This section defines VSTO properties that describe the host-changeable project properties. -->
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{BAA0C2D2-18E2-41B9-852F-F413020CAA33}">
<ProjectProperties HostName="Visio" HostPackage="{29A7B9D7-A7F1-4328-8EF0-6B2D1A56B2C1}" OfficeVersion="15.0" VstxVersion="4.0" ApplicationType="Visio" Language="cs" TemplatesPath="" DebugInfoExeName="#Software\Microsoft\Office\15.0\Visio\InstallRoot\Path#visio.exe" AddItemTemplatesGuid="{51063C3A-E220-4D12-8922-BDA915ACD783}" />
<Host Name="Visio" GeneratedCodeNamespace="VisioVstoTemplate" IconIndex="0">
<HostItem Name="ThisAddIn" Code="ThisAddIn.cs" CanonicalName="AddIn" CanActivate="false" IconIndex="1" Blueprint="ThisAddIn.Designer.xml" GeneratedCode="ThisAddIn.Designer.cs" />
</Host>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
</Project>
...and here's the same snippet from the new project file:
<Project>
<!-- Include the build rules for a C# project. -->
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- Include additional build rules for an Office application add-in. -->
<Import Project="$(VSToolsPath)\OfficeTools\Microsoft.VisualStudio.Tools.Office.targets" Condition="'$(VSToolsPath)' != ''" />
<!-- This section defines VSTO properties that describe the host-changeable project properties. -->
</Project>
Is there a good way to stop this from being excluded?
When you install TemplateBuilder a .props file is added to your project under Properties. By default when the project template is built ProjectExtensions is removed. To disable that add the following property into the .props file.
<ls-enable-remove-proj-extensions>false</ls-enable-remove-proj-extensions>

NAnt/NAntContrib 'VB6' failed to start on remote build

Background
I'm putting together a Continuous Integration system at work on two VMs running on my local desktop. VM #1 (Toolbox) is running CruiseControl.Net, Subversion, BugTracker.Net and SQL Server Express. VM #2 (BuildMaster) is running NAnt with NAntContrib and has VB 6.0 and the 1.0/1.1/2.0/3.5 .Net Framework SDKs installed. The intent is to tightly control what's installed on BuildMaster and be much looser on Toolbox and developer workstations.
Issue
I had a CCNet project on Toolbox that successfully compiled a test VB 6.0 application on BuildMaster, but the build started failing last week. The only thing I remember doing was install BugTracker.Net and SQL Server Express on Toolbox.
Symptoms
The build fails and returns an exception:
<![CDATA[Starting 'vb6 ( /make "\\buildmaster\Working\TestApp\TestApp.vbp" /outdir "\\buildmaster\Working\TestApp\build" /out "\\buildmaster\Working\TestApp\TestApp.build.err")' in '\\buildmaster\Working\TestApp']]></message><duration>711.02240000000006</duration></task><duration>761.09440000000006</duration></target><failure><builderror><type>NAnt.Core.BuildException</type><message><![CDATA['vb6' failed to start.]]></message><location><filename>\\buildmaster\Working\TestApp\TestApp.build</filename><linenumber>39</linenumber><columnnumber>4</columnnumber></location><stacktrace><![CDATA[ at NAnt.Core.Tasks.ExternalProgramBase.StartProcess() in c:\Nant\src\NAnt.Core\Tasks\ExternalProgramBase.cs:line 501
at NAnt.Core.Tasks.ExternalProgramBase.ExecuteTask() in c:\Nant\src\NAnt.Core\Tasks\ExternalProgramBase.cs:line 386
at NAnt.Contrib.Tasks.Vb6Task.ExecuteTask() in c:\Nant\contrib\src\Tasks\Vb6Task.cs:line 220
at NAnt.Core.Task.Execute() in c:\Nant\src\NAnt.Core\Task.cs:line 186
at NAnt.Core.Target.Execute() in c:\Nant\src\NAnt.Core\Target.cs:line 247
at NAnt.Core.Project.Execute(String targetName, Boolean forceDependencies) in c:\Nant\src\NAnt.Core\Project.cs:line 910
at NAnt.Core.Project.Execute() in c:\Nant\src\NAnt.Core\Project.cs:line 862
at NAnt.Core.Project.Run() in c:\Nant\src\NAnt.Core\Project.cs:line 947]]></stacktrace><internalerror><type>System.ComponentModel.Win32Exception</type><message><![CDATA[The system cannot find the file specified]]></message><stacktrace><![CDATA[ at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
at System.Diagnostics.Process.Start()
at NAnt.Core.Tasks.ExternalProgramBase.StartProcess() in c:\Nant\src\NAnt.Core\Tasks\ExternalProgramBase.cs:line 498]]></stacktrace></internalerror></builderror></failure><duration>1211.7424</duration></buildresults>
Obviously, the meat of the exception is [CDATA['vb6' failed to start.]]. My problem is that when I run the Nant build directly on BuildMaster it completes the build successfully every time.
For the sake of completeness, here's my NAnt build script:
<?xml version="1.0" ?>
<project name="TestApp" default="build">
<!-- set build.date property to current date in format yyyy-MM-dd -->
<tstamp property="build.date" pattern="yyyy-MM-dd" />
<!-- global project settings -->
<property name="project.name" value="TestApp" />
<property name="project.version" value="1.00" unless="${property::exists('project.version')}" />
<property name="project.release.type" value="release" unless="${property::exists('project.release.type')}" /> <!-- nightly / dev / alpha / beta# / rc# / release -->
<property name="build.warnaserror" value="false" />
<!-- default configuration -->
<property name="project.client" value="" />
<property name="build.defines" value="" />
<property name="build.number" value="${math::abs(math::floor(timespan::get-total-days(datetime::now() - datetime::parse('01/01/2000'))))}" />
<!-- platform specific properties. These are the defaults -->
<property name="current.build.defines" value="${build.defines}" />
<!-- Build Tasks -->
<target name="init" description="Initializes build properties">
<property name="build.dir" value="${project::get-base-directory()}\build" />
<echo message="Build Directory is ${build.dir}" />
</target>
<target name="clean" depends="init" description="Deletes current build configuration">
<echo message="Clearing out files before recompiling..." />
<delete verbose="true">
<fileset basedir="${build.dir}">
<include name="TestApp*.exe" />
</fileset>
</delete>
</target>
<target name="build" depends="clean" description="Perform a build of the base TestApp product">
<mkdir dir="${build.dir}" unless="${directory::exists(build.dir)}" />
<!-- Actually compile VB6 project into executable -->
<vb6 project="TestApp.vbp" outdir="${build.dir}" errorfile="TestApp.build.err" verbose="true" />
</target>
</project>
Your help is greatly appreciated!
I might be misinterpreting your question so please bear with me. CCNet's nant task operatates on the local machine (the machine running CCNet).
If ToolBox is running CCNet but BuildMaster is running all tools (i.e. VB6, etc), I'm fairly sure there no way to do what's being attempted. Generally, CCNet needs to be running on the machine actually performing the builds. Therefore, the fact that VB6 cannot be found is because VB6 is not installed on ToolBox.
However, CCNet does have a way to monitor/control multiple build servers from one. So in your case you could configure ToolBox to control BuildMaster's builds, but CCNet would need to be installed on both. For a reference on something like this you can check out Splitting the build on CCNet's site.

How do I create a ZIP file of my Cruise Control builds?

I use CruiseControl.NET to automatically build my .NET 3.5 web applications, which works a treat. However, is there any way to automatically create a ZIP file of these builds, and put the ZIP's into a separate directory?
I have seen this is possible using NAnt but cannot find an example of how to get this working.
Can anyone offer help/examples?
I've just added such a Nant task to our CC machine.
See http://nant.sourceforge.net/release/latest/help/tasks/zip.html
Note when initially viewing the zip archive, it may appear as if all the files are at the same level, i.e no folders, but actually they folders are preserved.
Notice how you can exclude file types or folders.
You could take the approach of only including the file types you want and excluding the rest.
First define properties for where the source files are allcode.dir and the name and location of the zip file sourcebackup.zip
Now here is the nant task
<zip zipfile="${sourcebackup.zip}" includeemptydirs="true" verbose="true">
<fileset basedir="${allcode.dir}">
<include name="**/*" />
<exclude name="**/_resharper*/**" />
<exclude name="**/build/**" />
<exclude name="**/obj/**" />
<exclude name="**/bin/**" />
<exclude name="**/*.dll" />
<exclude name="**/*.scc" />
<exclude name="**/*.log" />
<exclude name="**/*.vssscc" />
<exclude name="**/*.suo" />
<exclude name="**/*.user" />
<exclude name="**/*.pdb" />
<exclude name="**/*.cache" />
<exclude name="**/*.vspscc" />
<exclude name="**/*.msi" />
<exclude name="**/*.irs" />
<exclude name="**/*.exe" />
</fileset>
<echo message="########## Zipped##########" />
Call this from your cc build like any other nant task.
We find it best if each CC project calls a single task if possible, then you only have to change the nant script, and you can run the nant script on your local machine.
Eg in the project block, we have the single target "build", which as part of its work calls ZipSource
<targetList>
<target>Build</target>
</targetList>
We use the above for a BizTalk project.
Enjoy.
If you're using Nant, then doesn't the Zip task work for you?
We are zipping the sources of a CruiseControl.NET project
but we are using ant
<target name="zipProject">
<mkdir dir="output"/>
<zip destfile="output\sources.zip" basedir="C:\project\src" />
</target>
i don't know about nant but i would expect it to be similar
#David: The NAnt Zip task is what I'm after, yes, but I'm asking how to integrate it as part of an automatic CruiseControl.NET build. If you take a look at the NAnt documentation for the cruise control config it doesn't make it clear if I can run an NAnt task from inside the <tasks> XML node in my CruiseControl config - it only says that it can be part of a <schedule>.
I have found a few examples of setting up your CruiseControl config and a few examples of NAnt tasks but nothing that integrates the two: specifically, zipping up a CruiseControl build.
If anyone has some sample XML of their CruiseControl config, hooking up to an NAnt zip task, post samples here.
Cheers.

Resources