Advice
My advice would be to think of the dotted version number as a marketing artifact. It could just as well be a code name, or a color, or anything else that provides a short way to identify a publicly available build. But there are advantages to using names that are short enough for people to remember and which also indicate a some sort of natural order so people know at a glance that "kitkat" is newer than "jellybean".
Getting your revisions control system to provide it in any kind of automated way is a recipe for headaches. And with a distributed version control system, you have the additional problem that different versions could be assigned the same name when built in different places by different developers and likely with different options. You can't even use a count of builds, because different developers will build at different times.
If all "official" builds come from a single system, then you could use automation on that system to assign unique names to each such build. But what about a portable application built for many platforms, and in that case likely build on many distinct machines?
IMHO, the only sensible way out is to admit that the version number is completely arbitrary and artificial to the build system.
Of course, as a matter of project management you need to have a policy for how version numbers are created and assigned, and follow that policy carefully.
Branch for Releases
With fossil
, one approach to managing this is to create a branch for each public release. As one of the first checkins on that branch, you change the version number displayed in documentation and by the code to match, possibly including an additional qualifier like "alpha", "beta", or "release candidate". As the release cycle progresses, bugs found can be fixed in this branch. Meanwhile, features not scheduled for the release can be developed on their own branches and/or on the trunk without risk of disrupting the release process. Naturally, once the release is made, it can and likely should be merged back to the trunk so that none of the bug fixes get lost.
But don't close that branch, it gives you the logical framework for future maintenance of that released version, making it easy to check out the source code that was the basis for the final release of that version.
Include the UUID in the Release
Regardless of whether or not a branch was preserved, including enough of the manifest UUID to identify the which checkin was actually released in the release itself is important. That can mean that in practice one should never build a release in a fossil workspace with any pending changes.
Since making that mistake in a project of my own, I've added code to the project build process that verifies that the workspace is clean, and if not displays loud warnings, and makes the version number displayed by the built code say that the build is not safe for release as well. The process also provides the first few digits the UUID to the compiler via a command line option like -DUUID=[0123456789]
. This trick was done by using fossil changes
to detect a dirty workspace, and the manifest.uuid
file to provide the UUID. The UUID could also be obtained from fossil info
.
Example with Gnu Make
My project's top level Makefile includes the following code:
#
# Discover fossil checkin id and public version number
#
MANIFESTUUID := $(shell sed -e "s/\(.\{10\}\).*/\1/" manifest.uuid)
VERSIONMAJ := $(shell sed -n -e "/define VERSION_MAJ/ s/^[^0-9]*\([0-9]*\)[^0-9]*$$/\1/p" src/version.h)
VERSIONMIN := $(shell sed -n -e "/define VERSION_MIN/ s/^[^0-9]*\([0-9]*\)[^0-9]*$$/\1/p" src/version.h)
VERSIONPAT := $(shell sed -n -e "/define VERSION_PAT/ s/^[^0-9]*\([0-9]*\)[^0-9]*$$/\1/p" src/version.h)
CHECKCLEAN := $(if $(shell fossil changes),-WITH-UNCOMMITTED-CHANGES)
#
# Create a filename-friendly version string like v1.123p42 where
# exacly three digits of the minor version number are displayed.
#
VERSION := v$(VERSIONMAJ).$(shell echo 000$(VERSIONMIN) | sed -n -e s/^.*\([0-9]\{3\}\).*$$/\1/p )p$(VERSIONPAT)$(CHECKCLEAN)
This depends on two files in the fossil workpsace, and on the output of the fossil changes
command to construct a string that can be used as part of the file name of a released package that includes a major number, a minor number, a patch level, and if the workspace is not clean enough the extra text -WITH-UNCOMMITTED-CHANGES
. It also puts the first 10 characters of the UUID in the variable MANIFESTUUID
where it can be passed in to the compilation.
By policy, the major, minor, and patch parts of the version number are maintained in the file src/version.h
, and change from time to time as work progresses.