Search code examples
gitversion-controlversionreleaseautoconf

How can I elegantly update the version string from autoconf in things like the man page and other non-source documents?


As the xnec2c project grows, it seems that more and more files need to be modified to update its version. At the moment it looks like this:

  1. Update the AC_INIT version
  2. Update the man page version in doc/xnec2c.1
  3. Update the version in the packaged .spec file for RPM builds
  4. Update doc/xnec2c.html with the changelog and notes.
  5. Final release commit with the release message and version info
  6. Push tags to github
  7. Profit. (ok, really its a labor or Love, the profit is the Joy of developing!)

I can write a shell script to hack these each time I bump a version, but surely this has been solved before!

Is there a more elegant way?


Solution

  • You can write make recipes (basically shell scripts) which build the desired files from the available information by invoking more or less common tools.

    Let's start with something easy like the version number in the man page.

    dnl configure.ac snippet
    AC_PROG_SED
    

    You rename the man page file doc/xnec2c.1 to doc/xnec2c.1in, so that running make can build a doc/xnec2c.1 man page with the correct version number substituted in:

    .\" doc/xnec2c.1in man page snippet
    This is the man page for xnec2c version @PACKAGE_VERSION@.
    
    dnl Makefile.am or Makefile-files snippet
    
    EXTRA_DIST += doc/xnec2c.1in
    CLEANFILES += doc/xnec2c.1
    man1_DATA  += doc/xnec2c.1
    
    SED_REPLACEMENTS += -e 's|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g'
    
    .1in.1:
            @$(MKDIR_P) $(@D) ||:
            $(SED) $(SED_REPLACEMENTS) < $< > $@
    

    This is a basic recipe to get you started. Some issues it still has:

    • You need to define the variables somewhere above the snippet before you can += add to them (CLEANFILES =, etc). I like the += way of defining variables very much because makes diffs which add or remove something like generating doc/xnec2c.1 from doc/xnec2c.1in much easier to read.

    • When you put an invalid sed command into SED_REPLACEMENTS, running sed will fail leave behind an empty doc/xnec2c.1 file. The next make run then sees that the empty newer than doc/xnec2c.1in, skips rebuilding doc/xnec2c.1, resulting in an apparently successful build shipping an empty file as man page.

    • The generated file might contain @FOOBAR@ patterns which you forgot to add to the SED_REPLACMENTS variable, and you could detect that with grep and an appropriate regexp.

    • You might want to use a more generic suffix pattern for substitutions to allow e.g. substituting *.1.in to *.1, and *.spec.in to *.spec, etc. Using a pattern like %: %.in for GNU make will work here, but is not portable make. (I cannot remember off the top of my head whether .in: is the portable way to write %: %.in for all possible cases.)

    Then there is the question of whether you want to put the generated file under version control or not. For simple substitutions which can be done be very standard tools like sed, you can expect the user to have that tool available and have the build work. You might not want to require your users to require a seldomly used tool, and therefore choose to put the built result under version control. I usually err on the side of requiring tools on user systems and therefore tell my version control system to ignore the generated file, because I do not want to have to deal with source controlled files changing due to me editing another source controlled file.

    # .gitignore snippet
    /doc/xnec2c.1
    

    So, now we get to substituting changelogs into a HTML file. This now becomes arbitrarily complex depending on what format your changelog is in and where it is stored, and how you want to convert it to HTML.

    I personally like to write text documentation such as README as README.md markdown files due to github.com rendering that nicely as HTML, so I keep my changelogs in a NEWS.md file. (Part of) that NEWS.md file could be substituted into a doc/xnec2c.md file generated from, say doc/xnec2c.md.in, and then converted from .md to .html using the markdown tool.

    Then you need to have configure.ac check for the presence of and either require the user to have markdown installed, or add the generated doc/xnec2c.md file to the git index, and then deal with git suddenly noticing changed files just because you have changed the version number inside AC_INIT.

    You can also rely on the version information from git tags and use a shell script to generate the AC_INIT version number from git tags by using the m4_esyscmd m4 macro. That brings its own set of benefits and drawbacks however, and is probably a bit outside the scope of this question. In the end, though, some files should directly contain the version number after all, like e.g. a NEWS or NEWS.md file detailing the user visible changes from the last version, as you will want that file to show the proper information both on github.com and in a dist tarball. So there will always be some work to update the version number inside some files.

    Automatic consistency checks can be helpful here, either hooked into the all-local or check-local target, or written as a separate shell script you add to TESTS.

    In a few projects I have on github.com, I use the first section of the markdown formatted user changelog NEWS.md as the github release notes, often unchanged.

    There might be more elegant ways, but these are a few things which work for me in similar situations. Something is always helpful, however: Document every step in the release process so that you can just read off the list of steps the next time you need them, without needing to remember all the details inside your head.

    I hope this helps, even if it probably is not very general best practise in all aspects.