How to include PDBs in your VSIX extension

In order to get better stack traces and logs from your Visual Studio extensions, it may be a good idea to include the PDBs inside the VSIX. The VSSDK doesn’t do this by default, but it’s trivial to do it in a generic fashion for all your extensions.

There are two simple MSBuild properties you can set in your VSIX project to include the symbols produced by it:

<PropertyGroup>
    <IncludeDebugSymbolsInVSIXContainer>true</IncludeDebugSymbolsInVSIXContainer>
    <IncludeDebugSymbolsInLocalVSIXDeployment>true</IncludeDebugSymbolsInLocalVSIXDeployment>
</PropertyGroup>

These only include the PDBs generated by the VSIX project itself, so it’s marginally useful depending on your solution.

There are two pieces of ProjectReference metadata that control what’s included in the VSIX for them:

<ProjectReference Include="...">
    <IncludeOutputGroupsInVSIX>BuiltProjectOutputGroup;BuiltProjectOutputGroupDependencies;GetCopyToOutputDirectoryItems;SatelliteDllsProjectOutputGroup</IncludeOutputGroupsInVSIX>
    <IncludeOutputGroupsInVSIXLocalOnly>DebugSymbolsProjectOutputGroup;</IncludeOutputGroupsInVSIXLocalOnly>
</ProjectReference>

Those are the implicit default values if you don’t specify anything. As you can see, the aptly named DebugSymbolsProjectOutputGroup (and there’s also a DebugSymbolsProjectOutputGroupDependencies one) aren’t set except for the IncludeOutputGroupsInVSIXLocalOnly (that is, for a local VSIX deployment). This
project reference metadata can also be specified via the Properties window:

Project reference properties

The description of those properties isn’t very helpful though :(

In order to include the debug symbols for all project references by default, you can resort to MSBuild’s ItemDefinitionGroup construct, which can set the default value for all ProjectReferences you have if they don’t provide a value:

<PropertyGroup>
    <DefaultIncludeOutputGroupsInVSIX>
        BuiltProjectOutputGroup;
        DebugSymbolsProjectOutputGroup;
        GetCopyToOutputDirectoryItems;
        SatelliteDllsProjectOutputGroup;
        BuiltProjectOutputGroupDependencies;
        DebugSymbolsProjectOutputGroupDependencies;
        SatelliteDllsProjectOutputGroupDependencies
    </DefaultIncludeOutputGroupsInVSIX>
</PropertyGroup>

<ItemDefinitionGroup>
    <ProjectReference>
        <IncludeOutputGroupsInVSIX>$(DefaultIncludeOutputGroupsInVSIX)</IncludeOutputGroupsInVSIX>
    </ProjectReference>
</ItemDefinitionGroup>

You can also be more selective and just add that metadata to ProjectReferences that don’t have a <Private>false</Private> metadata (meaning they are project references that shouldn’t be included in the VSIX/output directory at all). In that case, instead of the ItemDefinitionGroup, you instead set the metadata before the VSSDK determines the items to include in the VSIX:

<Target Name="IncludeSymbolsFromProjectReferences" BeforeTargets="GetVsixSourceItems">
    <!-- For any project references that are set to copy local ('Private' property != false), add the output groups for project references that are not set -->
    <ItemGroup>
        <ProjectReferenceWithConfiguration Condition="'%(ProjectReferenceWithConfiguration.Private)' != 'false' AND
                                                      '%(ProjectReferenceWithConfiguration.IncludeOutputGroupsInVSIX)' == ''">
            <IncludeOutputGroupsInVSIX>$(DefaultIncludeOutputGroupsInVSIX)</IncludeOutputGroupsInVSIX>
        </ProjectReferenceWithConfiguration>
    </ItemGroup>
</Target>

Note that in this case we annotate the ProjectReferenceWithConfiguration instead, which is what the VSSDK GetVsixSourceItems target uses.