Est. 1996 Beginner

GTML

A Perl-based HTML pre-processor from 1996 that brought C-preprocessor directives - #include, #define, #ifdef - to hand-written web pages, and is still packaged in Debian thirty years later

Created by Gihan Perera (First Step Communications); extended by Bruno Beaufils from 1999

Paradigm Markup pre-processing: C-preprocessor-style directives and macro substitution embedded in HTML
Typing Untyped - every named constant and macro argument is plain text substituted into the output
First Appeared 1996
Latest Version GTML 3.6.1 (6 January 2022)

GTML is an HTML pre-processor written in Perl. You write web pages as .gtm files, mixing ordinary HTML with directives borrowed straight from the C preprocessor: #include, #define, #ifdef, #else, #endif. GTML expands them and writes plain .html files. Gihan Perera released version 1.0 on 8 June 1996, when most websites were still written by hand in a text editor and “templating” usually meant copy and paste. GTML gave those authors shared headers and footers, named constants, conditional builds and generated navigation links, and the output still worked on any web server.

GTML’s README pitches it at people who “write the HTML in your web pages by hand using a simple text editor”. It is a small piece of web history: a static-site generator from before that term existed. It is also unusually long-lived. The original maintainers stopped working on it long ago, but a Debian maintainer forked it to GitHub in 2022, and it is still in Debian today.

History & Origins

Gihan Perera and the first releases (1996-1998)

The header of the gtml script credits versions 1.0 to 2.3 to Gihan Perera of First Step Communications. The release history the project has kept since then dates every one of those early releases; the main ones were:

VersionDateChange
1.08 Jun 1996First release
1.0114 Jun 1996Added #newdefine; undefined constants allowed
1.0214 Jul 1996Path names in project files
2.024 Nov 1996“Added lots of new features”
2.0130 Nov 1996Commands inside HTML comments; user-specified substitution delimiters
2.0210 Feb 1997Fixed #ifdef nesting
2.123 Jul 1997Substitutions allowed in #include
2.215 Aug 1997allsource project-file command
2.2225 Jul 1998Handles years past 2000
2.319 Feb 1999Enhanced #define

Several GTML release dates fall in 1997, and that is probably why GTML is sometimes listed as a 1997 language. The first release, however, is dated June 1996.

Where the name came from

Bruno Beaufils, who later took over the project, wondered whether the “G” stood for Gihan. In the history notes he wrote for the project, dated 31 August 1999, he reports that Perera gave him the real answer: “G” is simply the letter before “H”. A GTML source file is the stage that comes just before the HTML file. Beaufils added that he hoped it would come to be read as “GNU”.

Bruno Beaufils and the 3.x series (1999)

Beaufils says he had been looking for a way to maintain the websites of the research team he belonged to. He tried several tools, found GTML “pretty cool, but lacking some important features”, and asked Perera to add them. Perera did not have the time, so Beaufils taught himself Perl by reading the GTML source and made the changes himself. He also asked Perera whether he could distribute GTML under the GNU General Public License, “since his license policy was not as open as GPL”, and Perera agreed.

Version 3.0 came out on 22 June 1999. It was followed by a rapid series of point releases that summer, adding Norwegian, Italian, Swedish, Dutch and German month names for timestamps along the way. Beaufils announced the tool on Freshmeat and, after user feedback, restructured the script. The result was version 3.5.0 (2 September 1999), which added most of the features GTML is known for today:

  • macros with arguments
  • embedded Perl code and shell-command output
  • Makefile generation
  • customizable site maps
  • character translation tables
  • HTML output compression

The 3.x series was bug-fixed through 3.5.3 (13 September 1999), and the project documentation pointed to download archives on LIFL’s FTP server. By then, Raphael Bossek had already packaged GTML for Debian.

SourceForge, abandonment and a GitHub revival (2004-2022)

In 2004 Andrew E. Schulman took over the code and moved it to SourceForge. The project was registered there on 28 January 2004. His 3.5.4 release (29 October 2004) fixed long-standing bugs in nested #if blocks and added error checks for unbalanced conditionals. No further upstream release followed for more than seventeen years.

Kenneth J. Pronovici had maintained the Debian package since 2010. In 2014 Schulman told him the code was abandoned, and Pronovici kept GTML going through Debian patches alone. In early 2022 a user’s questions led him to fork the code to GitHub “so there would be a real home for it again”. The release commits for versions 3.6.0 and 3.6.1 are both dated 6 January 2022, and only 3.6.1 was tagged as a GitHub release. They applied the Debian patches upstream, converted the documentation to Markdown and pointed the script at its new home.

