Skip to content

Commit

Permalink
Add ThisAssembly.Vsix for VSIX extensibility projects
Browse files Browse the repository at this point in the history
Fixes #405
  • Loading branch information
kzu committed Sep 30, 2024
1 parent 91afce4 commit ca18b8f
Show file tree
Hide file tree
Showing 7 changed files with 251 additions and 0 deletions.
7 changes: 7 additions & 0 deletions ThisAssembly.sln
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ThisAssembly.Git", "src\Thi
EndProject
Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "Shared", "src\Shared\Shared.shproj", "{8E0F2A43-C9AF-4E46-BF70-CF8645371C12}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThisAssembly.Vsix", "src\ThisAssembly.Vsix\ThisAssembly.Vsix.csproj", "{0C4A87B0-FBD7-42BE-A504-DBBC55132146}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -65,6 +67,10 @@ Global
{F34F8470-7C60-4BB8-ACB5-569D6D0F6A46}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F34F8470-7C60-4BB8-ACB5-569D6D0F6A46}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F34F8470-7C60-4BB8-ACB5-569D6D0F6A46}.Release|Any CPU.Build.0 = Release|Any CPU
{0C4A87B0-FBD7-42BE-A504-DBBC55132146}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0C4A87B0-FBD7-42BE-A504-DBBC55132146}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0C4A87B0-FBD7-42BE-A504-DBBC55132146}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0C4A87B0-FBD7-42BE-A504-DBBC55132146}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -73,6 +79,7 @@ Global
SolutionGuid = {ADF2DC9F-5F77-4F6E-8804-CFF5892CD064}
EndGlobalSection
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Shared\Shared.projitems*{0c4a87b0-fbd7-42be-a504-dbbc55132146}*SharedItemsImports = 5
src\Shared\Shared.projitems*{14d0c5ba-8410-4454-87a2-7bf5993e1ea2}*SharedItemsImports = 5
src\Shared\Shared.projitems*{31c53cce-13fc-4890-803a-85cd146b909b}*SharedItemsImports = 5
src\Shared\Shared.projitems*{3e7158b5-bfef-4416-8226-9096c7c98268}*SharedItemsImports = 5
Expand Down
Binary file added img/ThisAssembly.Vsix.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/ThisAssembly.Vsix/ThisAssembly.Vsix.Pack.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project>

<!-- Overwrites Pack to mean CreateVsixContainer -->
<Target Name="Pack" DependsOnTargets="CreateVsixContainer" Condition="'$(IsPackable)' != 'false'" />

</Project>
41 changes: 41 additions & 0 deletions src/ThisAssembly.Vsix/ThisAssembly.Vsix.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>latest</LangVersion>
<IsRoslynComponent>true</IsRoslynComponent>
<PackBuildOutput>false</PackBuildOutput>
<PackageTags>$(PackageTags) vsix</PackageTags>
</PropertyGroup>

<PropertyGroup>
<PackageId>ThisAssembly.Vsix</PackageId>
<Description>This package generates a static `ThisAssembly.Vsix` class with public
constants exposing key VSIX manifest properties. For example:

partial class ThisAssembly
{
public static partial class Vsix
{
public const string Id = "MyVsix";
}
}
</Description>
</PropertyGroup>

<ItemGroup>
<None Remove="ThisAssembly.Metadata.targets" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="NuGetizer" Version="1.2.2" />
<PackageReference Include="Microsoft.VSSDK.BuildTools" Version="17.0.5240" IncludeAssets="none" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ThisAssembly.Constants\ThisAssembly.Constants.csproj" />
</ItemGroup>

<Import Project="..\Shared\Shared.projitems" Label="Shared" />

</Project>
8 changes: 8 additions & 0 deletions src/ThisAssembly.Vsix/ThisAssembly.Vsix.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<Project>

<PropertyGroup>
<!-- Allows redefining Pack as CreateVsixContainer -->
<ImportNuGetBuildTasksPackTargetsFromSdk>false</ImportNuGetBuildTasksPackTargetsFromSdk>
</PropertyGroup>

</Project>
123 changes: 123 additions & 0 deletions src/ThisAssembly.Vsix/ThisAssembly.Vsix.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<Project>

<PropertyGroup>
<Vsix2011>&lt;Namespace Prefix='vs' Uri='http://schemas.microsoft.com/developer/vsx-schema/2011'/&gt;</Vsix2011>
<Vsix2010>&lt;Namespace Prefix='vs' Uri='http://schemas.microsoft.com/developer/vsx-schema/2010'/&gt;</Vsix2010>

<!-- This is required as `true` to cause FindSourceVsixManifest target to run and locate the manifest
which is required for the DetokenizeVsixManifestFile to run.
By setting this property, we will always succeed independently of the value of $(CreateVsixContainer),
$(DeployExtension) and $(CopyVsixExtensionFiles) -->
<CopyVsixManifestToOutput>true</CopyVsixManifestToOutput>
</PropertyGroup>

