Initialize a MATLAB Toolbox

As I learned during my MathWorks career, a set of recommended practices is much more likely to take hold if it can be automated.

I didn't start out to write about initializing a toolbox. Rather, I intended to write about an FFT padding strategy, and I anticipated it would be accompanied by a File Exchange submission.

As I considered what the code might look like, I realized that I would need a utility function related to extracting a subarray from an array of arbitrary dimension. And I thought that would be worth another File Exchange submission.

That got me to thinking about how I wanted to do File Exchange submissions, since the procedure would be somewhat different from what I did way back when I still worked for MathWorks (12 days ago). I recalled that there is a document, "MATLAB Toolbox Best Practices," authored by my MathWorks colleagues, that recommended a folder and file structure for a MATLAB "toolbox" that was intended to be hosted on GitHub (which was my plan).

In this context, what is a toolbox? The document explains it this way:

We use the term “toolbox” here to mean a collection of reusable MATLAB code that you want to share with other people. Toolboxes contain not just code files, but also data, apps, tests, and examples. Some toolboxes will be just a few files; others may be extensive and represent years of effort by multiple people. The guidelines we present can be adapted for toolboxes that are large or small, casual or sophisticated.

I wanted to try following the recommended practices, as least for the kind of projects I initially had in mind. As the document says: "The guidelines we present can be adapted for toolboxes that are large or small, casual or sophisticated."

The document is fairly lengthy, and it covers a wide variety of possible "toolboxes" and their contents. Combining several of the recommendations results in a set of folders and files that might look something like this.

arithmetic/
│   .gitattributes
│   .gitignore
|   README.md
|   license.txt
|   toolboxPackaging.prj
├───buildUtilities/
├───release/
|       Arithmetic Toolbox.mltbx
├───tests/
|       testAdd.m
└───toolbox/
    |   add.m
    |   functionSignatures.json
    |   gettingStarted.mlx
    ├───+describe/
    |       add.m
    ├───apps/
    |       arithmetic.mlapp
    ├───examples/
    |       usingAdd.mlx
    └───internal/
        |   addLiveTask.m
        |   intToWord.m
        └───resources/
                liveTasks.json

This is more than what I needed for the simple File Exchange submissions that I planned. Even a simplified organization, though, seemed like a lot of steps to me. So my software developer instincts kicked in, and I decided to try automating it. As I learned during my MathWorks career, a set of recommended practices is much more likely to take hold if it can be automated.

After some experimentation, I revised and simplified the structure to suit my needs as follows:

  • Changed license.txt to LICENSE.md.
  • Changed to use matlab.addons.toolbox.package.packageToolbox instead of using a packaging file, toolboxPackaging.prj, because packageToolbox is easier to automate.
  • Put the build utilities at the top level, instead of using a buildUtilities subfolder, to make it easier to use buildtool.
  • Eliminate the namespace, apps, and internal subfolders.
  • Use function argument validation, and the automatic tab completion it provides, instead of functionSignatures.json.

I wrote one function to make all this happen, and I called it inittbx. It is used this way:

inittbx("add")
cd add
buildtool
** Starting check
Analysis Summary:
    Total Files: 3
         Errors: 0 (Threshold: 0)
       Warnings: 0 (Threshold: Inf)
** Finished check

** Starting test
.

Test Summary:
    Total Tests: 1
         Passed: 1
         Failed: 0
     Incomplete: 0
       Duration: 0.00061558 seconds testing time.
                 
** Finished test

** Starting package
** Finished package

Those steps create the following:

add/
│   .gitattributes
│   .gitignore
|   buildfile.m
|   CHECKLIST.md
|   LICENSE.md
|   packageToolbox.m
|   README.md
|   toolboxOptions.m
├───release/
|       add Toolbox.mltbx
├───tests/
|       add_test.m
└───toolbox/
    |   add.m
    |   gettingStarted.mlx
    ├───examples/
    └       HelpfulExample.mlx

The created files are mostly just stubs. The file CHECKLIST.md reminds me of all the steps needed to complete the job.

# Checklist for Completing Toolbox

After calling `inittbx` to initialize your toolbox folder hierarchy,
you can use this checklist as a reference for making the additional
changes that are needed. After you have made these changes, you can
delete the checklist file.

- [ ] Initialize Git repository and commit the initial files produced
by `inittbx`.
- [ ] Edit README.md. Include a brief toolbox summary, installation
instructions, and a pointer to gettingStarted.mlx. Optionally, follow
with information for toolbox collaborators.
- [ ] Edit LICENSE.md. The license file helps everyone understand how
to use, change, and distribute the toolbox. If you do not provide a
license, then normal copyright rules will prohibit others from
copying, distributing, or modifying your work. If you accept
repository contributions from others, then your own use of the
repository may also become restricted.[^1] Avoid writing your own
license text. It is better to choose an existing license that meets
your needs. See https://choosealicense.com for help choosing a
license.
- [ ] Review and revise the options in toolboxOptions.m, especially
`ToolboxName` and `ToolboxVersion`.
- [ ] Replace the stub code in the `toolbox` folder with your own.
- [ ] Revise the test file in `tests` to test your own code.
- [ ] Revise `gettingStarted.mlx`. This file introduces your toolbox
and illustrates the primary workflows. It should link to any examples
that you create.
- [ ] Add one or more example files to the `toolbox/examples` folder.
- [ ] Add an "Open in MATLAB Online" badge using [this
tool](https://www.mathworks.com/products/matlab-online/git.html)
- [ ] If your GitHub repository has been linked to a MATLAB Central
File Exchange submission, then add a File Exchange badge to your
README.md. See the instructions at the top of your GitHub-linked File
Exchange submission.

This toolbox initialization helper, inittbx, is now available on File Exchange as well as on GitHub. I prepared the submission with the help of inittbx, of course.

Don't worry, I will get back to the FFT padding question soon.

MathWorks Requests

In the course of working on inittbx, I came across two issues that I would like MathWorks to address. One is a workflow gap, and one is a needed document clarification. I have sent these issues to MathWorks support.

Enhancement Request: Function to Generate UUIDs

The function matlab.addons.toolbox.packageToolbox can take a ToolboxOptions input, and that is the syntax I use for automating the toolbox packaging. Initializing that input requires that you provide a unique identifier for the toolbox. Furthermore:

If you want to share your toolbox on MATLAB File Exchange, identifier must follow the RFC 4122 specification which defines a Uniform Resource Name namespace for UUIDs (Universally Unique Identifier). For more information, see https://www.rfc-editor.org/info/rfc4122.

However, as far as I know, MATLAB does not provide a documented way to generate a UUID that conforms to this requirement. The documentation examples for ToolboxOptions make this gap fairly obvious.

Documentation Request: Clarify the Calling Syntax for Build Action Function Handles

I learned a lot about buildtool in this project. You can define a set of named tasks that buildtool can perform. One way to define a task's actions is to provide one or more function handles. The documentation should mention that these function handles must accept an input argument, and it should explain what that input argument is.