Issue with ApplicationFile file not found in MSBuild script - msbuild

my issue is that the msbuild script can't find my MSI installer file.
Here is the script file bootstrapper.msbuild :
<Project ToolsVersion="3.5"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<BootstrapperFile Include="Microsoft.Windows.Installer.4.5" >
<ProductName>Windows Installer 4.5</ProductName>
</BootstrapperFile>
<!--<BootstrapperFile Include="DotNetFX40" >
<ProductName>Microsoft DotNet Framework 4.5 SP1</ProductName>
</BootstrapperFile>-->
</ItemGroup>
<!-- from http://stackoverflow.com/questions/346175/use-32bit-program-files-directory-in-msbuild
-->
<PropertyGroup>
<ProgramFiles32>$(MSBuildProgramFiles32)</ProgramFiles32>
<ProgramFiles32 Condition=" '' == '$(ProgramFiles32)'">$(ProgramFiles%28x86%29)</ProgramFiles32>
<ProgramFiles32 Condition=" '' == '$(ProgramFiles32)'">$(ProgramFiles)</ProgramFiles32>
</PropertyGroup>
<Target Name="SetupExe">
<GenerateBootstrapper
ApplicationFile="..\MySetup\MySetup\bin\Debug\MySetup.msi"
ApplicationName="MyApplication"
Culture="en"
BootstrapperItems="#(BootstrapperFile)"
ComponentsLocation="HomeSite"
Path="$(ProgramFiles32)\Microsoft SDKs\Windows\v7.0A\Bootstrapper\"
OutputPath="output"/>
</Target>
</Project>
The folders structure is as follows :
The file boostrapper.msbuild is inside Mybootstrapper. I've tried the path using command line and it works fine. Why not in ms build ? Am I missing something ?
The question is what is the directory the task GenerateBootstrapper is referring to ?
Could you advice please ?

It looks like your relative path is not correct. Try:
ApplicationFile="..\MySetup\bin\Debug\MySetup.msi"

The issue was I didn't put MSI file in the path where the generated setup.exe is located. I was thinking that this later will contain all files...
Am I wrong ?

Related

How to find if file exist with msbuild command(or before)

I need to find if certain file exist prior to the run of a power-shell script
The file (if exist) will be in a specific folder.
Can i check for it's existence through the proj file or something like that?
Note the second MyCheck looks at the (conditional) value of (the first) MyCheck
<PropertyGroup>
<MyCheck Condition="Exists($(MyFileOrFolderName))">true</MyCheck>
<MyCheck Condition="'$(MyCheck)'==''">false</MyCheck>
</PropertyGroup>
<Message Text="My-File-Or-Folder-Name already exists? : $(MyCheck)" />
OR
<PropertyGroup>
<MyCheck>false</MyCheck>
<MyCheck Condition="Exists($(MyFileOrFolderName))">true</MyCheck>
</PropertyGroup>
<Message Text="MyFileOrFolderNameexists? : $(MyCheck)" />
In order to execute PS script, depending on the existance of a file, you can create a Target element in your *.*proj file with condition depending on the file existance:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="14.0">
<PropertyGroup>
<ScriptLocation>.\Do-Something.ps1</ScriptLocation>
<PowerShellExe Condition=" '$(PowerShellExe)'=='' ">%WINDIR%\System32\WindowsPowerShell\v1.0\powershell.exe
</PowerShellExe>
</PropertyGroup>
<Target Name="RunPSScript" Condition="Exists($(ScriptLocation))">
<Exec Command="$(PowerShellExe) -NonInteractive -executionpolicy Unrestricted -command "$(ScriptLocation)""/>
</Target>
</Project>
See more details here about executing PS scripts from msbuild projects. You can use AfterTargets, BeforeTargers, or any other methods to control order of the execution this target.

VSTS Continuous Integration: NuGet package are missing on this computer

