A Guile build script for Lilypond (and other) projects
Go to file
contrapunctus 284174d375 Fix output directory name when no VCS detected 2023-03-04 23:43:24 +05:30
.gitignore Merge branch 'development' into production 2021-03-29 12:00:34 +05:30
CHANGELOG.org Add changelog 2021-03-29 12:09:28 +05:30
README.org Demote license information 2021-03-29 12:09:54 +05:30
UNLICENSE Add license 2020-11-30 11:14:09 +05:30
mkly Fix output directory name when no VCS detected 2023-03-04 23:43:24 +05:30
mkly-tests.scm [mkly] remove `else` rule 2021-03-29 02:26:51 +05:30

README.org

mkly

README

Features

  1. Provides a default set of rules, so you don't have to.
  2. Provides a full-blown programming language for writing rules.
  3. Tiny (< 150 lines); trivial to hack on.
  4. Implemented and configured using the only reasonable language (a Lisp 😏)

Non-goals

  1. Incremental builds

Installation

Guile must be available at /usr/bin/guile

  1. Place mkly in your $PATH or your project root
  2. Make it executable -

    chmod u+x mkly
    
  3. In your project root, run -

    ./mkly
    

Invocation and default rules

mkly can be run without arguments -

mkly

This will compile main.ly and part-*.ly.

You can pass one or more targets -

mkly <target>*

Each target will be checked against the patterns defined in the rules, and the actions for all matching patterns will be run.

The default targets are

  1. main.ly - compiles the file without point-and-click. Emits a file to output/<project-name>.
  2. a file name matching part-*.ly, e.g. part-foo.ly - compiles the file without point-and-click. Emits a file to output/<project-name>-part-foo.
  3. dev - compiles main.ly with point-and-click enabled. Emits a file to output/<project-name>-pacON.
  4. all - compiles main.ly and all part-*.ly files, without point-and-click.

If a subdirectory is supplied, mkly will descend to it for all following targets (until another subdirectory on the command line is encountered) -

mkly main foo/ dev bar/ main dev

Here, target "main" will be run in the project root, target "dev" will be run in the subdirectory "foo/", and target "main" and "dev" will be run in subdirectory "bar/".

Defining rules

Rules are returned as a list by the procedure rules. This procedure can be redefined by loading your own Scheme file, by running mkly with the --load / -l command line option.

Some default variables are defined for use in defining rules -

  1. project-root - the project root directory
  2. project-name - by default, the basename of the parent directory
  3. shell-path - path to the shell you want to use for running your commands

Built-in helper functions

  1. (use-dir! DIR) - creates DIR if it does not exist. DIR must be a string. Raises an error if DIR exists and is not a directory. Returns DIR.
  2. (vcs-current-branch)
  3. (parent PATH) - returns the parent directory component of PATH, or #f if none is present.
  4. (getcwd-base) - returns the basename of the current directory.

TODO

Certain [50%]

  1. Make it declarative - define options and their effect, and the structure of the command, separately from the code that makes it happen.
  2. Use regexps to define target names and how the output file name(s) derive from them.
  3. Name files differently if PAC is on - the upside of this is, you don't accidentally send someone the point-and-click-enabled version of the file. The downside is, it's easy to have file-pacON.pdf open during editing, while the script is actually compiling to file.pdf (or vice-versa) 1, and wonder why your score isn't updating.
  4. Create output directory if it doesn't exist!
  5. Remove duplicate layer of CLI options - currently there's a needless extra layer of CLI options for options which already exist. Should make code simpler, UI familiar.

    • But the Lilypond CLI is ugly - what's better, "pac=off", or "-dno-point-and-click"? :\

      • After the revamp, the script doesn't (yet) aim to want to support options like this; the idea is that the user specifies a situation as a target, and all options relevant to that situation are passed (as specified in the rules by the user). It's a "maybe" to-do.
  6. Tab completion of specified target names
  7. Multiple targets in one command e.g. to compile both the main score and the parts in one command.
  8. Targets containing other targets.

    • I've tried calling the script itself with the required targets - not sure if that actually works. Especially considering that #7 - multiple targets in one command - isn't implemented yet.
    • If mkly is not in the user's $PATH and is invoked as ./mkly, the shell won't be able to find it if it calls itself. We can try constructing the path to the script (using getcwd - if (first (command-line)) is "./mkly", it's in the current working directory)
  9. Shell globs, both in targets and in actions.

    • Document shell-path as a user variable
    • Use shell-path to run commands
  10. Sub-project operations

    1. Specify targets for sub*-project(s), instead of project in current working directory.

      • The current way is somewhat inelegant, though - you either modify all rules to also work in sub-projects, or you make new rules for sub-projects…why can't the same rule work for both? What if we could specify a subdirectory, followed by the usual targets, resulting in us cd ing into that directory and running the usual targets? Same rules working in both situations!
    2. Use sub-project-specific mkly-rules.scm if present, else use project-root mkly-rules.scm
    3. I want to compile a particular target in every subproject. (use globs)

    (Old) implementation notes

    1. Maybe…if the input file in a target entry is just a "/" (or maybe "->"?), the target name will be understood as being the target to run in either

      1. all subdirectories directly below the directory containing the current build.scm (in this case, it is an error if any of these subdirectories do not contain a build.scm)
      2. all subdirectories in which a build.scm can be found, directly below the directory containg the current build.scm
  11. Expand branch detection to include more VCSs.
  12. Add -h/help, and -d/debug or -v/verbose
  13. Warn users about ignored mkly-rules.scm, if detected
  14. Add command line option to load a Scheme file, serving as an extension point (e.g. to define custom rules)

Maybe [0%]

  1. Allow users to define (command-line) options and their effect.
  2. VCS branch integration - check what branch you're on. If it's branch X (e.g. "main"), do nothing. If it's something else, add the name to the output file.

    • Or to the output directory name, e.g. files from the main branch go to "output/", files from branch "foo" go to "output-foo/", etc.
  3. Default rules for orchestral projects - for target part-<instrument>.ly, compile <instrument class>/<instrument>.ly
  4. Make rules into a macro
  5. Implement Scheme expressions as an action.

    • one possible way - all actions are Scheme expressions - for shell commands, create a ($ ...) form (with $ doing what run does now) which is eval-uated if it matches a target. (see branch "$-syntax")
  6. Replace regexp patterns with glob patterns?

    • more consistent (since targets and actions use globs too), but possibly less powerful.

Bugs

  1. Barfs when placed in and run from directories with a path containing non-ASCII characters. This is a Guile bug.
  2. ./mkly -> sh: 1: main.ly: not found

    • Default rule (no pattern provided) and else seem to conflict - do we want to do different things when there's no pattern provided and when a provided pattern does not match, or the same thing?
    • Meta-targets - if the command does not match another rule, run as shell command; otherwise treat as list of targets
  3. chdir to project root before descending into subdir
  4. Error on unmatched pattern

License

I dream of a world where all software is liberated - transparent, trustable, and accessible for anyone to use or improve. But I don't want to make demands or threats (e.g. via legal conditions) to get there.

I'd rather make a request - please do everything you can to help that dream come true. Please Unlicense as much software as you can.

mkly is released under the Unlicense (see file UNLICENSE).


1

These are likely to happen if you have a compile-with-last-command-on-save setup, like in my Emacs.