A build tool for Standard ML
Symbol is a build tool for Standard ML. It's designed to work alongside and on top of existing SML build tools, like SML/NJ's CM and MLton's MLBasis files.
Here's a quick screencast to show off its features:
Symbol's key features are:
-
Designed to build executables
Symbol builds and installs runnable executables for your project. With SML/NJ, these executables use heap images under the hood, while MLton generates linked binaries. These details are internal; the end result is always an executable that you can invoke from the command line.
-
Low friction for collaborators
Symbol's executable is designed to be checked into your project's version control. This means your collaborators do not have to install Symbol globally before they can build your project.
-
Built on
make
As much as possible, Symbol uses
make
to cache previous build steps. Runningsymbol make
a second time should usually be instant. All intermediate outputs are stored into a.symbol-work
folder in your current directory, and it's always safe to delete this folder. -
Scaffold new projects
The
symbol new
command is how you initialize new Standard ML projects. It prepopulates CM and MLBasis files and copies the Symbol scripts into your project, so you can jump write into your project. -
Convention over configuration
Symbol infers information about your project from its structure. This means that Symbol does not come with yet another file format to configure your project; just keep using
*.cm
files for SML/NJ and*.mlb
files for MLton.
Installing Symbol globally is only required by the collaborators who
initially set up new SML projects. The global command is called symbol-new
and
is used solely for scaffolding new projects. Once a Symbol project has been
created, a script called symbol
will exist locally within that project.
On macOS, you can use Homebrew:
brew install jez/formulae/symbol-new
You can also install from source:
git clone https://github.com/jez/symbol
cd symbol
make install
This will install to ~/.local/bin/symbol-new
. If you'd like to install to
a different prefix, you can pass that to the make
command. For example,
make install prefix=/usr/local
will instead install to /usr/local/bin/symbol-new
.
# Given `<target>` is the name to call the executable for your project:
symbol-new <target>
# or, to create a new project with minimal scaffolding:
symbol-new <target> --empty
# ^ This also works to initialize an existing project.
# Build the project (by default uses SML/NJ)
./symbol make
# Build the project with MLton (takes longer, but much faster executable)
./symbol make with=mlton
# Build the project (by default uses MLton), then install to ~/.local/bin
./symbol install
# same as above, but installs to /usr/local/bin
./symbol install prefix=/usr/local
# Global
symbol-new --help
# Local
./symbol --help
These are some general tips for working with Symbol and Standard ML.
Symbol adds an initial project structure with a src/
folder. Feel free to
arrange the SML source files however you want, and pull in whatever dependencies
you want. However:
-
There must always be either a
<target>.cm
or<target>.mlb
in the same folder as thesymbol
script. -
The
<target>.cm
must make available aMain.main
function.- This function is given directly to the
ml-build
command. - See the CM User Guide for more information.
- This function is given directly to the
-
The choice for
<target>
cannot easily be changed after runningsymbol-new
.- If you do want to change it, you'll have to manually edit the
target
variables in./symbol
and./.symbol.mk
- If you do want to change it, you'll have to manually edit the
And then a suggestion: keep your <target>.cm
and <target>.mlb
files very
transparent. Don't try to hide intermediate signatures, structures, or functors.
This will make it easier to test your code.
If you intend your project to produce both an executable and a library, have a separate CM file that hides internal implementation details from downstream users of your library, and keep the one that exposes everything.
There are a few folders you might want to add or make sure are in your PATH. To add all of the below folders to your PATH, add these lines to your bashrc or zshrc:
export PATH="$PATH:.symbol-work/bin"
export PATH="$PATH:."
export PATH="$PATH:$HOME/.local/bin"
The folders themselves are:
-
.symbol-work/bin
If this folder is in your PATH, you can replace
❯ .symbol-work/bin/my-target
with just
❯ my-target
-
.
(the current directory)If this folder is in your PATH, you can replace
❯ ./symbol make
with just
❯ symbol make
-
$HOME/.local/bin
This is the place where
./symbol install
puts executables by default. If this folder is not in your PATH, you can useprefix=...
to specify a folder that is on your PATH. For example:./symbol install prefix=/usr/local
To learn about historical context and implementation decisions:
- Read DECISIONS.md
To set up your development environment:
# macOS:
brew bundle
# linux: install the packages listed in ./Brewfile
For developing on Symbol locally:
# To run the lint checks:
make lint
# To run the tests:
make test
# To re-record the snapshot tests:
make test update=1
To make a new test:
- Add a
.sh
file anywhere intests/
. - Make it executable.
To make a new snapshot test:
- Make a new test, say
tests/foo.sh
- Make an empty file like
tests/foo.sh.exp
- Run
make test update=1
To bump the version:
- Update the VERSION in
symbol-new
- Update the version test output (
version.sh.exp
) - Commit the change, and create a new git tag with that version
- Make a new release on GitHub (
hub release
) - Update the Homebrew formula