user2479356
user2479356

Reputation:

Wix/Heat: Access <ProjectReference> inside .wixproj file

I am running a heat command as pre-build event in my installer.wixproj. I want my dir-parameter (HarvestPath) to be the target directory of my included project reference.

Now inside my .wixproj-file I have

<PropertyGroup>
  <HarvestPath Condition=" '$(HarvestPath)' == '' ">"@(ProjectReference.TargetDir)"</HarvestPath>
  <DefineConstants>HarvestPath=$(HarvestPath)</DefineConstants>
</PropertyGroup>

<ProjectReference Include="..\..\NAME.csproj">
  <Name>NAME</Name>
  <Project>SOME_GUID</Project>
  <Private>True</Private>
  <DoNotHarvest>True</DoNotHarvest>
  <RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
  <RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>

How can I access the target directory of the project reference? "@(ProjectReference)"only gives me the full path to its .csproj file and I can't find any documentation for that.

Upvotes: 3

Views: 1583

Answers (2)

Joey Adams
Joey Adams

Reputation: 43400

I found a more robust way to do it, though it's a little complicated. I had to do it this way to support building on a TFS build server with a newer build template (GitTemplate.12.xaml) because:

  • The build does not actually write to the OutputPath specified in the project (e.g. bin\Release\AnyCPU), but writes to its own build outputs folder.
  • The build does write to the IntermediateOutputPath (e.g. obj\Release\AnyCPU), but does not copy dependencies there, which heat.exe needs.

This works by finding where project outputs actually went (_GatheredProjectReferencePaths), which wix2010.targets has conveniently obtained for us via the MSBuild task. It gathers the relevant info into an item collection (COMProjects), which the HeatCOMProjects target loops over.

You will need to replace "ACME.Core", "ACME.Data", etc. with the names of the code projects you are heating, and ensure that the WiX project references them. You will also need to make sure the HeatFile commands have the correct arguments, particularly DirectoryRefId, which I think needs to point to where the DLLs will be installed on the target system.

<!-- Find where the .dll and .tlb files of source projects were written to.
     Can't rely on relative paths like "$(ProjectDir)..\..\ACME\ACME.Core\bin\$(ConfigurationName)\AnyCPU\ACME.Core.dll"
     because some build templates don't actually write to OutputPath.  Can't rely on the intermediate output path either
     (obj\...) because Heat needs to be able to find dependencies of any DLLs given. -->
<Target Name="GetCOMProjects" DependsOnTargets="ResolveProjectReferences">
  <ItemGroup>
    <COMProjects Include="%(_GatheredProjectReferencePaths.Name)"
                 Condition="%(_GatheredProjectReferencePaths.Name) == 'ACME.Core' Or
                            %(_GatheredProjectReferencePaths.Name) == 'ACME.Data' ">
      <Name>%(_GatheredProjectReferencePaths.Name)</Name>
      <DLLPath>%(_GatheredProjectReferencePaths.FullPath)</DLLPath>
      <TLBPath>$([System.IO.Path]::GetDirectoryName(%(_GatheredProjectReferencePaths.FullPath)))\%(_GatheredProjectReferencePaths.Filename).tlb</TLBPath>
    </COMProjects>
  </ItemGroup>
</Target>

<!-- Loop through each COMProjects item -->
<Target Name="HeatCOMProjects"
        Inputs="%(COMProjects.DLLPath); %(COMProjects.TLBPath)"
        Outputs="$(ProjectDir)%(COMProjects.Name).REGASM.wxs; $(ProjectDir)%(COMProjects.Name).REGTLB.wxs">
  <HeatFile
    ToolPath="$(WixToolPath)"
    File="%(COMProjects.DLLPath)"
    OutputFile="$(ProjectDir)%(COMProjects.Name).REGASM.wxs"
    GenerateGuidsNow="true"
    NoLogo="true"
    ComponentGroupName="%(COMProjects.Name).REGASM"
    DirectoryRefId="INSTALLDIR"
    SuppressRootDirectory="true"
    PreprocessorVariable="var.%(COMProjects.Name).TargetDir"
  />
  <HeatFile
    ToolPath="$(WixToolPath)"
    File="%(COMProjects.TLBPath)"
    OutputFile="$(ProjectDir)%(COMProjects.Name).REGTLB.wxs"
    GenerateGuidsNow="true"
    NoLogo="true"
    ComponentGroupName="%(COMProjects.Name).REGTLB"
    DirectoryRefId="INSTALLDIR"
    SuppressRootDirectory="true"
    PreprocessorVariable="var.%(COMProjects.Name).TargetDir"
  />
</Target>

<!-- Run Heat after building dependencies, but before building this project. -->
<Target Name="AfterResolveReferences" DependsOnTargets="GetCOMProjects; HeatCOMProjects" />

Upvotes: 1

user2479356
user2479356

Reputation:

Ok so I found a workaround in case anyone is interested. I simply used "$(SolutionDir)\MY_PROJECT_FOLDER\MY_PROJECT_TARGET". It's not exactly what I aimed for but it works.

Upvotes: 2

Related Questions