I am using Visual Studio 2015, i have a solution with 2 different web applications. There exists One Solution and two projects under it. The files related to nuget are distributed as:
Packages Folder is in the Solution Directory
Package.config in each project directory
There is no nuget folder: nuget.config and nuget.exe in my solution.
My Project build and run fine in Visual Studio, but I am facing a problem when using VSTS Continuous Integration with Build Solution Step (which use a build definition that maps to one project) it gives:
This project references NuGet package(s) that are missing on this computer. Use
NuGet Package Restore to download them. For more information, see The missing file is
..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props.
This is MyProject.csproj file:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props" Condition="Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" />
<Import Project="..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props" Condition="Exists('..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{BBD26A49-D1F4-4391-9D60-2469433DEE0D}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>AdminUI</RootNamespace>
<AssemblyName>AdminUI</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<MvcBuildViews>false</MvcBuildViews>
<UseIISExpress>true</UseIISExpress>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<UseGlobalApplicationHostFile />
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
I noticed that it only tries to import the package that generated the error, but it doesn't explicitly import other packages, but it include them as references:
<Reference Include="WebGrease">
<Private>True</Private>
<HintPath>..\packages\WebGrease.1.5.2\lib\WebGrease.dll</HintPath>
</Reference>
<Reference Include="Antlr3.Runtime">
<Private>True</Private>
<HintPath>..\packages\Antlr.3.4.1.9004\lib\Antlr3.Runtime.dll</HintPath>
</Reference>
This also the end of the myProject.csproj file:
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Net.Compilers.1.0.0\build\Microsoft.Net.Compilers.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\build\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.props'))" />
</Target>
I am wondering why it gives this error, taking into consideration that this package is already installed in the previous step "Nugget Install". Why it can't find it? i have spent a lot of time on this issue!
Before you add the build task, you can add nuget install or restore task. This will restore/install the nuget packages on the build agent.
Actually, this worked for me:
Adding the mapping (repository configuration) to the packages folder in the solution
Repository Configuration
Nuget Install Config
Then, it worked like a charm.
Ensure the Package.config file you've mentioned exists in source control.
It may be that everything builds successfully on your machine because Package.config exists there but not in source control for when the continuous integration agent tries to download and build the project.

Include .sln file in MsBuild file?

I'm trying to build Visual Studio project from .msbuild file. The idea is to do some things before the build, then execute build of .sln from the same place.
Something like this:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="BuildClient" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<Tools>$(MSBuildProjectDirectory)\tools</Tools>
<NugetExe>$(Tools)\nuget\nuget.exe</NugetExe>
<ClientFolder>$(MSBuildProjectDirectory)\src\Client</ClientFolder>
<ClientSolution>$(ClientFolder)\ClientProject.sln</ClientSolution>
</PropertyGroup>
<Target Name="BuildClient">
<!--Restore Nuget-->
<Message Text="Starting Restoring Nuget Packages" />
<Exec Command=" $(NugetExe) restore $(ClientSolution)" />
<Message Text="Finished Restoring Nuget Packages" />
<Import Project="$(ClientSolution)" />
<!--build project-->
</Target>
</Project>
But line with <Import> is underlined in VS, saying import should be on level up, not within <Target>. When I move <Import> a level up as suggested I get an execution error:
errorMSB4025: The project file could not be loaded. Data at the root level is invalid.
Any idea how to build whole project without major rewriting?
Silly me! the solution turns out to be very simple:
<MSBuild Projects="..\..\MySolution.sln" "/>
This did the trick with executing the build on .sln file
I got the pointer from this answer.

MSBuild clean directory string