Design Philosophy

GTML’s design comes down to three rules, all stated in its documentation:

  1. Leave the HTML alone. “GTML doesn’t attempt to interpret your HTML commands in any way”. Anything that is not a GTML command passes through unchanged, so an existing HTML page is already a valid GTML source file once it is renamed to .gtm.
  2. Borrow a syntax people already know. The reference manual says the command syntax “was stolen shamelessly from the C language pre-processor”. Anyone who had written #include <stdio.h> could read a GTML file.
  3. Generate static files in a batch. GTML runs from the command line and produces ordinary HTML. The output needs no server-side includes, no CGI and no particular browser or server. The README admits this means GTML is not for people using “a sophisticated graphical tool to generate your HTML”.

Key Features

Commands and named constants

Commands start with # at the very beginning of a line. Named constants are substituted wherever <<NAME>> appears:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#define GREETING Hello, World!
#define link(u,t) <a href="u">t</a>
<html><body>
<h1><<GREETING>></h1>
#ifdef DRAFT
<p>Draft copy</p>
#else
<p>See <<link(https://codearchaeology.dev,CodeArchaeology)>></p>
#endif
</body></html>

Running gtml hello.gtm writes hello.html:

1
2
3
4
<html><body>
<h1>Hello, World!</h1>
<p>See <a href="https://codearchaeology.dev">CodeArchaeology</a></p>
</body></html>

Running gtml -DDRAFT hello.gtm defines DRAFT on the command line and selects the other branch. (Both outputs above come from GTML 3.6.1 run under Perl.)

The full command set is small:

CommandPurpose
#include, #includeliteralInsert another file, with or without processing it
#define, #define!, #define+, #newdefine, #undefCreate, compute, extend or remove named constants and macros
#if, #ifdef, #ifndef, #elsif, #elsifdef, #elsifndef, #else, #endifConditional output
#timestamp, #mtimestampInsert the processing time or the file’s modification time, using a format string such as $dd/$MM/$yy
#literal, #entitiesStop command interpretation, or escape <, > and & (useful for code listings)
#definecharCharacter translation, e.g. #definechar é &eacute;
#compressStrip comments, newlines and extra whitespace from the output
#sitemap, #tocInsert a generated table of contents

Some HTML editors complained about lines starting with #. For those, every command can also be hidden inside a comment, as in <!-- ###include "header.txt" -->. The << >> delimiters can be changed by defining OPEN_DELIMITER and CLOSE_DELIMITER. GTML’s own documentation site, for example, used [[ and ]].

Project files and site hierarchy

A project file (conventionally .gtp) lists every page of a site, plus shared definitions that all of them inherit. If a file name is followed by a level number and a title, GTML builds a document hierarchy:

1
2
3
4
5
6
// site.gtp
define AUTHOR Gihan Perera
timestamp $dd $Month $yyyy
contents.gtm 1 Table of Contents
chapter1.gtm 2 Introduction
chapter2.gtm 2 Historical Evidence

For each page, GTML defines LINK_PREV, LINK_NEXT and LINK_UP, with matching TITLE_* constants. A shared navigation include can then use #ifdef LINK_NEXT to emit only the links that apply to that page. The same hierarchy drives #sitemap.

From version 3.x, project files also support:

  • filename aliases, so pages can move without breaking links
  • FAST_GENERATION, which rebuilds only sources newer than their output
  • a -F option for processing a single file
  • a -M option that writes a Makefile from the dependency graph

Escaping into Perl and the shell

Version 3.5.0 added two macros that turn GTML from a text substituter into something closer to a template language:

1
2
<<__PERL__(return (stat(<<ROOT_PATH>><<BASENAME>>.".gtm"))[7];)>>
<<__SYSTEM__(dir)>>

__PERL__ evaluates Perl code and inserts the result of the last expression. __SYSTEM__ inserts the output of a shell command.

Evolution

GTML’s development falls into four distinct periods:

PeriodMaintainerCharacter
1996-1999Gihan PereraOriginal C-preprocessor-style tool, versions 1.0-2.3
1999Bruno BeaufilsGPL relicensing, rewrite, macros with arguments, Perl/shell embedding (3.0-3.5.3)
2004Andrew E. SchulmanSourceForge home, conditional-logic fixes (3.5.4)
2010-presentKenneth J. PronoviciDebian maintenance, then the 2022 GitHub fork (3.6.x)

Almost all of the language’s features date from before 2000. Later releases have been about keeping it correct and installable, not extending it.

Current Relevance

The encyclopedia lists GTML as historical, and as a project under active design it is: the last feature work was in 1999. As software, though, it is still easy to get. The GitHub repository at pronovic/gtml holds the 3.6.1 release and the full original documentation. Debian’s current suites, and distributions built on Debian such as Ubuntu, Devuan, Raspbian and Trisquel, ship GTML as an installable package. The latest Debian upload, 3.6.1-6, is dated 26 September 2026. Because GTML is a single Perl script that uses no modules, it should run wherever Perl 5 does, with no build step, although most distribution packages are Debian or its derivatives.

Few people would choose GTML for a new site over modern static-site generators such as Hugo or Jekyll. Its maintainer says it was “designed in the 1990s, for a world where HTML files were still regularly maintained by hand, but some of us still find it useful today.”

Why It Matters

GTML is a clear example of a pattern that keeps coming back in web development: reuse an existing, well-understood macro language to stop people repeating themselves in HTML. In 1996 there were few ways to share a header across fifty pages without server-side includes. GTML’s answer was the C preprocessor model, which many programmers already knew. It ran as an offline build step and produced plain files that any server could host.

Its own documentation listed other tools working on the same problem around 1999, including WML, Meta-HTML, Chpp, Wpp and PHP. Many similar tools from that era have disappeared. GTML survived through a chain of four maintainers who each picked it up because they needed it, and through the Debian archive. Today it is a working, installable record of how static websites were built before “static-site generator” was a category.

Timeline

1996
Gihan Perera releases GTML 1.0 on 8 June 1996, a Perl script that adds #include, #define and conditional commands to HTML source files
1996
Version 2.0 (24 November) adds a large batch of new features; 2.01 (30 November) lets commands sit inside HTML comments and makes the substitution delimiters user-configurable
1998
Version 2.22 (25 July) makes GTML handle years past 2000, ahead of the Y2K rollover
1999
Bruno Beaufils takes over development with Perera's agreement and relicenses GTML under the GNU GPL; version 3.0 (22 June) adds filename aliases, file-modification timestamps and output-language settings
1999
Raphael Bossek's initial Debian package of GTML (3.3.6-1) enters Debian unstable; the Debian changelog dates it 18 June, but its 1999 dates do not line up with the upstream release dates, so only the year is given here
1999
Version 3.5.0 (2 September), a rewrite by Beaufils, adds macros with arguments, embedded Perl and shell code, makefile generation, customizable site maps and output compression
2004
The GTML project is registered on SourceForge (28 January), where Andrew E. Schulman, who took over the code that year, releases 3.5.4 on 29 October, fixing nested #if handling - the last release for more than seventeen years
2022
Kenneth J. Pronovici, the Debian package maintainer since 2010, forks the abandoned SourceForge code to GitHub; release commits for 3.6.0 and 3.6.1 both land on 6 January (only 3.6.1 is tagged as a GitHub release), folding in long-standing Debian patches
2026
Debian uploads gtml 3.6.1-6 (26 September), keeping the 1996 tool in Debian unstable

Notable Uses & Legacy

GTML's own website

The GTML documentation site on SourceForge was generated by GTML itself; its project file (gtml.gtp) and .gtm sources are preserved in the GitHub repository under docs/sourceforge/src

Research-team websites at LIFL

Bruno Beaufils, whose contact address was at the Laboratoire d'Informatique Fondamentale de Lille (lifl.fr), said he extended GTML to maintain the websites he had set up for his research team; the project documentation pointed to download archives on ftp.lifl.fr

Debian and derivative distributions

Packaged in Debian since 1999 and carried into Ubuntu, Devuan, Raspbian, Kali, Trisquel and other derivatives as a ready-to-install static-site preprocessor

Language Influence

Influenced By

C preprocessor

Running Today

Run examples using the official Docker image:

docker pull debian:trixie-slim

Example usage:

docker run --rm -v $(pwd):/app -w /app debian:trixie-slim bash -c 'apt-get update -qq && apt-get install -y -qq gtml > /dev/null 2>&1 && gtml hello.gtm && cat hello.html'
Last updated: