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.
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:
-
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"
-
Turn MISS_HIT on again in selected sub-directories. As
time progresses, gradually enable for more and more
sub-directories.
enable: true
- 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_bThen 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
Configuration file Reference
Naming
A configuration file must have one of the following names:
- miss_hit.cfg
- .miss_hit
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.mThis 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.