_______________ _____
|_ | ___| _ \_ _|
| | |_ | | | | | |
| | _| | | | | | |
/\__/ / | | |/ / _| |_
\____/\_| |___/ \___/
The build system for people who tolerate batch files and shell scripts for smaller projects. Comfortable like an old shoe.
JFDI scales down to tiny codebases. It is portable build scripting with helper functions -- designed to let you assemble your own build logic so you can just get the thing built. J. F. D. I.
JFDI is trivial to distribute to end users of your code.
See "Why JFDI?" below for a comparison with alternatives.
- Designed to scale to the small-end only
- Not destined to be fast for managing large builds
- Not the author's big idea of a perfect build system
- No definition format to learn, just code in Python with a helpful, optional api
- Just code your build in portable Python using functions designed to make building convenient
- Self-propagating: end-user runs the build script directly to download the jfdi build system
- Standalone win32 exe: Your Windows users don't even need a Python install.
- Portable build scripts that work anywhere Python runs
- Tested and nurtured to life on Linux, Macos and Windows with GCC, Clang and MSVC support
- First class support for non-compiler building (LaTeX, shaders, etc.)
- Build system will always be one small Python file with no third party dependencies.
- Provides a small API to simplify build tasks; much less typing than stock Python libraries
- Generate a self-documented build template to get started with
init
- Automatically swaps dir slashes for easy x-platform scripting
- Used by the author for a handful of small projects for three years and counting
Windows: Download a standalone exe from the Github releases tab and unzip it to your PATH
. Alternatively, run python jfdi.py <args>
with your preinstalled Python 3 interpreter.
Linux and Mac:
wget https://raw.githubusercontent.com/mlabbe/jfdi/master/jfdi.py && \
chmod +x jfdi.py && \
sudo mv jfdi.py /usr/local/bin/jfdi
JFDI has zero third party Python dependencies and is a single file standalone program. The master branch of the official github repo is always stable.
This builds a multi-file C project with clang, putting build products in a bin/
subdirectory.
# generate template build file build.jfdi
jfdi init
# build.jfdi
def start_build():
# set CC, LD, etc. to common clang values
use("clang")
# make subdir if it doesn't exist
mkd("bin")
# list all files to compile.
def list_input_files():
# wildcards are welcome
return ['hello.c', 'main.c', '*.c']
# called once per file that will be compiled
def build_this(in_path):
# given path to source file, get a path to the output .o
# note this would automatically be .obj if msvc was used
obj_path = obj(in_path, "bin")
# return the command to build this file
# exp expands $-based variables
return exp("$CC $CFLAGS -c $in_path -o $obj_path)
# called once at the end of a build
def end_build(in_files):
# given a list of all input source files, get a list of
# all obj files
objs = obj(in_files, "bin")
# cmd executes a command line program which must succeed to continue
cmd(exp("$LD $LDFLAGS $objs -o bin/hello"))
# called when the user uses jfdi clean
def clean(in_files):
# rm deletes a directory or file
rm("bin")
jfdi # builds build.jfdi
See examples for more use cases.
jfdi init
creates a build file which contains documentation in comments.- See examples for specific usage examples.
- Questions or suggestions? Post an issue on the official Github repo.
- Read the code to
jfdi.py
's _api_* functions. It is not deeply nested and easy to read. Try it out!
jfdi init # create build.jfdi in cwd
emacs build.jfdi # edit self-documenting build script
jfdi # run build.jfdi in cwd, building your program
jfdi DEBUG=1 # pass build variable DEBUG to build script, yes('DEBUG') returns True
jfdi clean DEBUG=1 # call build.jfdi clean() which cleans up the build
jfdi run # build normally, then call run(), which performs a canonical run
# of the build product
See also: examples
Changes are described in CHANGELOG.md.
JFDI is meant for tiny projects. It does not have a dependency graph and it does not support incremental building. If you desire this, your project is not tiny and you should use something else.
On Windows, GNU Make brings in a Unix runtime (via Cygwin, MSYS2, etc.) which can take up a gigabyte. Furthermore, it handles fork() poorly and whether it uses Unix paths is install-dependent. The official GNU Make binary is over a decade old and does not work on modern Windows.
GNU Make is, arguably, overkill for smaller projects.
JFDI is a standalone script or executable capable of performing shell-like commands portably. Thanks to Python's extensive standard library, this includes things like recursively creating directories and zipping up files.
Batch files only run on Windows but JFDI runs on Linux, Mac and Windows. They are fiddly to write and don't let you easily do things like subsitute one file extension for another. (in.c
builds in.obj
, for instance).
JFDI offers a compact build-specific API. You will type significantly less to get the same thing built.
Bash scripts make you use Unix paths on Windows, whereas you often call programs that use Windows paths. Mixed path scripting is gross. Bash also depends on cp, rm, chmod, chown and a battery of Unix commands to be available. Getting a compliant Unix environment up and running is asking a Windows user to install around a gigabyte of exes.
If you use bash files you have to explicitly check every command for errorlevel and exit on failure. JFDI implicitly assumes your build fails when the compiler returns errors.
JFDI offers a compact build-specific API. You will type significantly less to get the same thing built.
SLN files integrate with Visual Studio which has a debugger, so you should use that if you value that closely knit integration.
SLN files are mostly only forwards compatible. JFDI lets people with earlier Visual Studio versions than you compile your code.
SLN files will never build on Linux or Macos, but JFDI does. See the multi-compiler example.
SLN files are unspeakably inappropriate for tasks outside of traditional compiling like building a LaTeX book. JFDI is a better fit for non-code compilation building.
SCons is a fully featured Python-based software construction tool. It must be installed on your operating system. In contrast, JFDI is a standalone exe or script that can be easily downloaded or distributed.
Scons, unlike JFDI, has deep knowledge about various compilers and imposes conventions through its environment model. Modifying it to do something it previously did not is often not worth the effort.
This is a blessing and a curse: if SCons knows about your build environment, you are in luck and can build quickly. If it does not, adding support for it is involved and poorly documented. In contrast, JFDI barely possesses any conventions that enforce a toolchain, expecting you to expressly support it using a very simple API.
SCons is not widely installed. Therefore, if you distribute your software, you are asking all of your users to install it and an outdated version of Python on their machines just to build your small program. JFDI is also not widely installed, but it self propagates and does not need to be installed at the operating system level. A user simply needs to run the build.jfdi
script directly to retrieve the latest version.
By design, JFDI has no dependency graph. Every file in your project is re-processed when the build script is re-run.
This software has been in use for three years on the author's small projects. The issues on Github consist of all the known issues.
Copyright © 2016-2024 Frogtoss Games, Inc. File LICENSE covers all files in this repo.
JFDI by Michael Labbe. contact@frogtoss.com
Directed support for this work is available from the original author under a paid agreement.