I have a property in MSBuild to represent the directory above the MSBuildProjectDirectory:
<PropertyGroup>
<BuildDir>$(MSBuildProjectDirectory)\..</PRSBuildDir>
</PropertyGroup>
I need to then use this property, but I need the directory string cleaned so that it doesn't include the ... In other words I need the .. evaluated, so that if the current project file is in C:\Test\Tom\MyDir, then I need a property containing the string C:\Test\Tom.
The reason I'm asking is because I'm trying to run a command like this:
msiexec /passive /i "D:\Build\2.3.84.40394\Deployment\..\Vendor\LogParser.msi"
But it's complaining about the path to the msi: This installation package could not be opened. Verify that the package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer package.
There's a ConvertToAbsolutePath task, that any use?
The best method I've got right now is below, but I was wondering if there might be a better way..
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildDir>$(MSBuildProjectDirectory)\..</BuildDir>
</PropertyGroup>
<Target Name="Test">
<ItemGroup>
<CleanBuildDir Include="$(BuildDir)" />
</ItemGroup>
<PropertyGroup>
<BuildDir>%(CleanBuildDir.FullPath)</BuildDir>
</PropertyGroup>
<Message Text="$(BuildDir)" />
</Target>
</Project>
If you want to have wildcard evaluated, you should use Item instead of Property.
<ItemGroup>
<BuildDir Include="$(MSBuildProjectDirectory)\.."/>
</ItemGroup>
<Target Name="ExecMSIExec">
<Exec Command="msiexec /passive /i %(BuildDir.FullPath)\Vendor\LogParser.msi"/>
</Target>
(deleted my answer as I didn't see that Tom had answered in exactly the same way!)
By the way, why don't you set the "WorkingDirectory" attribute of the Exec task where you actually call msiexec to be the location of your MSI - that way you won't run into an path length issues

How to get the Windows SDK folder in MSBuild?

What would be the way to retrieve the Windows SDK folder in an MSBuild task?
Using the generateBootstrapper task I'm creating a bootstrapper for my setup to be able to install the pre-requisites. This task needs the path to the folder where the pre-requisite packages are located, i.e. the Windows SDK folder
"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\"
when using Visual Studio 2008. So far I have been using a hard-coded path but this won't work on any system. Is there a better way to get the path?
This is my build script:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"
ToolsVersion="3.5">
<ItemGroup>
<BootstrapperFile Include="Microsoft.Net.Framework.2.0">
<ProductName>.NET Framework 2.0</ProductName>
</BootstrapperFile>
<BootstrapperFile Include="Microsoft.Windows.Installer.3.1">
<ProductName>Windows Installer 3.1</ProductName>
</BootstrapperFile>
</ItemGroup>
<Target Name="Bootstrapper">
<GenerateBootstrapper ApplicationFile="mySetup.msi"
Culture="de-DE"
ApplicationName="My Application"
OutputPath="$(OutDir)\de-DE"
BootstrapperItems="#(BootstrapperFile)"
Path="C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\" />
<GenerateBootstrapper ApplicationFile="mySetup.msi"
Culture="en-US"
ApplicationName="My Application"
OutputPath="$(OutDir)\en-US"
BootstrapperItems="#(BootstrapperFile)"
Path="C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bootstrapper\Packages\" />
</Target>
</Project>
You can also use the GetFrameworkSdkPath MSBuild task.
<GetFrameworkSdkPath>
<Output TaskParameter="Path" PropertyName="WindowsSdkPath" />
</GetFrameworkSdkPath>
For example:
<GenerateBootstrapper
ApplicationFile="$(SolutionName).application"
ApplicationName="$(ClickOnceAppTitle)"
ApplicationUrl="$(ClickOnceUrl)"
BootstrapperItems="#(BootstrapperFile)"
Culture="en"
FallbackCulture="en-US"
Path="$(WindowsSDKPath)"
OutputPath="." />
thanks John. According to your post I edited the MSBuild script to read the folder from the registry. It was however not necessary to append "Packages" on the end, that was another mistake in my original script.
The following is the working script:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WindowsSDKPath>$(registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\GenericBootstrapper\3.5#Path)</WindowsSDKPath>
</PropertyGroup>
<ItemGroup>
<BootstrapperFile Include="Microsoft.Net.Framework.2.0">
<ProductName>.NET Framework 2.0</ProductName>
</BootstrapperFile>
<BootstrapperFile Include="Microsoft.Windows.Installer.3.1">
<ProductName>Windows Installer 3.1</ProductName>
</BootstrapperFile>
</ItemGroup>
<Target Name="Bootstrapper">
<GenerateBootstrapper ApplicationFile="mySetup.msi"
Culture="de-DE"
ApplicationName="My Application"
OutputPath="$(OutDir)\de-DE"
BootstrapperItems="#(BootstrapperFile)"
Path="$(WindowsSDKPath)" />
<GenerateBootstrapper ApplicationFile="mySetup.msi"
Culture="en-US"
ApplicationName="My Application"
OutputPath="$(OutDir)\en-US"
BootstrapperItems="#(BootstrapperFile)"
Path="$(WindowsSDKPath)" />
</Target>
</Project>
The install path of the Windows SDK is stored in the CurrentInstallFolder value of the following registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\Windows
I followed the answer from Jeremy D, but that gave the error message:
error MSB3147: Could not find required file 'setup.bin' in 'C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\Engine'.
The reason is that the path to the bootstrapper (at least with V8.0A of the SDK) is a subdirectory under the path returned by the GetFrameworkSdKPath.
So the MSBuild code that works for me is:
<Target Name="AfterBuild">
<GetFrameworkSdkPath>
<Output TaskParameter="Path" PropertyName="WindowsSdkPath"/>
</GetFrameworkSdkPath>
<GenerateBootstrapper
ApplicationFile="myapp.msi"
ApplicationName="MyApplication"
BootstrapperItems="#(BootstrapperFile)"
OutputPath="$(OutputPath)"
Path="$(WindowsSdkPath)\Bootstrapper" />
</Target>
Note the \Bootstrapper suffix to $(WindowsSdkPath)
The path to the bootstrapper is stored under the registry key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\GenericBootstrapper\3.5
To find out the packages folder, open this, read the "Path" registry value, and append "Packages" on the end and that should give you the full path to the folder you want.
For example:
string bootStrapperPackagesFolder = "";
RegistryKey regKey = Registry.LocalMachine.OpenSubKey
(#"SOFTWARE\Microsoft\GenericBootstrapper\3.5");
if (regKey != null)
{
bootStrapperPackagesFolder = (string)regKey.GetValue("Path");
if (bootStrapperPackagesFolder != null)
{
bootStrapperPackagesFolder += #"Packages\";
Console.WriteLine(bootStrapperPackagesFolder);
}
}