37 releases (18 breaking)

0.19.1 Oct 26, 2024
0.18.3 Sep 5, 2024
0.16.5 Jul 3, 2024
0.13.1 Jan 27, 2024
0.11.0 Nov 20, 2023

#47 in Build Utils

Download history 230/week @ 2024-07-29 196/week @ 2024-08-05 113/week @ 2024-08-12 275/week @ 2024-08-19 6/week @ 2024-08-26 137/week @ 2024-09-02 148/week @ 2024-09-16 67/week @ 2024-09-23 7/week @ 2024-09-30 191/week @ 2024-10-21 31/week @ 2024-10-28 12/week @ 2024-11-04

234 downloads per month

MIT license

37KB
593 lines

About

Build automation tool

  • Inspired by make
  • No automatic targets[^one]
  • Minimalist functionality; maximalist readability
  • Configuration is one or more simple Markdown files (Makefile.md by default)
  • Output is colorized Markdown (unless redirected or piped)
  • Processes the target(s) specified or if none, processes the first target
  • Commands run independently, in script mode, or via a custom command
  • If any command fails (exits with a non-zero code), processing halts immediately (if not using a custom shell that does not provide this functionality)
  • Verbosity levels:
    • -v: add -x to bash command in script mode
    • -vv: print up to date targets
    • -vvv: show configuration
  • Generates a default Makefile.md for a Rust project via -g rust
  • Lists targets via -l; if target(s) is specified, list hierarchical dependencies
  • Processes targets and dependencies in the order specified
  • Designed to work flexibly with other shells, scripting languages, and utilities like dotenv

Syntax

