Creating a Build Process Plug-In
This topic will walk you through the creation of a plug-in project.
The first step is to determine what items will appear in the plug-in configuration. The
configuration is stored as an XML fragment in the help file builder project file. The root node is always
configuration. Define your own elements to contain the plug-in configuration that will
be nested within the root element. When first added to a project, the configuration will be empty. Your plug-in
should use appropriate default values as needed. It is possible that a plug-in will have no configurable
elements. In that case, there is nothing to add to the default configuration. Below is an example of a
configuration that is passed to a plug-in.
<configuration deleteAfterDeploy="true">
<deploymentLocation id="help1x" location="..\..\SHFB\Deploy\Help\">
<userCredentials useDefault="true" userName="" password="" />
<proxyCredentials useProxy="false" proxyServer="">
<userCredentials useDefault="true" userName="" password="" />
</proxyCredentials>
</deploymentLocation>
<deploymentLocation id="help2x" location="">
<userCredentials useDefault="true" userName="" password="" />
<proxyCredentials useProxy="false" proxyServer="">
<userCredentials useDefault="true" userName="" password="" />
</proxyCredentials>
</deploymentLocation>
<deploymentLocation id="helpViewer" location="" renameMSHA="false">
<userCredentials useDefault="true" userName="" password="" />
<proxyCredentials useProxy="false" proxyServer="">
<userCredentials useDefault="true" userName="" password="" />
</proxyCredentials>
</deploymentLocation>
<deploymentLocation id="website" location="..\WebHelp\SHFB\">
<userCredentials useDefault="true" userName="" password="" />
<proxyCredentials useProxy="false" proxyServer="">
<userCredentials useDefault="true" userName="" password="" />
</proxyCredentials>
</deploymentLocation>
<deploymentLocation id="openXml" location="">
<userCredentials useDefault="true" userName="" password="" />
<proxyCredentials useProxy="false" proxyServer="">
<userCredentials useDefault="true" userName="" password="" />
</proxyCredentials>
</deploymentLocation>
</configuration>
This section describes how to create and configure the build process plug-in project. It will
describe the process for a C# project but the steps should be fairly similar for a VB.NET project with a few
differences in the configuration option titles.
In Visual Studio, select the New Project option. In the New Project
dialog box, select the C# or VB.NET language, and then select the Documentation subcategory. Select
the Sandcastle Help File Builder Plug-In template, give it a name and click OK to create it.
Once it has been created, right click on the project and select Properties.
In the Application tab, set the assembly name and default namespace as you see fit.
In the Package tab, set the NuGet package properties.
By default, the Debug project properties are set to use the standalone GUI for debugging
which will help you see if the component can be located, that the configuration form is working if you created
one for the component, and that it is working within the build as expected. See the
Debugging Components and Plug-Ins for some special requirements when debugging
components and plug-ins.
Using the standalone GUI as the host is easier than using Visual Studio as the package
locks the assemblies once the help file builder project has loaded them for use and you will not be able to
rebuild them. Shutting down the standalone GUI frees the assemblies so that you can rebuild them and start a
new debugging session.
In the help file builder project that you use to test your plug-in, set the project's
Component Path property to the folder containing your build component's assembly. If
you have opened the project's Components property page before setting the component path, you may need
to close and reopen the project in order for it to discover your build component assembly.
Optionally, select the Signing tab and check the "Sign the assembly" checkbox.
Select "<New...>" from the "Choose a strong name key file" dropdown, enter a filename, and click OK to
create the key file. You can protect the key file with a password if you like or uncheck the option to create
one without a password.
You are now ready to edit the plug-in class itself. See the comments in the template class for
information on how to get started. Some general information is given below. Note that multiple plug-ins can
reside within the same assembly. Add new class files to the project and implement the necessary methods as
described below and as shown in the template class.
The plug-in is derived from the
SandcastleBuilder.Utils.BuildComponentIPlugIn
interface and consists of a few properties and methods that you must implement. These are described below.
Review the code for "TODO:" comments to find sections that need attention such as
setting the plug-in's ID, defining the execution points, etc. If you followed the steps in the
Creating the Project section, you can run the project and debug it by setting
breakpoints in the plug-in's code. As noted above, set the test project's Component Path
property to the folder containing the plug-in assembly first. If necessary, close and reopen the test project
so that it can discover the plug-in.
The plug-in is a Managed Extensibility Framework (MEF) component. The
HelpFileBuilderPlugInExportAttribute
is used to define the necessary metadata that enables the help file builder to load and use the component.
The Id parameter is required and is used to uniquely identify the
plug-in.
The Version property is optional and allows you to define the plug-in
version displayed in the help file builder property page when the plug-in is selected.
The Copyright property is optional and allows you to define the
plug-in copyright displayed in the help file builder property page when the plug-in is selected.
The Description property is optional and allows you to define the
plug-in description displayed in the help file builder property page when the plug-in is selected.
The RunsInPartialBuild property is optional and allows you to define
whether or not the plug-in runs during a partial build. Partial builds occur when generating the reflection data
for the namespace comments and API filter editor dialogs. In such cases, the plug-in may not be needed. If this
property returns false, the default, the plug-in is omitted which can speed up the partial build. Set it to true
if your plug-in adds information needed for the reflection data or namespace comments.
The IsHidden property is optional and allows you to define
whether or not the plug-in is hidden and does not appear on the project property page. If false, the default,
the plug-in will appear on the property page. If true, the property page will not list it as one that can
be added to the project. This is useful for presentation style dependency plug-ins that have no configurable
elements and thus do not need to be manually added to the project to override settings.
The interface consists of one property and four methods.
The ExecutionPoints property is probably the most important member of
the interface. It returns a collection of
ExecutionPoint
objects that define at which steps in the build process the plug-in should be ran. Execution points can be set
to run before and/or after a build step to supplement the default processing or they can be set to run instead of
the build step to completely suppress or replace the default processing. See the
BuildStep
enumeration for a list of the defined build steps.
Each execution point can be assigned a priority. The execution priority is used to determine
the order in which the plug-ins will be executed. Those with a higher priority value will be executed before
those with a lower value. Those with an identical priority may be executed in any order within their group. If
not specified, a default priority of 1,000 is used.
The Initialize method is used to initialize the plug-in at the start
of the build process. It is passed a reference to the current
BuildProcess
and an XPath navigator containing the plug-in configuration. You should cache a copy of the build process
reference for use during execution as it contains many properties and methods that you will find useful.
The Execute method is called to perform the plug-in processing during
the relevant build steps. It is passed an
SandcastleBuilder.Utils.BuildComponentExecutionContext
object that defines the current execution context. If your plug-in determines that it does not need to run, it
should set the ExecutionContextExecuted
property to false. This is especially important for plug-ins with the InsteadOf
behavior. If none run, the default processing will occur.
The Dispose method can be used to dispose of any resources used during
the build process. If you do not have any resources that need to be disposed of, this method can be left as
it is and the finalizer method can be removed.
There are certain conditions to be aware of when choosing and coding the execution behavior for
your plug-in.
The earliest execution point for a plug-in is the After behavior with
the Initializing step.
The Before behavior cannot be used with the Initializing,
Canceled, or Failed steps.
The InsteadOf behavior cannot be used with the Initializing,
Completed, Canceled, or Failed steps.
The InsteadOf behavior always takes precedence. The build process
will not call any plug-ins with Before or After behavior for
the step if an InsteadOf plug-in runs. As such, it is up to the plug-in with the
InsteadOf behavior to call the
ExecuteBeforeStepPlugIns
and ExecuteAfterStepPlugIns
methods to run them if needed.
If the build step involves creating, modifying, or deleting a non-script file or folder, the
Before behavior is always executed prior to creating/modifying/deleting the file or
folder. Use the After behavior if you need to guarantee that the file or folder exists
or you do not want your version overwritten. The exceptions to this rule are MSBuild project files
(*.proj) and MRefBuilder.config which are always created prior to
running the Before behavior plug-ins. This allows you to modify the script files prior
to them being ran.
If the build step involves creating, modifying, or deleting a script file or folder and the
InsteadOf behavior is used, the plug-in is responsible for creating the script
file/folder. All normal processing involving the file/folder including its creation is skipped.
The GenerateHelpFormatTableOfContents,
GenerateHelpFileIndex, GenerateHelpProject,
UpdateTableOfContents, and CompilingHelpFile steps will run
once for each help file format selected. You can use the
CurrentFormat
property to determine the current help file format being built in order to skip or alter the plug-in's processing
based on the help file format.
Be aware that the HTML Help 1 index and table of contents files and the website table of
contents file are actually generated as part of the ExtractHtmlInfo step. However, the
before and after plug-in behaviors for the index and table of contents steps in each of those formats will still
be executed.