<ItemGroup>
<!-- Make sure we're always private to the referencing project.
Prevents analyzers from "flowing out" of the referencing project. -->
<PackageReference Update="ThisAssembly.Vsix" PrivateAssets="all" PackTransitive="false" />
</ItemGroup>

<Target Name="PrepareVsixConstants"
Condition="$(VsSDKInstall) != ''"
BeforeTargets="PrepareConstants"
DependsOnTargets="DetokenizeVsixManifestFile">

<!-- DetokenizeVsixManifestFile populates the values in $(IntermediateVsixManifest) from project references -->

<XmlPeek Condition="'$(VsixID)' == ''" Namespaces="$(Vsix2010)" XmlInputPath="$(IntermediateVsixManifest)" Query="/vs:Vsix/vs:Identifier/@Id">
<Output TaskParameter="Result" PropertyName="VsixID" />
</XmlPeek>
<XmlPeek Condition="'$(VsixID)' == ''" Namespaces="$(Vsix2011)" XmlInputPath="$(IntermediateVsixManifest)" Query="/vs:PackageManifest/vs:Metadata/vs:Identity/@Id">
<Output TaskParameter="Result" PropertyName="VsixID" />
</XmlPeek>

<XmlPeek Condition="'$(VsixVersion)' == ''" Namespaces="$(Vsix2010)" XmlInputPath="$(IntermediateVsixManifest)" Query="/vs:Vsix/vs:Identifier/@Version">
<Output TaskParameter="Result" PropertyName="VsixVersion" />
</XmlPeek>
<XmlPeek Condition="'$(VsixVersion)' == ''" Namespaces="$(Vsix2011)" XmlInputPath="$(IntermediateVsixManifest)" Query="/vs:PackageManifest/vs:Metadata/vs:Identity/@Version">
<Output TaskParameter="Result" PropertyName="VsixVersion" />
</XmlPeek>

<XmlPeek Condition="'$(VsixName)' == ''" Namespaces="$(Vsix2010)" XmlInputPath="$(IntermediateVsixManifest)" Query="/vs:Vsix/vs:Identifier/vs:Name/text()">
<Output TaskParameter="Result" PropertyName="VsixName" />
</XmlPeek>
<XmlPeek Condition="'$(VsixName)' == ''" Namespaces="$(Vsix2011)" XmlInputPath="$(IntermediateVsixManifest)" Query="/vs:PackageManifest/vs:Metadata/vs:DisplayName/text()">
<Output TaskParameter="Result" PropertyName="VsixName" />
</XmlPeek>

<XmlPeek Condition="'$(VsixDescription)' == ''" Namespaces="$(Vsix2010)" XmlInputPath="$(IntermediateVsixManifest)" Query="/vs:Vsix/vs:Identifier/vs:Description/text()">
<Output TaskParameter="Result" PropertyName="VsixDescription" />
</XmlPeek>
<XmlPeek Condition="'$(VsixDescription)' == ''" Namespaces="$(Vsix2011)" XmlInputPath="$(IntermediateVsixManifest)" Query="/vs:PackageManifest/vs:Metadata/vs:Description/text()">
<Output TaskParameter="Result" PropertyName="VsixDescription" />
</XmlPeek>

<XmlPeek Condition="'$(VsixPublisher)' == ''" Namespaces="$(Vsix2010)" XmlInputPath="$(IntermediateVsixManifest)" Query="/vs:Vsix/vs:Identifier/vs:Author/text()">
<Output TaskParameter="Result" PropertyName="VsixPublisher" />
</XmlPeek>
<XmlPeek Condition="'$(VsixPublisher)' == ''" Namespaces="$(Vsix2011)" XmlInputPath="$(IntermediateVsixManifest)" Query="/vs:PackageManifest/vs:Metadata/vs:Identity/@Publisher">
<Output TaskParameter="Result" PropertyName="VsixPublisher" />
</XmlPeek>


<ItemGroup>
<Constant Include="Vsix.Id" Value="$(VsixID)" Root="." />
<Constant Include="Vsix.Name" Value="$(VsixName)" Root="." />
<Constant Include="Vsix.Description" Value="$(VsixDescription)" Root="." />
<Constant Include="Vsix.Publisher" Value="$(VsixPublisher)" Root="." />
<Constant Include="Vsix.Version" Value="$(VsixVersion)" Root="." />
</ItemGroup>

</Target>

<Target Name="VsixID" Returns="$(VsixID)">
<PropertyGroup>
<VsixID Condition="$(VsixID) ==''">$(PackageId)</VsixID>
<VsixID Condition="$(VsixID) ==''">$(AssemblyName)</VsixID>
</PropertyGroup>
</Target>

