👟 Update files, fast.
upd
is a command-line utility that automatically determines what files in a
directory need to be updated as resulting from the transformation of other
files, and issues the commands to update them. In that regard it is similar to
the make
utility. upd
focuses on convenience and performance. It can
automatically identify sets of source files based on globbing patterns. It can
automatically track transitive dependencies of a file to be updated.
For example, upd
can be used to manage conveniently the compilation of a C++
project. I will run the process to compile all the *.cpp
source files of a
specified directory, then run the process to link the object files. It will
track the header dependencies of each source files automatically, so that
changes to a header file triggers a new compilation. At the same time, unrelated
header or source changes will not cause a new compilation, making the update as
fast as possible. Finally, upd
take care of creating output directories before
they get generated.
The most convenient way to install upd
is by using either
npm
or yarn
. The utility
is split in two main packages. upd-cli
is the global CLI tool, while upd
can be installed locally and separately for each project. This allows you to
use completely different versions of upd
on different projects easily. This
constrasts with utilities such as make
, which are generally global.
For installing with npm
:
npm install -g @jeanlauliac/upd-cli # only if not installed already on your machine
npm install @jeanlauliac/upd --save-dev
Then you can initialize your project by running the following in the project's root directory:
upd init
This creates a single file .updroot
that is used by upd
to know what is the
root directory of the project. Only files in that directory or in any of its
children directories can be managed and updated by upd
for that particular
project.
To know which files to update and how, upd
needs a document describing the
relationship between files. For example, it can describe that all *.cpp
files
should be compiled into object files. This document is called the manifest, and
should be contained in a file at the root directory of your project, under the
name updfile.json
. The manifest is in the JSON format, that is fairly readable
by humans but tedious to maintain by hand. On the other hand it is easy to
generate from a script. As such it is recommended, but not required, to use
upd-configure
to
generate the manifest.
Here's is an example of manifest that compiles all files matching src/*.cpp
into their respective object files in the output
directory.
{
"command_line_templates": [
{
"binary_path": "clang++",
"arguments": [
{
"literals": ["-c", "-o"],
"variables": ["output_file"]
},
{
"literals": ["-I", "/usr/local/include"],
"variables": ["input_files"]
}
]
}
],
"source_patterns": ["src/(*).cpp"],
"rules": [
{
"command_line_ix": 0,
"inputs": [{"source_ix": 0}],
"output": "output/$1.o"
}
]
}
command_line_templates
describes the commands that compile, transform, link,
etc. files. In that example, we only have 1 command. This command is clang++
,
a C++ compiler. Its arguments are component of several parts. Each part has
literal arguments, and variable arguments. The variables are replaced by the
correct file names when it tries to update a given file. In the example, if we
have a file src/foo.cpp
, then upd
will instantiate the following command
line:
clang++ -c -o output/foo.o -I /usr/local/include src/foo.cpp
source_patterns
describes what are the source files of the project. In this
example, only the C++ source files are considered. upd
crawls the filesystem
to find all the files matching each of the patterns. Each pattern can
specify capture groups using parentheses. For example src/(*).cpp
captures
the basename of the file without extensions. For src/foo.cpp
, it would be
foo
. These capture groups can be refered to later in rules.
rules
describes the relationship from source files to generated files, and
between different generated files. It is possible to build a chain of
generated files. Each rule need to specify the following:
command_line_ix
: this is the index in base zero of one of the command line templates specified earlier.inputs
: this is the indices of the source patterns or the rules that give us this rule input files. In this example, a single source pattern is used as input. We could use a rule by using the fieldrule_ix
instead ofsource_ix
.output
: this is a substitution pattern that indicates the name of the output file for this or these inputs. In that example, it isoutput/$1.o
.$1
refers to the first capture group of the input pattern. It is replaced by the corresponding captured string. For example, if the source pattern issrc/(*).cpp
and we found a filesrc/foo.cpp
, the captured group isfoo
. So, the resulting output file name will beoutput/foo.o
.
To get started on developing upd
:
# install dependencies (alternative: npm install)
# and bootstrap a compiled version of `upd`
yarn
# setup update manifest
./configure
# compile `upd` using itself
bootstrap/upd update dist/upd
# run unit+e2e tests
yarn test