Accessing InstallShield Task in MSBuild - msbuild

I'm trying to use MSBuild to create a target that will create an installer with InstallShield 2012. I'm having difficultly understanding how to access InstallShield. I ran across this mentioning an InstallShield task but I'm not sure how to get access to it. I think I need a UsingTask directive, but not sure what to import. Can someone give me a pointer on how to get this going? Thanks.

You need to import the targets file. Took me a while to figure that out as well since it's in the msbuild extensions directory together with a dll. Here's a basic sample of how to use it (note this is for 2012Spring but you get the idea):
<Import Project="$(MSBuildExtensionsPath32)\InstallShield\2012Spring\InstallShield.targets" />
<Target Name="BuildInstaller">
<InstallShield.Tasks.InstallShield
Project="/path/to/my.ism"
ProductConfiguration="Package"
ReleaseConfiguration="MSI" />
</Target>
Btw if this doesn't work out for some reason, you can always invoke ISCmdBld.exe in an Exec task, it will do the job just as fine.

InstallShield provides Visual Studio integration. When you create an InstallShield project in VS it creates a project file (.ISPROJ) which imports the InstallShield.targets file for that version of InstallShield. The project file contains plenty of examples on how to build a particular configuration and pass in such things as merge module path, properties, path variable overrides and so on.
Please note that building InstallShield requires the x86 MSBuild platform due to COM components.

Related

MSBuild task does not build solution file

I have a project file that should build another solution. I tried using the MSBuild task for this, like this:
<MSBuild Projects="MySolution.sln" Properties="Configuration=$(Configuration)"/>
I tried a lot of variants, like supplying hardcoded configuration, target Rebuild and so on. Building reports an error when I pass a wrong solution name, non-existing configuration or target and so on, so it definitely loads the solution and the project files. It exits relatively fast though and produces no output. According to documentation and examples, this should work though. I also tried passing an ItemGroup for the project, including project-specific properties as suggested by examples or in MSBuild - How to build a .NET solution file (in an XML task script) from pre-written command line commands , but that does not work either. It runs without error but no output.
When passing a list of project files instead (or a single project file), it builds correctly, but the problem is that dependencies between the projects are not properly resolved. At the end, I have to supply all project files in the solution and add them to the list, which is what I want to avoid.
So, why does solution building not work, even though it should? What is wrong here?
Is this a .net project? If so you probably need to pass in a platform as well as a configuration.
<MSBuild
Projects="MySolution.sln"
Targets="build"
Properties="Configuration=$(Configuration);Platform=$(Platform)" />
If the project just contains web sites and libraries then the platform should be Any CPU if your solution just contains executables then x86 or x64 or if it's a mixture of different types of platform then you can use mixed platforms
To check what are available open the solution in Visual Studio, right click on the solution in solution explorer and select "Configuration Manager" you'll then have a drop down for "Active Solution Platforms"

WiX project that references an SSDT project fails to build in TFSBuild - Undefined preprocessor variable '$(var.DatabaseProject.TargetDir)'

My project which is part of a larger solution was converted from a dbproj to asqlproj (SSDT). The solution includes a WiX installer which references the SSDT project. The WiX project builds fine on multiple developer systems through VS2010. The automated builds that we've always used, however, are failing with this error:
error CNDL0150: Undefined preprocessor variable '$(var.DatabaseProject.TargetDir)'.
The solution has been built by TFS automated builds for many months without issue prior to this project being converted. I can see from the build log that the SSDT project is being built, here are some relevant lines from the build log:
Project "C:\B\1\SourcePath\Server\Server.wixproj" (8) is building "C:\B\1\SourcePath\Database\DatabaseProject.sqlproj" (12) on node 1 (default targets).
...
Done Building Project "C:\B\1\SourcePath\Database\DatabaseProject.sqlproj" (default targets).
I can also see that the .dacpac and .dll files for the database project are created and copied into the output directory that TFS build has redirected the projects to.
The reference to the database project seems fine and TFS build seems to know that it should be built, but WiX is complaining about the undefined preprocessor variable for TargetDir.
I must be missing something...Is the output redirect employed by TFS causing my issue? I'm not sure where to go from here and appreciate any help you can suggest.
MORE INFO
Looking at the log file for the build in more detail I can see that the parameters for the .sqlproj project aren't being passed on the command line to candle.exe. When I build locally in VS2010, I can see the expected parameters passed as follows (just like the other projects referenced by the WiX project):
-d"DatabaseProject.FullConfiguration=Release|AnyCPU"
-dDatabaseProject.Platform=AnyCPU
-dDatabaseProject.ProjectDir=C:\SourcePath\Database\
-dDatabaseProject.ProjectExt=.sqlproj
-dDatabaseProject.ProjectFileName=DatabaseProject.sqlproj
-dDatabaseProject.ProjectName=DatabaseProject
-dDatabaseProject.ProjectPath=C:\SourcePath\Database\DatabaseProject.sqlproj
-dDatabaseProject.TargetDir=C:\SourcePath\Database\sql\release\
-dDatabaseProject.TargetExt=.dll
-dDatabaseProject.TargetFileName=DatabaseProject.dll
-dDatabaseProject.TargetName=DatabaseProject
-dDatabaseProject.TargetPath=C:\SourcePath\Database\sql\release\DatabaseProject.dll
None of these parameters are being passed to candle.exe during the TFS build. I figured this information might be useful for helping answer the question.
Any help is greatly appreciated!
Based on #RobMenching's answer, you won't be able to rely on the SSDT project variable to be set by any command-line build, so you'll need to find another way to reference the project's output.
Fortunately, in a standard TFS build, the output folders will be the same for all projects in a given build configuration. Assuming you haven't redirected the output somehow, you should be able to use any of the other projects (which are working properly) as the reference path. E.g., assuming that you have a DatabaseProject.sqlproj and an DataAccessProject.csproj in the solution, you should be able to do:
<Component Id='MyComponent' Guid='12345678-1234-1234-1234-123456789012'>
<?ifdef DatabaseProject.TargetDir ?>
<File Id='foo' Name='foo' src='$(var.DatabaseProject.TargetDir)\foo.dacpac' />
<?else?>
<File Id='foo' Name='foo' src='$(var.DataAccessProject.TargetDir)\foo.dacpac' />
<?endif?>
</Component>
For builds done inside VS2010, where each project is built to its own output folder, the correct preprocessor variable ought to be defined and it will use the first option. For TFS builds, where the preprocessor variable is not defined, TFS will have redirected the SSDT's output to the same folder as all of the other projects, so any one of the preprocessor variables should work just as well.
I believe the root issue here is that the .sqlproj does not support the necessary Visual Studio integration to provide the data for the preprocessor variable. This has created a lot of problems for the WiX toolset over the years. Unfortunately, fixing it requires the owners of the .sqlproj to fix their project system.

