0.0 Using A Pre-compiled Binary
Pre-compiled binaries are available for recent releases. Just download the appropriate executable for your platform and put it on your $PATH. To uninstall, simply delete the executable. To upgrade from an older release, just overwrite the older binary with the newer one.
For details about how those binaries are built, see the Release Build How-To wiki page.
0.1 Executive Summary
Building and installing is very simple. Three steps:
- Download and unpack a source tarball or ZIP.
- ./configure; make
- Move the resulting "fossil" or "fossil.exe" executable to someplace on your $PATH.
1.0 Obtaining The Source Code
Fossil is self-hosting, so you can obtain a ZIP archive or tarball containing a snapshot of the latest version directly from Fossil's own fossil repository. Additionally, source archives of released versions of fossil are available from the downloads page. To obtain a development version of fossil, follow these steps:
Point your web browser to https://fossil-scm.org/
Click on the Timeline link at the top of the page.
Select a version of of Fossil you want to download. The latest version on the trunk branch is usually a good choice. Click on its link.
Finally, click on one of the "Zip Archive" or "Tarball" links, according to your preference. These link will build a ZIP archive or a gzip-compressed tarball of the complete source code and download it to your computer.
Aside: Is it really safe to use an unreleased development version of the Fossil source code?
Yes! Any check-in on the trunk branch of the Fossil Fossil self-hosting repository will work fine. (Dodgy code is always on a branch.) In the unlikely event that you pick a version with a serious bug, it still won't clobber your files. Fossil uses several self-checks prior to committing any repository change that prevent loss-of-work due to bugs.
The Fossil self-hosting repositories, especially the one at http://fossil-scm.org/home, usually run a version of trunk that is less than a week or two old. Look at the bottom left-hand corner of this screen (to the right of "This page was generated in...") to see exactly which version of Fossil is rendering this page. It is always safe to use whatever version of the Fossil code you find running on the main Fossil website.
2.0 Compiling
-
Unpack the ZIP or tarball you downloaded then cd into the directory created.
(Optional, Debian-compatible Linux only) Make sure you have all the necessary tools and libraries at hand by running: sudo apt install tcl-dev tk libssl-dev zlib1g-dev.
(Optional, Unix only) Run ./configure to construct a makefile.
The build system for Fossil on Unix-like systems assumes that the OpenSSL development and runtime files are available on your system, because unprotected repositories are trivial to attack otherwise. Indeed, some public Fossil repositories — including Fossil's own — today run in an HTTPS-only mode, so that you can't even do an anonymous clone from them without using the TLS features added to Fossil by OpenSSL. To weaken that stance could allow a man in the middle attack, such as one that substitutes malicious code into your Fossil repository clone.
You can force the Fossil build system to avoid searching for, building against, and linking to the OpenSSL library by passing --with-openssl=none to the configure script.
If you do not have the OpenSSL development libraries on your system, we recommend that you install them, typically via your OS's package manager. The Fossil build system goes to a lot of effort to seek these out wherever they may be found, so that is typically all you need to do.
For more advanced use cases, see the OpenSSL discussion in the "TLS and Fossil" document.
To build a statically linked binary (suitable for use inside a chroot jail) add the --static option. (See the Docker section below.)
To enable the native Tcl integration feature feature, add the --with-tcl=1 and --with-tcl-private-stubs=1 options.
Other configuration options can be seen by running ./configure --help
Run "make" to build the "fossil" or "fossil.exe" executable. The details depend on your platform and compiler.
Unix → the configure-generated Makefile should work on all Unix and Unix-like systems. Simply type "make".
Unix without running "configure" → if you prefer to avoid running configure, you can also use: make -f Makefile.classic. You may want to make minor edits to Makefile.classic to configure the build for your system.
MinGW / MinGW-w64 → The best-supported path is to build via the MinGW specific Makefile under a POSIX build of GNU make: "make -f win/Makefile.mingw".
There is limited support for building under MinGW's native Windows port of GNU Make instead by defining the USE_WINDOWS=1 variable, but it's better to build under MSYS, Cygwin, or WSL on Windows since this mode doesn't take care of cases such as the "openssl" target, which depends on sed. We've gone as far down this path as is practical short of breaking cross-compilation under Linux, macOS, and so forth, as we'd have to do to make everything work under cmd.exe.
Unless you're building under MSYS where commands like "gcc" give MinGW's GCC and not some other version, you will need to make minor edits to win/Makefile.mingw to configure the cross-compilation environment. It should suffice to switch to one of the predefined PREFIX values, causing the build to be done using "x86_64-w64-mingw32-gcc" for example, yielding a 64-bit native Windows binary.
To enable the native Tcl integration feature, use a command line like the following (all on one line):
make -f win/Makefile.mingw FOSSIL_ENABLE_TCL=1 FOSSIL_ENABLE_TCL_STUBS=1 FOSSIL_ENABLE_TCL_PRIVATE_STUBS=1
Alternatively, running ./configure under MSYS should give a suitable top-level Makefile. However, options passed to configure that are not applicable on Windows may cause the configuration or compilation to fail (e.g. fusefs, internal-sqlite, etc).
MSVC → Use the MSVC makefile.
Run all of the following from a "x64 Native Tools Command Prompt".
First change to the "win/" subdirectory ("cd win") then run "nmake /f Makefile.msc".
Alternatively, the batch file "win\buildmsvc.bat" may be used and it will attempt to detect and use the latest installed version of MSVC.
To enable the optional OpenSSL support, first download the official source code for OpenSSL and extract it to an appropriately named "openssl" subdirectory within the local compat directory then make sure that some recent Perl binaries are installed locally, and finally run one of the following commands:nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
To enable the optional native Tcl integration feature, run one of the following commands or add the "FOSSIL_ENABLE_TCL=1" argument to one of the other NMAKE command lines:buildmsvc.bat FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
nmake /f Makefile.msc FOSSIL_ENABLE_TCL=1
buildmsvc.bat FOSSIL_ENABLE_TCL=1
Cygwin → The same as other Unix-like systems. It is recommended to configure using: "configure --disable-internal-sqlite", making sure you have the "libsqlite3-devel" , "zlib-devel" and "openssl-devel" packages installed first.
3.0 Installing
-
The finished binary is named "fossil" (or "fossil.exe" on Windows). Put this binary in a directory that is somewhere on your PATH environment variable. It does not matter where.
-
(Optional:) To uninstall, just delete the binary.
4.0 Additional Considerations
If the makefiles that come with Fossil do not work for you, or for some other reason you want to know how to build Fossil manually, then refer to the Fossil Build Process document which describes in detail what the makefiles do behind the scenes.
The fossil executable is self-contained and stand-alone and usually requires no special libraries or other software to be installed. However, the "--tk" option to the diff command requires that Tcl/Tk be installed on the local machine. You can get Tcl/Tk from ActiveState.
To build on older Macs (circa 2002, MacOS 10.2) edit the Makefile generated by configure to add the following lines:
TCC += -DSQLITE_WITHOUT_ZONEMALLOC TCC += -D_BSD_SOURCE TCC += -DWITHOUT_ICONV TCC += -Dsocketlen_t=int TCC += -DSQLITE_MAX_MMAP_SIZE=0
5.0 Building a Docker Container
Fossil ships a Dockerfile at the top of its source tree which you can build like so:
$ docker build -t fossil .
If the image built successfully, you can create a container from it and test that it runs:
$ docker run --name fossil -p 9999:8080/tcp fossil
This shows us remapping the internal TCP listening port as 9999 on the host. As a rule, there's little point to using the "fossil server --port" feature inside a Docker container. Let it default to 8080 internally, then remap it to wherever you want it on the host instead.
Our stock Dockerfile configures Fossil with the default feature set, so you may wish to modify the Dockerfile to add configuration options, add APK packages to support those options, and so forth. It also strips out all but the default and darkmode skins to save executable space.
There are two convenience targets for you, make container-image and make container-run. The first creates a versioned container image, and the second does that and then launches a fresh container based on that image. You can pass extra arguments to the first command via the Makefile's DBFLAGS variable and to the second with the DRFLAGS variable. To get the custom port setting as in second command above, say:
$ make container-run DRFLAGS='-p 9999:8080/tcp'
Contrast the raw "docker" commands above, which create an unversioned image called fossil:latest and from that a container simply called fossil. The unversioned names are more convenient for interactive use, while the versioned ones are good for CI/CD type applications since they avoid a conflict with past versions; it lets you keep old containers around for quick roll-backs while replacing them with fresh ones.
5.1 Repository Storage Options
If you want the container to serve an existing repository, there are at least two right ways to do it.
The wrong way is to use the Dockerfile COPY command to bake it into the image at build time. It will become one of the image's base layers, so that each time you build a container from that image, the repo gets reset to its build-time state. Worse, restarting the container will do the same thing, since the base image layers are immutable in Docker. This is almost certainly not what you want.
The correct ways put the repo into the container created from the image, not in the image itself.
5.1.1 Storing the Repo Inside the Container
The simplest method is to stop the container if it was running, then say:
$ docker cp /path/to/my-project.fossil fossil:/jail/museum/repo.fossil
$ docker start fossil
$ docker exec fossil chown -R 499 /jail/museum
That copies the local Fossil repo into the container where the server expects to find it, so that the "start" command causes it to serve from that copied-in file instead. Since it lives atop the immutable base layers, it persists as part of the container proper, surviving restarts.
(The same is true of the default mode of operation: the fossil server --create flag initializes a fresh Fossil repo atop the base image.)
Notice that the copy command changes the name of the repository database. The container configuration expects it to be called repo.fossil, which it almost certainly was not out on the host system. This is because there is only one repository inside this container, so we don't have to name it after the project it contains, as is traditional. A generic name lets us hard-code the server start command.
If you skip the "chown" command above and put "http://localhost:9999/" into your browser, expecting to see the copied-in repo's home page, you will get an opaque "Not Found" error. This is because the user and group ID of the file will be that of your local user on the container's host machine, which is unlikely to map to anything in the container's /etc/passwd and /etc/group files, effectively preventing the server from reading the copied-in repository file. 499 is the default "fossil" user ID inside the container, causing Fossil to run with that user's privileges after it enters the chroot. (See below for how to change this default.) You don't have to restart the server after fixing this with chmod: simply reload the browser, and Fossil will try again.
5.1.2 Storing the Repo Outside the Container
The simple storage method above has a problem: Docker containers are designed to be killed off at the slightest cause, rebuilt, and redeployed. If you do that with the repo inside the container, it gets destroyed, too. The solution is to replace the "run" command above with the following:
$ docker create \
--name fossil -p 9999:8080/tcp \
-v museum:/jail/museum fossil
Now when you "docker cp" the local repo into the container, it lands on a separate Docker volume mounted inside it, giving those files an independent lifetime. When you need to rebuild the container or its underlying image — such as to upgrade to a newer version of Fossil — the volume remains behind and gets remapped into the new container when you recreate it by giving the above command again.
5.2 Why Chroot?
A potentially surprising feature of this container is that it runs Fossil as root. Since that causes Fossil's chroot jail feature to kick in, and a Docker container is a type of über-jail already, you may be wondering why we don't either:
- run fossil server --nojail to skip the internal chroot; or
- set "USER fossil" in the Dockerfile so it starts Fossil as that user instead
The reason is, although this container is quite stripped-down by today's standards, it's based on the surprisingly powerful Busybox project. (This author made a living for years in the early 1990s using Unix systems that were less powerful than this container.) If someone ever figured out how to make a Fossil binary execute arbitrary commands on the host or to open up a remote shell, they'd likely be able to island-hop from there into the rest of your network. We need this cute double-jail dance to keep the Fossil instance from accessing the Busybox features.
We deem this risk low since a) it's never happened, that we know of; and b) we've turned off all of the risky features like TH1 docs. Nevertheless, we believe in defense-in-depth.
If you shell into the container and do a ps, you'll find fossil running as root. Why? Because that's the parent process, which may need to do rootly things like listening on port 80 or 443. Fossil's chroot feature takes effect in the child processes forked off to handle each HTTP/CGI hit. This is why you can fix broken permissions with chown after the container is already running, without restarting it: each hit reevaluates the repository file permissions when deciding what user to become when dropping root privileges.
5.3 Extracting a Static Binary
Our 2-stage build process uses Alpine Linux only as a build host. Once we've got everything reduced to the two key static binaries — Fossil and Busybox — we throw all the rest of it away.
A secondary benefit falls out of this process for free: it's arguably the easiest way to build a purely static Fossil binary for Linux. Most modern Linux distros make this surprisingly difficult, but Alpine's back-to-basics nature makes static builds work the way they used to, back in the day. If that's what you're after, you can skip the "run" command above and create a temporary container from the image, then extract the executable from it instead:
$ docker create --name fossil-static-tmp fossil
$ docker cp fossil-static-tmp:/jail/bin/fossil .
$ docker container rm fossil-static-tmp
The resulting binary is the single largest file inside that container, at about 6 MiB. (It's built stripped.)
5.4 Container Build Arguments
5.4.1 Package Versions
You can override the default versions of Fossil and BusyBox that get fetched in the build step. To get the latest-and-greatest of everything, you could say:
$ docker build -t fossil \
--build-arg FSLVER=trunk \
--build-arg BBXVER=master .
(But don't, for reasons we will get to.)
Because the BusyBox configuration file we ship was created with and tested against a specific stable release, that's the version we pull by default. It does try to merge the defaults for any new configuration settings into the stock set, but since it's possible this will fail, we don't blindly update the BusyBox version merely because a new release came out. Someone needs to get around to vetting it against our stock configuration first.
As for Fossil, it defaults to fetching the same version as the checkout you're running the build command from, based on checkin ID. The most common reason to override this is to get a release version:
$ docker build -t fossil \
--build-arg FSLVER=version-2.19 .
It's best to use a specific version number rather than the generic "release" tag because Docker caches downloaded files and tries to reuse them across builds. If you ask for "release" before a new version is tagged and then immediately after, you expect to get two different tarballs, but because the URL hasn't changed, if you have an old release tarball in your Docker cache, you'll get the old version even if you pass the "docker build --no-cache" option.
This is why we default to a checkin ID rather than a generic tag like "trunk": so the URL will change each time you update your Fossil source tree, forcing Docker to pull a fresh tarball.
5.4.2 User & Group IDs
The "fossil" user and group IDs inside the container default to 499. Why? Regular user IDs start at 500 or 1000 on most Unix type systems, leaving those below it for "system" users like this Fossil daemon owner. Since standard OS system users start at 0 and go upward, we started at 500 and went down one to reduce the chance of a conflict to as close to zero as we can manage.
To change it to something else, say:
$ docker build -t fossil --build-arg UID=501 .
This is particularly useful if you're putting your repository on a Docker volume since the IDs "leak" out into the host environment via file permissions. You may therefore wish them to mean something on both sides of the container barrier rather than have "499" appear on the host in "ls -l" output.
6.0 Building on/for Android
6.1 Cross-compiling from Linux
The following instructions for building Fossil for Android via Linux, without requiring a rooted OS, are adapted from forumpost/e0e9de4a7e.
On the development machine, from the fossil source tree:
export CC=$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang
./configure --with-openssl=none
make
On the Android device, enable the USB debugging option from
Developer menu in Device Options. Connect the device to the development
system with USB. If it's configured and connected properly,
the device should show up in the output of adb devices
:
sudo adb devices
Copy the resulting fossil binary onto the device...
sudo adb push fossil /data/local/tmp
And run it from an adb
shell:
sudo adb shell
> cd /data/local/tmp
# Fossil requires a HOME directory to work with:
> export HOME=$PWD
> export PATH=$PWD:$PATH
> fossil version
This is fossil version 2.11 [e5653a4ceb] 2020-03-26 18:54:02 UTC
The output might, or might not, include warnings such as:
WARNING: linker: ./fossil: unused DT entry: type 0x6ffffef5 arg 0x1464
WARNING: linker: ./fossil: unused DT entry: type 0x6ffffffe arg 0x1ba8
WARNING: linker: ./fossil: unused DT entry: type 0x6fffffff arg 0x2
The source of such warnings is not 100% certain. Some information about these (reportedly harmless) warnings can be found on this StackOverflow post.
7.0 Building for Fuzz Testing
This feature is primarily intended for fossil's developers and may change at any time. It is only known to work on Linux systems and has been seen to work on x86/64 and ARM.
Fossil has builtin support for processing specific features using libfuzzer. The features which can be tested this way are found in the help text for the test-fuzz command.
Fuzzing requires:
- Customizing the build of fossil a small bit.
- The clang C compiler.
- libfuzzer. On Ubuntu-derived systems, it can be installed with apt install libfuzzer-XYZ, where XYZ is a version number (several versions may be available on any given system)
First, modify the top-level Makefile.in:
- Extend the TCCFLAGS variable with: -fsanitize=fuzzer -DFOSSIL_FUZZ (and see src/fuzz.c for more options).
- Rename APPNAME from fossil to fossil-fuzz.
Then rebuild:
$ make clean $ ./configure CC=/path/to/clang $ make
If clang is your default compiler, the CC configure option is not required.
The resulting fossil-fuzz binary differs from the standard one primarily in that it runs the test-fuzz command by default. It needs to be told what to fuzz and needs to be given a directory of input files to seed the fuzzer with:
$ mkdir cases # Copy input files into ./cases. e.g. when fuzzing the markdown # processor, copy any to-be-tested .md files into that directory. # Then start the fuzzer: $ ./fossil-fuzz --fuzztype markdown cases
As it works, it writes its mutated test files into the test-input directory, each one named in the form of a hash. When it finds a problem it will produce a stack trace for the offending code, will output the name of the file which triggered the crash (named cases/SOME_HASH) and may, depending on the nature of the problem, produce a file named crash-SOMETHING. In theory the crash file can be fed directly back into the fuzzer to reproduce the problem:
$ ./fossil-fuzz --fuzztype markdown crash-SOMETHING
But whether or not it will genuinely crash may depend on static app-level state which might not trigger the crash when running an individual test.
For a detailed information about the fuzzer's flags and features, see:
- https://llvm.org/docs/LibFuzzer.html
- https://github.com/google/fuzzing/blob/master/tutorial/libFuzzerTutorial.md
Flags for the fuzzer can be passed directly to fossil, e.g. -jobs=4 to start four fuzzer jobs in parallel, but doing so may cause the fuzzer to strip the --fuzztype flag, leading to it testing the wrong thing. When passing on fuzzer-specific flags along with --fuzztype, be sure to check your system's process list to ensure that your --fuzztype flag is there.
8.0 Building WebAssembly Components
As of version 2.19, fossil uses one component built as WebAssembly a.k.a. WASM. Because compiling WASM code requires non-trivial client-side tooling, the repository includes compiled copies of these pieces. Most Fossil hackers should never need to concern themselves with the WASM parts, but this section describes how to for those who want or need to do so.
The bits described in this section are necessary when updating extsrc/pikchr.c from the upstream source, or the fossil binary will use a different version of pikchr than /pikchrshow does (as the latter runs the WASM build of pikchr).
These instructions have only ever been tested on Linux systems. They "should" work on any Unix-like system supported by Emscripten. The fossil makefiles for Windows builds do not include any of the WASM-related components (patches to add that would be welcomed, of course).
The first step is to configure the tree with support for Emscripten. This requires that the system has the Emscripten SDK (a.k.a. emsdk) installed, as documented at:
https://emscripten.org/docs/getting_started/downloads.html
For instructions on keeping the SDK up to date, see:
https://emscripten.org/docs/tools_reference/emsdk.html
Sidebar: getting Emscripten up and running is trivial and painless, at least on Linux systems, but the installer downloads many hundreds of megabytes of tools and dependencies, all of which will be installed under the single SDK directory (as opposed to being installed at the system level). It does, however, require that python3 be installed at the system level and it can optionally make use of a system-level cmake for certain tasks unrelated to how fossil uses the SDK.
After installing the SDK, configure the fossil tree with emsdk support:
$ ./configure --with-emsdk=/path/to/emsdk ...other options...
If the --with-emsdk flag is not provided, the configure script will check for the environment variable EMSDK, which is one of the standard variables the SDK environment uses. If that variable is found, its value will implicitly be used in place of the missing --with-emsdk flag. Thus, if the emsdk_env.sh script is sourced into the shell before running the configure script, the SDK will be detected even without the config flag.
The configure script installs some makefile variables which tell the build where to find the SDK and it generates a script named tools/emcc.sh (from the template file /tools/emcc.sh.in), which is a wrapper around the Emscripten C compiler (emcc). The wrapper script uses the configure-time state to attempt to set up the various environment variables which are required by emcc and will fail if it cannot do so. Once it's set up the environment, it passes on all of its arguments to emcc.
The WASM-related build parts are set up such that none of them should ever trigger implicity (e.g. via dependencies resolution) in a normal build cycle. They are instead explicitly built as described below.
From the top of the source tree, all WASM-related components can be built with:
$ make wasm
As of this writing, those parts include:
- extsrc/pikchr.wasm is a WASM-compiled form of extsrc/pikchr.c.
- extsrc/pikchr.js is JS/WASM glue code generated by Emscripten to give JS code access to the API exported by the WASM file.
Sidebar: The file extsrc/pikcher-worker.js is hand-coded and intended to be loaded as a "Worker" in JavaScript. That file loads the main module and provides an interface via which a main JavaScript thread can communicate with pikchr running in a Worker thread. The file src/fossil.page.pikchrshowasm.js implements the /pikchrshow app and demonstrates how pikchr-worker.js is used.
When a new version of extsrc/pikchr.c is installed, the files pikchr.{js,wasm} will need to be recompiled to account for that. Running make wasm will, if the build is set up for the emsdk, recompile those:
$ make wasm
./tools/emcc.sh -o extsrc/pikchr.js ...
$ ls -la extsrc/pikchr.{js,wasm}
-rw-rw-r-- 1 stephan stephan 17263 Jun 8 03:59 extsrc/pikchr.js
-rw-rw-r-- 1 stephan stephan 97578 Jun 8 03:59 extsrc/pikchr.wasm
Sidebar: if that fails with a message along the lines of:setting `EXPORTED_RUNTIME_METHODS` expects `<class 'list'>` but got `<class 'str'>`
then the emcc being invoked is too old: emcc changed the format of list-type arguments at some point. The required minimum version is unknown, but any SDK version from May 2022 or later "should" (as of this writing) suffice. Any older version may or may not work.
After that succeeds, we need to run the normal build so that those generated files can be compiled in to the fossil binary, accessible via the /builtin page:
$ make
Before checking in those newly-built files, they need to be tested by running the /pikchrshow page. If that page loads, the compilation process fundamentally worked (a load failure will be made obvious to the viewer). If it fails to load then the browser's dev tools console likely provides at least a small hint (and sometimes a useful hint) about the nature of the problem. Don't check those files in until /pikchrshow runs, though!
Should pikchr's C interface ever change, pikchr-worker.js will need to be updated to accommodate it, but such modification is typically trivial.
8.1 Solutions other than Emscripten?
Emscripten is not the only option for building C as WASM, but it provides a complete toolchain which eliminates many other steps which must otherwise be accounted for on a per-project basis. Despite its convenience, it behooves us to explore other build options for the sake of portability and avoiding what amounts to vendor lock-in.
For later refererence, here are articles specifically covering building WASM projects without using Emscripten: