MISS_HIT 0.9.45-dev Configuration System
The behaviour of the MISS_HIT tools can be controlled with configuration files. This document describes the syntax and semantics of these files, and provides a more general explanation of how to set up and configure MISS_HIT for your projects.
Picture of text

Setting up MISS_HIT

Setting up a new project

For new projects we recommend to apply MISS_HIT project-wide. Create a configuration file (in UTF-8 encoding) named miss_hit.cfg and place it at the root of the project containing:
project_root
copyright_entity: "Your Name"
This configuration file applies to all files in this directory and any sub-directories.
Individual sub-directories can be separately configured. Configuration files placed in sub-directories inherit all settings from their parent directory, except for the ones changed by such a file. For example if you have a directory containing auto-generated code (called src/autogen) that you do not want to check you can selectively disable analysis by creating a new configuration file src/autogen/miss_hit.cfg containing:
enable: false
The configuration files apply to all MISS_HIT tools. The general file format and syntax is described in this document. The specific settings applicable to each tool are described in the user manuals of each tool.

Applying MISS_HIT to an existing project

MISS_HIT has special support for legacy projects. The following process is recommended:
  1. Create top-level configuration file setting up all rules, but turning MISS_HIT off. For example:
    project_root
    enable: false
    copyright_entity: "Potato AG"
    line_length: 100
    suppress_rule: "operator_whitespace"
    
  2. Turn MISS_HIT on again in selected sub-directories. As time progresses, gradually enable for more and more sub-directories.
    enable: true
    
  3. Eventually remove all individual config files and turn MISS_HIT on in the top-level configuration.

Dealing with external projects

Larger, more complex projects may include source code from other projects or repositories. MISS_HIT includes several features to deal with this.
There are essentially two scenarios:
  • Cooperating - the included source, project, or repository also uses MISS_HIT.
  • Non-cooperating - the included source, project, or repository does not use MISS_HIT, or uses a much older version (newer versions likely generate more messages).
If the included code has its own miss_hit.cfg file with a project_root directive, then everything will work without further action: the project_root directive will stop the inheriting mechanism of configuration files, so any files in this included project will be analysed in their intended way.
If a project is not cooperating then the best option is to place a configuration file in the parent directory, excluding the non-cooperating project from analysis. For example if the directory structure is as follows:
/
  src
  external
    other_a
    other_b
Then add the following to the root configuration file:
exclude_dir: "external"
This will cause any MISS_HIT tools to completely skip these directories.

Dealing with multiple programs and shared code

MISS_HIT has special support for large shared repositories with multiple systems and shared code. While tools like MH Style will likely be run on all code, all the time, this does not make sense for other tools like MH Metric. Instead here you will likely want to create multiple reports, one for each component.
Consider for example a repository with the following layout:
  /
    components/
      swc/
        potato/
          src/
          test/
        kitten/
          src/
          test/
      libs/
        common/
          src/
          test/
Both the potato and kitten program depend on the common code. If I want to produce the code metric report for just kitten, I would have to carefully name each directory:
mh_metric components/swc/potato/src components/libs/common/src
With some configuration we can make this easier. First, we can define a library by adding a config file in components/libs/common/miss_hit.cfg:
library "Common" {
  paths {
    "src"
  }
  tests {
    "test"
  }
}
What this does is create a globally available library called Common. It's deliverable code is in src, but test is not. This is identical to how you would set up your matlabpath (in MATLAB) or path (in Octave). Most tools will only consider the deliverable source code; but mh_trace will also look at your tests.
Next, we want to declare our two programs. To do this for kitten program, we create an entry-point in components/swc/kitten/miss_hit.cfg:
entrypoint "Kitten_SWC" {
  paths {
    "src"
  }
  tests {
    "test"
  }
  libraries {
    "Common"
  }
}
You can now analyze kitten anywhere by using the entry-point command-line option:
mh_metric --entry-point=Kitten_SWC
Note: Currently this feature is just a nice shot-cut to existing functionality. In the future, this will be required for performing any kind of advanced static analysis.

Integrating MISS_HIT into CI/CD

Analyze early and often

WIP/TODO

Git Hooks

WIP/TODO

Hooks via pre-commit

The pre-commit project is an alternative way of unify running hooks in Git projects. You can use the following configuration snippet in your .pre-commit-config.yaml:
repos:
-   repo: local
    hooks:
    -   id: mh_style
        name: mh_style
        entry: mh_style
        args: [--process-slx, --fix]
        files: ^(.*\.(m|slx))$
        language: python
        additional_dependencies: [miss_hit_core]
    -   id: mh_metric
        name: mh_metric
        entry: mh_metric
        args: [--ci]
        files: ^(.*\.(m|slx))$
        language: python
        additional_dependencies: [miss_hit_core]
    -   id: mh_lint
        name: mh_lint
        entry: mh_lint
        files: ^(.*\.(m|slx))$
        language: python
        additional_dependencies: [miss_hit]

Travis

This is an example .travis.yml to use check your entire repository.
# Linux distribution (bionic beaver)
dist: bionic

# Language and version
language: python
python:
  - "3.6" # current default Python on Travis CI

cache:
  apt: true # only works with Pro version

# Install miss_hit
before_install:
  - pip3 install miss_hit

# Lists all the tasks we will do
jobs:
  include:
    - name: "miss_hit: checking code quality"
      script: mh_metric --ci
    - name: "miss_hit: checking code style"
      script: mh_style --process-slx
    - name: "miss_hit: finding bugs"
      script: mh_lint

GitHub action

This example GitHub action can be used to check your entire repository.
name: miss_hit

on:
  push:
    branches:
      - master
  pull_request:
    branches: '*'

jobs:
  build:

    runs-on: ubuntu-latest

    steps:

    - uses: actions/checkout@v2
      with:
        submodules: true
        fetch-depth: 1

    - name: Set up Python 3.6
      uses: actions/setup-python@v2
      with:
        python-version: 3.6

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip setuptools
        pip3 install miss_hit

    - name: MISS_HIT Metrics
      run: |
        mh_metric --ci

    - name: MISS_HIT Code style
      run: |
        mh_style --process-slx

    - name: MISS_HIT Bug finder
      run: |
        mh_lint
Picture of text

Configuration file Reference

Naming

A configuration file must have one of the following names:
  • miss_hit.cfg
  • .miss_hit
It is recommended to use miss_hit.cfg, except for the root directory of your project.

Scope

Configuration files apply to all files in the directory they are placed in (including any files in sub-directories).
A configuration file in a sub-directory inherits all options valid in the parent directory, and then subsequently over-rides any options it wants to.

Project Root

A project root is the basic configuration and is initially set to all default values.
A project root is any directory that fulfils one or more of the following:
  • Contains configuration file with the project_root directive
  • Was excluded by a exclude_dir directive

Syntax

Grammar:
DIRECTIVE ::= PROJECT_ROOT
            | GLOBAL_ENABLE
            | STYLE_APPLICATION
            | STYLE_CONFIGURATION
            | METRIC_LIMIT
            | DIALECT
            | EXCLUSION
            | PROJECT_DIRECTIVE

CONFIG_FILE ::= { DIRECTIVE } EOF_OF_FILE
A configuration contains comments (starting with #) and configuration directives. Each directive ideally should be separated by one or more new lines (but this is optional).
Configuration directives are processed sequentially. I.e. if two directives conflict, then the latest one takes precedence.

Values

Directives often contain values, typeset in all-caps. This section defines the syntax for each value.

BOOLEAN

Grammar:
BOOLEAN ::= false | true | 0 | 1
Note the numeric form is here for backwards-compatibility. It is recommended to use the false and true literals instead.

STRING

Grammar:
STRING ::= <double-quoted-string>

INTEGER

Grammar:
INTEGER ::= <non-negative-integer>

Directives

Project Root

Grammar:
PROJECT_ROOT ::= project_root
This directive means we do not inherit configuration from our parent directory, but instead we start from a clean (default) slate. It is recommended to place this directive in the top level of your project.

Global enable/disable

Grammar:
GLOBAL_ENABLE ::= enable ':' BOOLEAN 
This directive completely enables or disables all miss_hit tools. This is especially useful if you have a large legacy code-base that you're slowly transitioning. In this use-case you have enable: false in your project root, and individual components overwrite it again with enable: true on a case-by-case basis.

Style rule application

Grammar:
STYLE_APPLICATION ::= enable_rule ':' STRING
                    | suppress_rule ':' STRING
This directive turns style rules on or off. The given string must refer to a valid style rule.
Please note that any configuration associatd with a style rule is not affected by this directive. For example you can set line_length and then turn the corresponding rule on and off without having to re-specify the acceptable line length.

Style configuration application

Grammar:
STYLE_CONFIGURATION ::= IDENTIFIER ':' STRING
                      | IDENTIFIER ':' INTEGER
                      | IDENTIFIER ':' BOOLEAN
This directive configures a particular style rule. The identifier must refer to a valid rule parameter.

Metric configuration

Grammar:
METRIC_NAME ::= STRING
METRIC_OR_WC ::= METRIC_NAME | '*'
METRIC_LIMIT ::= metric METRIC_OR_WC ':' report
               | metric METRIC_OR_WC ':' ignore
               | metric METRIC_NAME  ':' limit INTEGER
This directive configures a particular code metric. The metric name must refer to a valid code metric. Using the wild-card means this directive applies to all metrics MISS_HIT supports.
When report is specified, a metric is measured and included in the final report, but no limits are checked or enforced.
When ignore is specified, a metric is not measured and not included in the final report. Use this to completely remove metrics you don't care about.
When limit is specified, the measured metric must be less than or equal to the given number; otherwise a message is generated by mh_metric. (But these can be justified by special comments in the code, please refer to the mh_metric manual.)

Language dialect

Grammar:
DIALECT ::= octave ':' STRING
          | matlab ':' STRING
This directive controls which language is processed by MISS_HIT. By default we process MATLAB (latest). Note that specifying the dialect option more than once just means the most recent one takes effect, which is the same for any other option.
The MATLAB version string can be "latest" or a YEAR[ab] string, such as "2017b". The earliest MATLAB supported is 2017b. The latest MATLAB supported is 2022a.
The OCTAVE version string can be "latest" or a MAJOR.MINOR string, such as "4.4". The earliest Octave supported is 4.2. The latest Octave supported is 7.2. Please note that Octave support is extremely limited right now, but I consider it a medium/long term priority. Many features are missing, such as the specific end keywords, the treatment of strings, default arguments, increments, unwind protect, etc.

Directory exclusion

Grammar:
EXCLUSION_KIND ::= exclude_dir | ignore_dir
EXCLUSION      ::= EXCLUSION_KIND ':' STRING
This directive completely removes a directory from consideration, and makes them project roots. The exclude_dir directive raises an error if the excluded directory does not exist, the ignore_dir does not. It is recommended to use exclude_dir whenever possible, since that way you notice if you rename or move a directory without updating the configuration files.
MISS_HIT will not enter such directories, unless explicitly demanded. For example, consider this directory structure:
root
  / miss_hit.cfg (which excludes kitten)
  / potato
      / foo.m
      / bar.m
  / kitten
      / baz.m
Assume the user is in root.
$ mh_style .
This command will analyse two files, foo.m and bar.m.
$ mh_style kitten/baz.m
This command will analyse one file, baz.m. Furthermore, because kitten is considered a project root, any configuration from root is entirely ignored.
$ mh_style *
Using the * wildcard will have a surprising effect: it will expand to potato and kitten, so miss_hit will analyze all files in this case, since you've explicitly requested the traversal of kitten.

Projects

Grammar:
PROJECT_DIRECTIVE ::= LIBRARY_DECLARATION
                    | ENTRYPOINT_DECLARATION

LIBRARY_DECLARATION ::= [global] library [ STRING ] LIBRARY_PROPERTIES
LIBRARY_PROPERTIES ::= '{' [ LIBRARY_PROPERTY { ',' LIBRARY_PROPERTY } ] '}

ENTRYPOINT_DECLARATION ::= entrypoint STRING ENTRYPOINT_PROPERTIES
ENTRYPOINT_PROPERTIES ::= '{' [ ENTRYPOINT_PROPERTY { ',' ENTRYPOINT_PROPERTY } ] '}'

OPTIONAL_STRING_LIST ::= [ STRING_LIST ]
STRING_LIST ::= STRING { ',' STRING }

PATH_LIST ::= paths '{' OPTIONAL_STRING_LIST '}

TEST_LIST ::= tests '{' OPTIONAL_STRING_LIST '}

LIBRARY_PROPERTY ::= PATH_LIST | TEST_LIST

ENTRYPOINT_PROPERTY ::= libraries '{' OPTIONAL_STRING_LIST '}'
                      | PATH_LIST
                      | TEST_LIST
A library declaration defines (an optionally named) library, which is essentially a set of paths that will be searched for functions and classes. If no name is given, the name of the library defaults to the directory name. By default only the directory containing the configuration directive is searched, but this can be overridden by a path list. The items in the path list may contain wild-cards.
A global library is automatically a dependency for all libraries and entry points. This is useful for projects with code shared between all components (e.g. interfaces definitions).
An entrypoint declares a program or system, which may depend on a list of libraries.

Future plans

In the future an entry point will also be able to name a specific function or member function that serves as the main function for this system, allowing static analysis to detect e.g. unused code.