TMAKE - A Template Based Make Utility


TMAKE License

Copyright (C) 1996,1997 by Troll Tech AS.

Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies. No representations are made about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty.


Introduction

TMAKE is a tool for creating platform-independent makefiles. Briefly told, TMAKE reads a project file, fills in a template and produces a makefile.

TMAKE was written specifically for creating makefiles for Qt applications. We faced this situation:

  1. Qt is a multi-platform GUI toolkit: Source code written for Qt is easily recompiled on all platform supported by Qt, but you still have to write and maintain one makefile per platform and compiler. This is very time-consuming and tedious work.
  2. The Qt meta object compiler (moc) generates C++ files from C++ classes containing signals and slots. It is non-trivial to add these extra rules in the makefile.
We therefore wrote TMAKE. This utility is extremely handy for creating makefiles for Qt applications, but it can also be employed for other projects.

TMAKE is written in Perl and requires perl version 5 or newer. You do not need to be familiar with Perl programming to use TMAKE, but you should learn Perl if you want to write your own templates.

You'll find more information about Perl on www.perl.com. You can download perl for Win32 (Windows NT and 95) from www.activeware.com


Installation

  1. Make sure you have perl version 5 or later installed.
  2. Unpack the tmake.tar.gz or tmake.zip archive.
  3. Set the TMAKEPATH environment variable to the directories containing the template files (see below).
  4. Add the tmake/bin directory to your PATH.
Here are some examples:

UNIX Bourne shell:

    TMAKEPATH=/local/tmake/lib/linux-gcc
    PATH=$PATH:/local/tmake/bin
    export TMAKEPATH PATH
UNIX C shell:
    setenv TMAKEPATH /local/tmake/lib/linux-gcc
    setenv PATH $PATH:/local/tmake/bin
Windows NT and Windows 95:
    set TMAKEPATH=c:\tmake\lib\win32-msvc
    set PATH=%PATH%;c:\tmake\bin
The template directory name has the form platform-compiler. Each template directory contains template files and a configuration file.

Supported platforms and compilers:

  freebsd-gcc   FreeBSD and GNU gcc
  hpux-cc   HPUX and HP CC
  hpux-gcc   HPUX and GNU gcc
  irix-dcc   SGI IRIX and DCC
  irix-gcc   IRIX and GNU gcc
  linux-gcc   Linux and GNU gcc
  netbsd-gcc   NetBSD and GNU gcc
  sco-gcc   SCO UNIX and GNU gcc
  solaris-cc   Sun Solaris and Sun CC
  solaris-gcc   Sun Solaris and GNU gcc
  sunos-gcc   SunOS and GNU gcc
  win32-borland   Windows 95/NT and Borland C++
  win32-gcc   Windows 95/NT and Cygnus GNU Win32 gcc
  win32-msvc   Windows 95/NT and Microsoft Visual C++
  win32-symantec   Windows 95/NT and Symantec C++
  win32-watcom   Windows 95/NT and Watcom C++


Getting Started

Let's assume you have a small Qt application consisting of one C++ header file and two source files. First you need to create a project file, hello.pro:
    HEADERS   =  hello.h
    SOURCES   =  hello.cpp main.cpp
    TARGET    =  hello
Then run tmake to create a Makefile:
    tmake hello.pro -o Makefile
And finally:
    make
This builds the hello program. Remember to set the TMAKEPATH environment variable before you run tmake.

See Makefile for Linux/gcc.
See Makefile for Win32/msvc (Microsoft Visual C++).


Templates and Configurations

The TMAKE distribution includes two template files and one configuration file for each platform/compiler combination. The TMAKEPATH environment variable tells tmake where to find these files:

  app.t   Creates a makefile for building applications.
  lib.t   Creates a makefile for building libraries.
  tmake.conf   This configuration file contains compiler options and lists tools and libraries.

If you have Microsoft Visual C++ 5.0, you can use two special templates to generate project (.dsp) files. After you have generated e.g. hello.dsp, choose "File"->"Open Workspace" and select the hello.dsp file. Visual C++ will then create a workspace for you.

  vcapp.t   Creates an application project file (Microsoft Visual C++ 5.0 only).
  vclib.t   Creates a library project file (Microsoft Visual C++ 5.0 only).

The hello.pro project file above does not have a TEMPLATE or a CONFIG tag. The default template is app (the .t extension is optional) and the default configuration is qt warn_on release:

    TEMPLATE =  app
    CONFIG   =  qt warn_on release
    HEADERS  =  hello.h
    SOURCES  =  hello.cpp main.cpp
    TARGET   =  hello
This project file produces exactly the same result as the original project file. If you're building a library instead of an application you can use the library template instead of the application template:
    TEMPLATE =  lib
    VERSION  =  1.9

The VERSION tag is recognized by the lib.t template and defines the library version. This is important for UNIX shared libraries, but ignored on Windows.