<Target Name="VsixVersion" Returns="$(VsixVersion)">
<PropertyGroup>
<VsixVersion Condition="$(VsixVersion) ==''">$(Version)</VsixVersion>
</PropertyGroup>
</Target>

<Target Name="VsixDisplayName" Returns="$(VsixDisplayName)">
<PropertyGroup>
<VsixDisplayName Condition="$(VsixDisplayName) ==''">$(Title)</VsixDisplayName>
</PropertyGroup>
</Target>

<Target Name="VsixDescription" Returns="$(VsixDescription)">
<PropertyGroup>
<VsixDescription Condition="$(VsixDescription) ==''">$(Description)</VsixDescription>
</PropertyGroup>
</Target>

<Target Name="VsixProduct" Returns="$(VsixProduct)">
<PropertyGroup>
<VsixProduct Condition="$(VsixProduct) ==''">$(Product)</VsixProduct>
</PropertyGroup>
</Target>

<Target Name="VsixPublisher" Returns="$(VsixPublisher)">
<PropertyGroup>
<VsixPublisher Condition="$(VsixPublisher) ==''">$(Company)</VsixPublisher>
</PropertyGroup>
</Target>

<Target Name="VsixLanguage" Returns="$(VsixLanguage)">
<PropertyGroup>
<VsixLanguage Condition="$(VsixLanguage) ==''">$(NeutralLanguage)</VsixLanguage>
<VsixLanguage Condition="$(VsixLanguage) ==''">en-US</VsixLanguage>
</PropertyGroup>
</Target>

<ItemGroup Condition="$(VsSDKInstall) != '' and $(PackageId) != ''">
<ProjectCapability Include="Pack"/>
</ItemGroup>

<Import Project="ThisAssembly.Vsix.Pack.targets" Condition="$(VsSDKInstall) != '' and $(PackageId) != ''"/>

</Project>
66 changes: 66 additions & 0 deletions src/ThisAssembly.Vsix/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<!-- include https://github.com/devlooped/.github/raw/main/sponsorlinkr.md -->
<!-- #vsix -->
This package generates a static `ThisAssembly.Vsix` class with public
constants exposing key VSIX manifest properties.

![](https://raw.githubusercontent.com/devlooped/ThisAssembly/main/img/ThisAssembly.Vsix.png)

For example:

```csharp
partial class ThisAssembly
{
public static partial class Vsix
{
public const string Id = "MyVsix";
}
}
```

In addition to making the [VSIX manifest metadata](https://learn.microsoft.com/en-us/visualstudio/extensibility/vsix-extension-schema-2-0-reference?view=vs-2022#metadata-element)
properties available as constants, the package also provides targets for those properties
with sensible defaults from project properties so that the manifest can leverage
[placeolder syntax](https://learn.microsoft.com/en-us/visualstudio/extensibility/vsix-extension-schema-2-0-reference?view=vs-2022#metadata-element)
and avoid duplication.

For example, the following `source.extension.vsixmanifest` uses values from MSBuild exclusively:
```xml
<PackageManifest Version="2.0.0"
xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011"
xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
<Metadata>
<!-- You can use the |ProjectName;TargetName| syntax throughout this manifest, BTW -->
<Identity Id="|%CurrentProject%;VsixId|" Version="|%CurrentProject%;VsixVersion|" Language="|%CurrentProject%;VsixLanguage|" Publisher="|%CurrentProject%;VsixPublisher|" />
<DisplayName>|%CurrentProject%;VsixDisplayName|</DisplayName>
<Description>|%CurrentProject%;VsixDescription|</Description>
</Metadata>
...
</PackageManifest>
```

The available properties and their default values are:

| Name | Default Value |
|-------------------|-----------------------------------|
| VsixID | $(PackageId) or $(AssemblyName) |
| VsixVersion | $(Version) |
| VsixDisplayName | $(Title) |
| VsixDescription | $(Description) |
| VsixProduct | $(Product) |
| VsixPublisher | $(Company) |
| VsixLanguage | $(NeutralLanguage) or 'en-US' |
| VsixDescription | $(Description) |

As shown in the example above, the syntax for using these properties from the `.vsxmanifest` is
`|%CurrentProject%;[PROPERTY]|`. This is because the package defines a corresponding target to
retrieve each of the above properties. You can provide a different value for each property via
MSBuild as usual, of course.

Since the `$(PackageId)` property can be used as the VSIX ID, the `Pack` target is redefined to
mean `CreateVsixManifest`, so "packing" the VSIX is just a matter of right-clicking the VSIX
project and selecting "Pack".

<!-- #vsix -->
<!-- include ../visibility.md -->
<!-- include https://github.com/devlooped/sponsors/raw/main/footer.md -->
<!-- exclude -->

0 comments on commit ca18b8f

Please sign in to comment.