Input

  • A level 1 heading begins the definition of a target.
  • A plain text target name is a "phony" target and always runs.[^two]
  • A code span target name is a file target and will only run if (a) any dependency file target's modification time is newer than the file target's, (b) the file target does not exist and has a recipe, or (c) force processing (-B) is enabled.[^two]
  • An unordered list item defines a target dependency.
  • A plain text dependency name is a phony dependency and will run if the target runs.
  • A code span dependency name is a file dependency, which either has an associated target or not. If not, it is interpreted as a file glob matching existing files, which enables a target to easily depend on any files matching the glob, for instance, the build target may depend on **/*.rs, meaning any *.rs file under ./.
  • A code block is a recipe and contains the commands that are run when the target is processed.
  • Recipe commands run independently via sh -c by default, via bash -eo pipefail if script mode (-s) is enabled, via bash -xeo pipefail if script mode and verbose level 1 or greater (-sv) are enabled, or by the command given in the code block info string
  • Commands may use the following variables:
    • {0}: first dependency
    • {target}: target name
    • {dirname}: directory name

See Makefile.md, styles/Makefile.rust.md and/or the -g option for examples.

Output

  • A level 2 heading is the output section: "Configuration", "Target(s)".

  • A Level 3 heading in the Target(s) section is each target, either as plain text "phony" target or a code span file target.

  • Code blocks:

    Script Mode Dry Run Description
      Each command and output
      Each command
    Each script
    Each script and output (in separate code block)

Usage

$ mkrs -V
mkrs 0.19.1
$ mkrs -h
Build automation tool

Usage: mkrs [OPTIONS] [NAME]...

Arguments:
  [NAME]...  Target(s)

Options:
  -l                   List targets/dependencies
  -B                   Force processing
  -n                   Dry run
  -s                   Script mode
  -v...                Verbose
  -q                   Quiet
  -C <PATH>            Change directory
  -f <PATH>            Configuration file(s) [default: Makefile.md]
  -g <STYLE>           Generate Makefile.md content [styles: rust]
      --color <COLOR>  Force enable/disable terminal colors [default: auto]
                       [possible values: auto, always, never]
  -r                   Print readme
  -h, --help           Print help
  -V, --version        Print version

Examples

List available targets

$ mkrs -l
* all
* check
* update
* run
* clippy
* test
* build
* `target/release/mkrs`
* `README.md`
* doc
* outdated
* audit
* update-toml
* update-lock
* install
* uninstall
* install-deps
* clean
* cocomo
* commit
* publish
* full
* fail
* `nonexistent`
* custom

List dependencies for full target

$ mkrs -l full
* full
    * update
        * update-toml
        * update-lock
    * check
        * outdated
        * audit
    * all
        * clippy
            * `Cargo.lock`
            * `Cargo.toml`
            * `src/main.rs`
        * test
            * `Cargo.lock`
            * `Cargo.toml`
            * `src/main.rs`
        * build
            * `target/release/mkrs`
                * `Cargo.lock`
                * `Cargo.toml`
                * `src/main.rs`
                * `README.md`
                    * `t/README.md`
                    * `Cargo.toml`
                    * `CHANGELOG.md`
                    * `src/main.rs`
        * doc
    * install
        * `README.md`
            * `t/README.md`
            * `Cargo.toml`
            * `CHANGELOG.md`
            * `src/main.rs`

Dry run

$ mkrs -n
# `target/release/mkrs`

```text
cargo build --release
```

# clippy

```text
cargo clippy -- -D clippy::all
```

# test

```text
cargo test
```

# doc

```text
cargo doc
```

Process default target

$ mkrs
# `target/release/mkrs`

```text
$ cargo build --release
   Compiling mkrs v0.19.1 (/home/nick/github.com/qtfkwk/mkrs)
    Finished `release` profile [optimized] target(s) in 1.74s
```

# clippy

```text
$ cargo clippy -- -D clippy::all
    Checking mkrs v0.19.1 (/home/nick/github.com/qtfkwk/mkrs)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.55s
```

# test

```text
$ cargo test
   Compiling mkrs v0.19.1 (/home/nick/github.com/qtfkwk/mkrs)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.57s
     Running unittests src/main.rs (target/debug/deps/mkrs-fa95270962d9f1ab)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

```

# doc

```text
$ cargo doc
 Documenting mkrs v0.19.1 (/home/nick/github.com/qtfkwk/mkrs)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.49s
   Generated /home/nick/github.com/qtfkwk/mkrs/target/doc/mkrs/index.html
```

Process check target

$ mkrs check
# outdated

```text
$ cargo outdated --exit-code=1
All dependencies are up to date, yay!
```

# audit

```text
$ cargo audit
    Fetching advisory database from `https://github.com/RustSec/advisory-db.git`
      Loaded 664 security advisories (from /home/nick/.cargo/advisory-db)
    Updating crates.io index
    Scanning Cargo.lock for vulnerabilities (117 crate dependencies)
```

Process update, check, and build targets

$ mkrs update check build
# update-toml

```text
$ cargo upgrade -i
    Checking mkrs's dependencies
note: Re-run with `--verbose` to show more dependencies
  latest: 13 packages
```

# update-lock

```text
$ cargo update
    Updating crates.io index
     Locking 0 packages to latest compatible versions
note: pass `--verbose` to see 20 unchanged dependencies behind latest
```

# outdated

```text
$ cargo outdated --exit-code=1
All dependencies are up to date, yay!
```

# audit

```text
$ cargo audit
    Fetching advisory database from `https://github.com/RustSec/advisory-db.git`
      Loaded 664 security advisories (from /home/nick/.cargo/advisory-db)
    Updating crates.io index
    Scanning Cargo.lock for vulnerabilities (117 crate dependencies)
```

# `target/release/mkrs`

```text
$ cargo build --release
   Compiling mkrs v0.19.1 (/home/nick/github.com/qtfkwk/mkrs)
    Finished `release` profile [optimized] target(s) in 1.97s
```

Generate a default Makefile.md for a Rust project

$ mkrs -g rust
# all

* clippy
* test
* build
* doc

# check

* outdated
* audit

# update

* update-toml
* update-lock

# run

* `target/release/{dirname}`

```
target/release/{dirname}
```

# clippy

* `Cargo.lock`
* `Cargo.toml`
* `**/*.rs`

```
cargo clippy -- -D clippy::all
```

# test

* `Cargo.lock`
* `Cargo.toml`
* `**/*.rs`

```
cargo test
```

# bench

```
cargo bench -q 2>&1 |tee benches/report.txt
```

# build

* `target/release/{dirname}`

# `target/release/{dirname}`

* `Cargo.lock`
* `Cargo.toml`
* `**/*.rs`
* `README.md`

```
cargo build --release
```

# `README.md`

* `t/README.md`
* `Cargo.toml`
* `CHANGELOG.md`
* `**/*.rs`

```
cargo build --release
kapow {0} >{target}
```

# doc

```
cargo doc
```

# outdated

```
cargo outdated --exit-code=1
```

# audit

```
cargo audit
```

# update-toml

```
cargo upgrade -i
```

# update-lock

```
cargo update
```

# install

* `README.md`

```
cargo install --path .
```

# uninstall

```
cargo uninstall {dirname}
```

# install-deps

```
cargo install cargo-audit cargo-edit cargo-outdated cocomo dtg kapow tokei toml-cli
```

# scaffold

```bash -eo pipefail
if ! toml get -r Cargo.toml package.description >/dev/null; then
toml set Cargo.toml package.description "Insert a description here" >Cargo.toml.new
mv Cargo.toml.new Cargo.toml
echo Edit package description in Cargo.toml, then rerun \`mkrs scaffold\`.
exit 0
fi
mkdir -p t
if [ ! -e t/README.md ]; then
NAME=$(toml get -r Cargo.toml package.name)
ABOUT=$(toml get -r Cargo.toml package.description)
cat <<EOF >t/README.md
# About

$ABOUT

# Usage

~~~~text
\$ $NAME -V
!run:../target/release/$NAME -V 2>&1
\$ $NAME -h
!run:../target/release/$NAME -h 2>&1

!inc:../CHANGELOG.md

EOF fi if [ ! -e CHANGELOG.md ]; then VERSION=$(toml get -r Cargo.toml package.version) TODAY=$(dtg -n %Y-%m-%d) cat <CHANGELOG.md

Changelog

  • $VERSION ($TODAY): Initial release

EOF fi


# clean

cargo clean


# cocomo

```bash -eo pipefail
tokei; echo
cocomo -o sloccount
cocomo

commit

set -xeo pipefail
V=$(toml get -r Cargo.toml package.version)
git commit -m "$V"
git tag -a "$V" -m "$V"

publish

cargo publish
git push
git push --tags

full

  • update
  • check
  • all
  • install

**Note:** Save to `Makefile.md` via redirection: `mkrs -g rust >Makefile.md`

## Generate a COCOMO report

~~~text
$ mkrs cocomo
# cocomo

```bash -eo pipefail
tokei; echo
cocomo -o sloccount
cocomo
```

```
text
===============================================================================
 Language            Files        Lines         Code     Comments       Blanks
===============================================================================
 TOML                    1           24           22            0            2
-------------------------------------------------------------------------------
 Markdown                5         1064            0          779          285
 |- BASH                 3          112           90            6           16
 |- Python               1            1            1            0            0
 (Total)                           1177           91          785          301
-------------------------------------------------------------------------------
 Rust                    1          698          593           29           76
 |- Markdown             1           12            0           12            0
 (Total)                            710          593           41           76
===============================================================================
 Total                   7         1786          615          808          363
===============================================================================

Total Physical Source Lines of Code (SLOC)                    = 615
Development Effort Estimate, Person-Years (Person-Months)     = 0.12 (1.44)
  (Basic COCOMO model, Person-Months = 2.40*(KSLOC**1.05)*1.00)
Schedule Estimate, Years (Months)                             = 0.24 (2.87)
  (Basic COCOMO model, Months = 2.50*(person-months**0.38))
Estimated Average Number of Developers (Effort/Schedule)      = 0.50
Total Estimated Cost to Develop                               = $16,217
  (average salary = $56,286/year, overhead = 2.40)

Description                | Value
---------------------------|---------------------------------
Total Source Lines of Code | 615
Estimated Cost to Develop  | $16,216.63
Estimated Schedule Effort  | 2.87 months
Estimated People Required  | 0.50

```

Use a custom shell program

$ mkrs custom
# custom

```python
print("This is a custom recipe in Python.")
```

```
text
This is a custom recipe in Python.
```

Use with a .env file via dotenv

  1. Install dotenv: cargo install dotenv.
  2. Create a .env file with environment variables.
  3. Prepend command(s) in your Makefile.md recipes with dotenv .
  4. Run the mkrs command.

Changelog

See CHANGELOG.md in the repository.

[^one]: Unlike make, mkrs does not have any built-in knowledge about how to compile any sort of file; all such commands must be defined in the configuration file.

[^two]: A target of either sort is only processed if it is a dependency of the target(s) that are being processed.

Dependencies

~12–24MB
~383K SLoC