Awesome
PLGX Build Tasks
The MSBuild task in this package uses build items, item metadata and a "clean room" implementation of the KeePass 2.x archive creation utility to generate .PLGX files as a plugin project's build product. This was inspired by and partially mimics the KeePassPluginDevTools package, a.k.a. PlgxTool.
So....why?? Primarily for development flexibility. While the strict coding requirements for KeePass plugins are well defined, new build environments such as the dotnet
CLI are welcome changes to old-school .NET development. Also, by fully integrating .PLGX production within MSBuild, new features leveraging intermediate build products are achievable, such as localization resource deployment.
The hope is to encourage new plugin development, and help existing plugin authors migrate to new, perhaps improved tooling.
- Requirements
- Quick Start
- Background (what's all this about KeePass plugins?)
- Unique Features
- PlgxTool Compatible Features
- Missing PlgxTool Features
- Future Enhancements
- Customization
- .PLGX Creation Properties
Requirements
- A .NET Framework development environment including MSBuild v15 or later, such as Visual Studio 2017 or
dotnet
CLI. - The .NET SDK or targeting pack supporting your plugin's target framework.
The software has been tested thoroughly with Visual Studio 2019 Community, and .NET 5 development tools, with net472
and net45
TFMs. And only on Windows.
Background
.PLGX files are an installation media file format often distributed by plugin providers as recommended by KeePass. Traditionally, .PLGX archives contain the source code files of the plugin, optional WinForms-based resources, non-framework assembly dependencies, and a copy of the plugin's ".csproj" project file. When KeePass loads a new plugin, it extracts the contents of the .PLGX archive, reads select portions of its project file, and invokes the installation target's .NET Framework C# compiler to create and install the plugin assembly. This is done "on the fly", usually without user intervention. Ostensibly, this convention ensures that the plugin is compliant with the interface and runtime characteristics of the installed version of KeePass. Further, the scheme allows KeePass to maintain control of a central "cache" of plugins installed on the target machine.
Today, KeePass users benefit from a large collection of useful plugins. But while the KeePass v2 plugin interface and the .NET Framework are both now quite mature, with only infrequent and compatibility-conscious changes, .PLGX archive distribution remains a well established regimen within the plugin community.
Unique Features of This Tool
- Supports both .NET SDK and traditional .NET Framework project file types.
- Supports either
<PackageReference>
orpackages.config
NuGet dependencies. - Supports deployment of MSBuild-generated, resource-only satellite assemblies, commonly used for localization (Hint: currently, this feature requires you to set the
<Satellite_ProductName>
property to specify something other than "KeePass Plugin", so KeePass doesn't confuse it with the plugin assembly). - Uses MSBuild item and item metadata products, rather than a separate scan of project file contents, to populate the archive.
- Archives a task-generated, minimal project file, rather than a copy of the development project file. The development project file name need not match the plugin symbolic name.
- .PLGX files are almost always smaller.
- Optionally,
<EmbeddedResource>
items may be archived as pre-complied .RESOURCE files, for a modest boost in initialization performance and further reduced .PLGX file size. - Several MSBuild property extensions are defined and can be overridden to customize the output, including archive contents, name, output path, and KeePass .PLGX deployment options.
- Build-time checks for a few common plugin development pitfalls produce build errors or warnings.
- Full integration with MSBuild 'Clean' target features to ensure proper removal of .PLGX output.
PlgxTool Compatible Features
- Installs as a NuGet build tool package.
- Observes
<ExcludeFromPlgx/>
item metadata, to exclude specific project items, which would otherwise be included, from the archive. - Enabled via invocation of a post-build target added to the project file.
PlgxTool Incompatibility
- The
<PlgxConfiguration>
property is not supported. That property's<Prerequisistes>
XML fragment, which provides the means to set some .PLGX creation options, is replaced by individual, first-class MSBuild properties.
Limitation
- Microsoft NuGet packages providing .NET Framework "shim" types for older frameworks are not supported by the current PLGX format.
These packages can allow projects targeting older .NET frameworks access to features available in newer .NET frameworks. One example is System.Runtime.InteropServices.RuntimeInformation
. The types in this package are provided by mscorlib.dll
, etc., in .NET 4.7.1 and newer frameworks. By referencing the NuGet package, projects targeting frameworks as old as .NET 4.5 may also access those types. Unfortunately, this technique won't work for PLGX builds run by KeePass, unless the target machine is actually running an older framework. Machines running .NET 4.7.1 or later will fail to install the plugin via PLGX, due to the collision of types in System.Runtime.InteropServices.RuntimeInformation
and mscorlib
.
Hint: When PLGX builds fail, use the
-debug
option ofKeePass.exe
to see compiler error output.
KeePass doesn't currently provide a way to control how these supplemental references are compiled into the plugin (like MSBuild does, for example). The only workaround for this issue is to avoid referencing such packages.
Project TODOs
- Add companion task to produce a .ZIP archive for "portable installation" distributions.
- Enhance build-time error checks, possibly via Reflection, to ensure the output assembly meets KeePass' "conventions".
- Supplement MSBuild project file schema to include targets, properties, and lightweight documentation.
- Source code "minify" option for yet smaller .PLGX files.
- VSIX extension to provide plugin "starter" project templates (maybe a wizard?) including the NuGet reference.
Quick Start
-
Add the NuGet package reference to a plugin project using a NuGet package management tool of choice (too many to list here).
-
Run a release build of the project. If all requirements are met, a distributable .PLGX file should be copied to the output directory. To customize the build, read on.
Likely Next Steps
- By default, the
GeneratePlgx
property is set to create the .PLGX file after a successful release build, precisely as shown below. Optionally modify theGeneratePlgx
property in your project file if desired. When settrue
, thePlgxBuild
task is run after a successful build.
<PropertyGroup Condition=" '$(GeneratePlgx)' == '' and '$(Configuration)' == 'Release' ">
<GeneratePlgx>true</GeneratePlgx>
</PropertyGroup>
- To exclude project items from the .PLGX, such as certain
<None>
or<Content>
files, use the<ExcludeFromPlgx>
metadata tag in the XML content of the item. For example:
<None Include="MyTemplate.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>MyTemplate.cs</LastGenOutput>
<Visible>true</Visible>
<ExcludeFromPlgx/>
</None>
- Optionally configure customizations with property extensions. For example, alter the output path of the .PLGX file:
<PropertyGroup>
<PlgxArchiveFileName>$(MyAssemblyName)-$(MyVersion)</PlgxArchiveFileName>
<PlgxOutputFolder>bin\$(Configuration)\plgx\</PlgxOutputFolder>
</PropertyGroup>
Properties
Use the following MSBuild property extensions to set various options, including those that configure the way KeePass prepares the plugin for loading. Some of these correspond to options specified in the <PlgxConfiguration>
property used by PlgxTool. In particular, the properties named PlgxTarget* correspond to manifest options in the .PLGX archive.
Specify these properties within a <PropertyGroup>
element of your project file to override the default values shown in the table.
Property | Default value | Description |
---|---|---|
PlgxArchiveFileName | Base name of the plugin assembly, for example MyPlugin . | The name of the output .PLGX file, specified as a base file name (without the .PLGX extension). |
PlgxOutputFolder | The project output folder, $(OutputPath) . | The directory where the output .PLGX file will be placed. Must be specified as a partial path relative to the project file directory, e.g., "bin\$(Configuration)\plgx\". |
PlgxTargetKpVersion | No default. | Optional. If specified, sets the --plgx-prereq-kp option to declare the "minimum" KeePass version supported by the plugin. KeePass only recognizes release numbers given in simple, dotted notation, e.g., "2.09". |
PlgxTargetNetFramework | The project target framework, $(TargetFrameworkVersion) . | Sets the --plgx-prereq-net option to declare the .NET Framework version requirements of the plugin. Valid values are dotted notation .NET Framework version numbers with no prefix. For example, "4.5" or "4.7.2". Set this property to an empty string to omit --plgx-prereq-net from the .PLGX manifest. |
PlgxTargetOs | Windows | Sets the --plgx-prereq-os option to declare the operating system required by the plugin. KeePass recognizes only two values: Unix , and Windows . Set this property to an empty string to omit --plgx-prereq-os from the .PLGX manifest. |
PlgxTargetPtrSize | No default. | Optional. If specified, sets the --plgx-prereq-ptr option to declare the pointer size (platform architecture) required by the plugin. Valid values are 4 and 8 . |
PlgxUseCompiledResource | false | If set true , .RESOURCE files created by MSBuild are archived, rather than the <EmbeddedResource> .RESX source files from which they derive. This improves initialization performance and usually reduces .PLGX file size. If false , .RESX source files are archived instead. Recommended: if set to true , use the default value of the PlgxTargetNetFramework property, to allow KeePass to determine if the target platform is compatible with the archived .RESOURCE files. |
PlgxReferencesArchivedFolderName | ___PLGX_References | Defines the name of the folder within the .PLGX archive where "copied", non-framework assembly dependencies, if any, reside. |
PlgxSatelliteAssembliesArchivedFolderName | ___PLGX_Satellites | Defines the name of the folder within the .PLGX archive where resource-only satellite assemblies, if any, reside. |