The CONFIG tag is recognized by both the app.t and lib.t templates and tells the TMAKE templates what compiler options to use and which extra libraries to link in.

  qt   This is a Qt application. Tells tmake to add rules for moc files and link with the Qt library.
  opengl   This is an application which requires the OpenGL library.
  warn_on   The compiler should emit more warnings than normally. This option is ignored if "warn_off" is specified.
  warn_off   The compiler should emit no warnings or as few as possible.
  release   Compile with optimization enabled. This option is ignored if "debug" is specified.
  debug   Compile with debug options enabled.

As an example, if the hello application uses both Qt and OpenGL and you want to compile it for debugging, your CONFIG line must read:

    CONFIG = qt opengl debug

This is all you need to know to start using TMAKE.


Program Usage: tmake

Usage:
  tmake [options] project-file
Options:
  -e expr    Evaluate the Perl expression.  Ignores the template file.
  -nodepend  Don't generate dependency information.
  -o file    Write output to file instead of stdout.
  -p file    Load an additional project file.
  -t file    Specify a template file.
  -v         Verbose/debugging on.
The -t option overrides any TEMPLATE tag in the project file.

The default project file extension is ".pro". The default template file extension is ".t". If you do not specify these extension tmake will automatically add them for you.


The progen Utility

The progen utility creates project files for you. It can be used like this:
  progen -n hello -o hello.pro
If no .cpp or .h files are specified on the command line, progen searches for .cpp and .h (except moc_*.cpp) in the current directory and below.

Usage:

  progen [options] [C/C++ header files and source files]
Options:
  -lower   Lower-case letters in filenames (useful on Windows).
  -n name  Specify a project name (TARGET).
  -o file  Write output to file instead of stdout.
  -t file  Specify a template file.

Advanced Topics

If you know Perl programming, there is virtually no limitation to what you can do with TMAKE. First you need to know how TMAKE works.

Template Processing

When you run tmake, it first reads the tmake.conf file. This configuration file has the same syntax as the project file. tmake then reads the project file and sets the project tags it finds, e.g. HEADERS, SOURCES etc. All tags and values are stored in a global associative Perl hash array called project. For example, $project{"SOURCES"} contains "hello.cpp main.cpp" after processing hello.pro. When both the tmake.conf and the project files have been read, tmake starts reading the template file line by line and executes any Perl code it finds in the template.

Example:

    #! This is a comment which will be removed.
    This text will appear in the output.
    #$ $text = "The header file(s) are: " . $project{"HEADERS"};
    # This text also appears in the output.
    #${
       $a = 12;
       $b = 13;
       $text = $a * $b;
    #$}
    That's all.
Output:
    This text will appear in the output.
    The header file(s) are: hello.h
    # This text also appears in the output.
    156
    That's all.

Using TMAKE With Lex and Yacc

The standard TMAKE templates knows how to process C and C++ files, but sometimes you need to process additional files and link them into your project. A typical example is to process lex and yacc files when you're builing a parser.

Parser template:

    #!
    #! parser.t: This is a custom template for building a parser
    #!
    #$ IncludeTemplate("app.t");

    ####### Lex/yacc programs and options

    LEX	    =	flex
    YACC    =	#$ $text = ($is_unix ? "yacc -d" : "byacc -d");

    ####### Lex/yacc files

    LEXIN   =	#$ Expand("LEXINPUT");
    LEXOUT  =	lex.yy.c
    YACCIN  =	#$ Expand("YACCINPUT");
    YACCOUT =	y.tab.c
    YACCHDR =	y.tab.h
    PARSER  =	#$ Expand("PARSER");

    ####### Process lex/yacc files

    $(LEXOUT): $(LEXIN)
	    $(LEX) $(LEXIN)

    $(PARSER): $(YACCINPUT) $(LEXOUT)
	    $(YACC) $(YACCINPUT)
	    #$ $text = ($is_unix ? "-rm -f " : "-del ") . '$(PARSER)';
	    #$ $text = ($is_unix ? "-mv " : "-ren ") . '$(YACCOUT) $(PARSER)'; 
The parser template adds some extra rules to the application template in order to build the lex and yacc portions of the project. This template is portable across UNIX and Windows since it generates different commands depending on the $is_unix variable.

To learn more about the Expand() function and other Perl functions which TMAKE provides, please consult the reference manual.

Example project file:

    TEMPLATE    =	parser.t
    CONFIG	=	release
    LEXINPUT    =	lexer.l
    YACCINPUT   =	grammar.y
    PARSER      =	parser.cpp
    SOURCES     =	$$PARSER		\
			node.cpp		\
			asmgen.cpp
    TARGET	=	parser
Here we use macro expansion $$PARSER to avoid writing parser.cpp two places.

Counting the Number of Code Lines

TMAKE is generic since it is based on Perl. You can create your own templates for other purposes than producing makefiles. Here is an example template that counts the number of code lines in our project.

Template wc.t:

  #! Template that count number of C++ lines.
  The number of C++ code lines for #$ $text=$project_name;
  #${
    $files = $project{"HEADERS"} . " " . $project{"SOURCES"};
    $text = `wc -l $files`;
  #$}
Run it:
  tmake -t wc hello
Output:
  The number of C++ code lines for hello.pro
       25    hello.h
       98    hello.cpp
       38    main.cpp
      161    total
This will only work if the wc program is installed on your system.