TFS Build server and COM references - does this work?

On my Developer PC i have registered the according dll´s and referenced them. (Add reference -> COM)
On the TFS build server this of course causes errors since the references are not registered there and wont be in the future.
What is the best way to circumvent this?
Using tlbimp.exe directly is not necessary. Try replacing any <COMReference> items in the project file with <COMFileReference>. An example would look like this:
<ItemGroup>
<COMFileReference Include="MyComLibrary.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMFileReference>
</ItemGroup>
The COM dll doesn't need to be registered on the machine for this to work.
Each COMFileReference item can also have a WrapperTool attribute but the default seems to work fine. The EmbedInteropTypes attribute is not documented as being applicable to COMFileReference, but it seems to work as intended.
See https://learn.microsoft.com/en-ca/visualstudio/msbuild/common-msbuild-project-items#comfilereference for a little more detail. This MSBuild item has been available since .NET 3.5.
OK, the answer was more easy than one might think.
Just use "tlbimp.exe" to create an according intrerop.dll which you include in your project and reference to this dll instead to the COM objects.
You may save the time using tlbimp.exe because Visual Studio creates this dll automatically when you reference a COM object.
But it does not upload it to TFS. So finally I just used the automatically created dll and the TFS build Server was happy.
One may find the automatically created Interop dll in: "ProjectFolder\obj\x86\Development"
For anyone who's having trouble with namespace:
tl;dr: use tlbImp /namespace: YOUR_NAMESPACE to give your dll a namespace
This solution worked for Azure Pipeline too. But at first I was having trouble with COMFileReference. When I replaced COMReference with COMFileReference my VS build was failing saying "Namespace can't be found". Finally found the solution here.
I used tlbImp to generate a dll of the COM file using /namespace attribute following this article. Only then you can use the dll in COMFileReference. Here's a bit elaborate example:
<ItemGroup>
<COMFileReference Include="My/Folder/MyComLibrary.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMFileReference>
</ItemGroup>
or
<ItemGroup>
<COMFileReference Include="..\My\parent\folder\MyComLibrary.dll">
<EmbedInteropTypes>True</EmbedInteropTypes>
</COMFileReference>
</ItemGroup>

What name do you give the MSBuild project build file?

