Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ThisAssembly.Vsix for VSIX extensibility projects #412

Merged
merged 1 commit into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 -->