I am trying to learn how to use MSBuild so we can use it to build our project. There's what seems to be a very big hole in the documentation, and I find the hole everywhere I look, the hole being how do you name or otherwise designate the MSBuild project file?
For example, the tutorial on MSBuild that can be downloaded from Microsoft goes into some detail on the contents of the build file. For example, here's a little bit of their Hello World project file.
<Project MSBuildVersion = "1.0" DefaultTargets = "Compile">
<Property appname = "HelloWorldCS"/>
<Item Type = "CSFile" Include = "consolehwcs1.cs"/>
<Target Name = "Compile">
<Task Name = "CSC" Sources = "#(CSFile)">
<OutputItem TaskParameter = "OutputAssembly" Type = "EXEFile" Include = "$(appname).exe"/>
</Task>
<Message Text="The output file is #(EXEFile)"/>
</Target>
</Project>
And it goes on blah, blah, blah Items blah blah blah tasks, here's how you do this and here's how you do that. Useless, completely useless. Because they never get around to saying how this xml file is supposed to be recognized by the MSBuild app. Is it supposed to be named in a particular way? Is it supposed to be placed in a particular directory? Both? Neither?
It isn't just the MS tutorial where they don't tell about it. I haven't been able to find it on MSDN, or on any link I can wring out of Groups.Google, either.
Does someone here know? I sure hope so.
Edited to add: I mistook the
.proj file included in the tutorial
to be the .csproj file and that is what
one fed to MSBuild, but it took the answer below before I saw this.
It should have been rather obvious, but I missed it.
You can name the file as you see fit. From the help for MSBuild
msbuild.exe /?
Microsoft (R) Build Engine Version 2.0.50727.3053
[Microsoft .NET Framework, Version 2.0.50727.3053]
Copyright (C) Microsoft Corporation 2005. All rights reserved.
Syntax: MSBuild.exe [options] [project file]
So if you save the file as mybuildfile.xml you would use the syntax:
msbuild.exe mybuildfile.xml
You don't have to specify the build file if you respect following strategy:
Today, when you invoke msbuild.exe
from the command line and don't
specify any project files as
arguments, then we do some auto
inferral and scanning and decide if we
should build anything. If we find
either a msbuild project (anything
that has an extension of *proj) or a
solution file (.sln), we will build
either the project or the solution as
long as there is only one solution or
one project in the directory. If there
is a solution and a project, we will
give preference to the solution. If
there's more than one project or more
than one solution, we issue an error
message because we can't decide which
one to build.
This is taken from New Feature Feedback Request: /IgnoreProjectExtensions - A new command-line switch.
I always name my manually written scripts build.proj.
Not a direct answer, but related; if you use .msproj as your extension, Visual Studio gives you intellisense.
Or for the truly lazy, like me.
msbuild.exe project-file-name.vcproj /t:Rebuild /p:Configuration=Release
Visual Studio 2012 recognizes the .msbuildproj as an extension and will treat it as a "project" in the Solution Explorer.

MSBuild doesn't pick up references of the referenced project

I bumped into a strange situation with MSBuild just now. There's a solution which has three projects: LibX, LibY and Exe. Exe references LibX. LibX in its turn references LibY, has some content files, and also references to a third-party library (several pre-built assemblies installed in both GAC and local lib folder). The third-party library is marked as "Copy Local" ("private") and appears in the output of the LibX project, as the LibY's output and LibX's content files do. Now, Exe project's output has LibX project output, content files of the LibX project, LibY project output (coming from LibX), but NO third-party library's assemblies.
Now I worked this around by referencing the third-party library directly in Exe project, but I don't feel this is a "right" solution.
Anyone had this problem before?
There is a difference in behavior when building with MSBuild (i.e. command line, TFS Build and other tools) compared to building with Visual Studio. The secondary references are not included in the references variable sent into MSBuild compile tasks.
There are several extension points provided by MSBuild to change how references are to be resolved. I have successfully used AfterResolveReference to fix this issue for some of my projects - I have posted more info about the background on my blog.
The workaround is to add the following code into you vbproj or csproj files
<Target Name="AfterResolveReferences">
<!-- Redefine referencepath to add dependencyies-->
<ItemGroup>
<ReferencePath Include="#(ReferenceDependencyPaths)">
</ReferencePath>
</ItemGroup>
</Target>
Microsoft has stated that this is a won't fix on Connect
You can actually go into the Microsoft.CSharp.targets or Microsoft.VisualBasic.targets file (located in the framework directory, usually C:\Windows\Microsoft.NET\Framework\v3.5) and modify the csc or vbc task parameters to include additional reference dependencies. In the file (VB targets, line 166; C# targets, line 164) change:\
References="#(ReferencePath)"
to
References="#(ReferencePath);#(ReferenceDependencyPaths)"
This might cause other issues depending on how complicated things are and it may play tricks with the Visual Studio inproc compiler, but it's the only way to do it in MSBuild that I've found.
josant's answer almost worked for me; I kept getting an error in Visual Studio when I tried that:
A problem occurred while trying to set the "References" parameter for the IDE's in-process compiler. Error HRESULT E_FAIL has been returned from a call to a COM component
The solution to my problem was to put a condition on the ItemGroup, like this:
<Target Name="AfterResolveReferences">
<!-- Redefine referencepath to add dependencies-->
<ItemGroup Condition=" '$(BuildingInsideVisualStudio)' != 'true' ">
<ReferencePath Include="#(ReferenceDependencyPaths)"></ReferencePath>
</ItemGroup>
</Target>
That caused Visual Studio to ignore the reference change completely, and the build works fine locally and on the build server.
Yes, I've had that problem, too. Though I'd love to say otherwise, I believe you must include all transitive dependencies as references in your build file.
I've combined Alex Yakunin's solution with one that will also copy native dll's.
The AfterResolveReferences method fails if you've got a directed graph not a tree with a "trying to deploy different copies of the dll" error. (cf. How to configure msbuild/MSVC to deploy dependent files of dependent assemblies)