shithub: freetype+ttf2subf

ref: 8cd9575c27d01a313ef13196c1fa2d67d831b2a1
dir: /docs/BUILD/

View raw version
                       The FreeType Build System Internals
                       -----------------------------------

Introduction:

  This document describes the details of the FreeType build system. The
  build system is a set of Makefiles and other configuration files used
  to select, compile and link together the various FreeType components
  according to the current platform, compiler and requested feature set.

  This document also explains how to use the build system to develop
  third-party font drivers or extensions to the engine, without altering
  the general FreeType hierarchy;


I. Portability issues :

  Given that the design of FreeType 2 is much more modular and flexible than
  in previous versions, its build system is entirely based on GNU Make. There
  are several reasons for this :

    - It is by far the most available make tool on the planet, and
      has probably been ported to every development environment known
      to homo programmaticus.

    - It provides useful features (like conditional defines, pattern
      and wildcard matching) which are essential when implementing a
      flexible configuration system, as described below

  Note that you do not need to have a unix-like shell (like "sh" or "csh")
  on your system in order to build FreeType.
  



II. The library design :

  FreeType is made of several components, each with a specific role :

    - THE BASE LAYER:
      It is used to implement generic font services as well as provide
      the high-level API used by client applications.

    - ONE OR MORE FONT DRIVERS:
      Each driver is a small component used to read and process a given
      font format. Note that with FreeType 2, it is possible to add,
      remove or upgrade a font driver at *runtime*.

    - ONE OR MORE RASTERS:
      A raster is a module used to render a vectorial glyph outline into
      a bitmap or an anti-aliased pixmap. They differ in their output
      quality, speed and memory usage.

    - A LOW-LEVEL MODULE, CALLED "FTSYSTEM":
      It is used to implement memory management and file i/o. Uses the
      Ansi C Library by default, though some system-specific replacements
      are provided in order to improve performance.

    - AN "INIT" LAYER:
      A tiny module used to implement the library initialisation routine,
      i.e. FT_Init_FreeType. It is in charge of registering the font drivers
      and rasters selected at build time.

    - AN "OLD API" LAYER:
      A simple layer used to link legacy applications using the FreeType
      1.x API. Note that it is binary backwards compatible, which means that
      applications do not need to be recompiled, only re-linked to this
      layer.

  For more details, please read the "FreeType Internals" Document.      


  The FreeType build system is in charge of the following tasks :

    - detect (or select) the current platform in order to select the
      best version of the "ftsystem" module. By default, it will use
      the pure-ANSI version.

    - determine which font drivers, and which rasters, should be
      statically linked to the library at build time. These will always
      be available after the library has been initialised through a call
      to FT_Init_FreeType.

    - eventually compile other font drivers or rasters in order to later
      link them dynamically to the library at runtime, through
      FT_Add_Driver / FT_Upgrade_Driver..

    - compile the "init" layer, putting code in the implementation of
      the FT_Init_FreeType function to register each selected font driver
      or raster to the library.



III. General overview :

  The FreeType build system uses a hierarchy of included sub-Makefiles
  to compile and link the library.

  Each included sub-Makefile is called a "rules" file, and has a very
  specific purpose. The suffix for rules files is ".mk" as in :

       detect.mk
       config.mk
       rules.mk
       etc...


  Here's a simple diagram of the build hierarchy, which is then explained
  with details :



                       Makefile            ( ./Makefile )

                          |
                          |
                          v

                     Config Rules          ( ./config/<system>/config.mk )

                          |
                          |
                          v

                    Library  Rules         ( ./config/freetype.mk )

                      |   |   |
                      |   |   |
                      v   v   v

                  Component(s) Rules       ( ./src/<component>/rules.mk )



  1. The "root" Makefile :

     This file must be invoked from the "freetype" directory with GNU Make.

     a. Host platform auto-detection:

       When run for the first time, this Makefile will try to auto-detect
       the current host platform, by running the rules file named
       `./config/detect.mk'. If the host system cannot be detected,
       it will default to the `ansi' system.

       It will then copy the rules file `./config/<system>/config.mk' to
       the current directory and display the results of the auto-detection.

       You can, at any time, re-run the auto-detection routine by invoking
       the root Makefile with the "setup" target, as in :

              % make setup

       Note also that it is possible to use a second argument to indicate
	   a specific compiler. For example, here are the lignes to be used
	   in order to configure a build with LCC, Visual C++ and Visual Age
	   on a Win32 machine
	   
	          > gmake setup lcc
			  > gmake setup visualc
			  > gmake setup visualage

       The list of compilers is platform-specific and should be contained
	   in `config/<system>/detect.mk'.

       If the detection results do not correspond to your platform or
       settings, refer to chapter VI which describes the auto-detection
       system in great details..


     b. Building the library:

       Once the host platform has been detected, you can run `make' once
       again. The root Makefile will then detect the configuration rules
       file in the current directory then include it.

     Note also that the root Makefile is responsible for defining, if it
     is not already part of the current environment, the variable TOP, which
     designates the top of the FreeType source hierarchy.

     When undefined, it defaults to `.'


  2. The Configuration file :

     The configuration rules file is used to set many important variables
     before including/calling the library rules file (see below).

     These variables are mainly used to describe the host environment
     and compilers. Indeed, this file defines, among others, the following:

       SEP     The directory path separator. This can be `/',`\' or ':'
               depending on the current platform. Note that all pathnames
               are composed with $(SEP) in all rules file (except in
               `include' statements which work well with '/' on all
               platforms)

       CC      The compiler to use

       CFLAGS  The compiler flags used to compile a given source to an
               object file. Usually contains flags for optimisation,
               debugging and/or ansi-compliance

       I       The flag to be used to indicate an additionnal include path
               to the compiler. This defaults to `-I' for an "ansi" system,
               but can be different for others (e.g. `/i=',`-J ', etc..)

       D       The flag to be used to indicate a macro definition to the
               compiler. This defaults to `-D' for an ANSI system.

       T       The flag to be used to indicate a target object file to the
               compiler. This defaults to `-o ' for an ANSI system. Note the
               space after the `o'.        

       O       The object file extension to be used on the current platform.
               Defaults to `o' for an ANSI system, but can be `obj', `coff'
               or others.. There is no dot in the extension !

       A       The library file extension to be used on the current platform.
               Defaults to 'a' for an ANSI system, but can be `lib', `so',
               `dll' or others.. There is no dot in the extension !


       BUILD   The directory where the build system should grab the
               configuration header file `ftconfig.h' as well as the
               system-specific implementation of `ftsystem'.

       OBJ     The directory where all object files will be placed


  3. The Library Rules files :

     Once the variables defined in the configuration rules file, the
     library rules file is included. This one contains all rules required
     to build the library objects into OBJ

     Its structure works as follows:

       - provide rules to compile the low-level `ftsystem' module

       - include the rules files from each font driver or component

       - include the rules file for the "old api" layer

       - provide rules to compile the initialisation layer

       - provide additional targets like `clean', ..


     Note that linking all objects files together into a library is not
     performed in this file, though it might seem reasonable at first
     glance. The reason for this is that not all linkers have a simple
     syntax of the form:

         librarian archive_file  object1 object2 ....

     hence, linking is performed through rules provided in the configuration
     rules file, using the phony `library' target, which has been defined for
     this very specific purpose.


  4. The Components Rules files :

     Each font driver has its own rules file, called `rules.mk' located
     in its own directory. The library rules file includes these component
     rules for each font driver.

     These rules must perform the following:

       - provide rules to compile the component, either into a single `large'
         object, or into multiple small ones

       - for font drivers and rasters, update some variables, that are
         initially defined in the library rules file, which indicate wether
         the component must be registered in the library initialisation code


     a. Component Compile Modes :     

       There are two ways to compile a given component :

         i. Single-object compilation:

            In this mode, the component is compiled into a single object
            file. This is performed easily by defining a single C file whose
            sole purpose is to include all other component sources. For
            example, the truetype driver is compiled as a single object
            named `truetype.o'.


         ii. Multiple objects compilation:

            In this mode, all source files for a single component are compiled
            individually into an object file.

       Due to the way the FreeType source code is written, single mode
       has the following advantages over multiple mode:

         - with many compilers, the resulting object code is smaller than
           the concatenation of all individual objects from multiple mode.
           this, because all functions internal to the component as a whole
           are declared static, allowing more optimisation. It often also
           compiles much faster.


         - most importantly, the single object only contains the external
           symbols it needs to be linked to the base layer (all extern that
           are due to inter-source calls within the component are removed).
           this can reduce tremendously the size of dynamic libraries on
           some platforms

       Multiple mode is useful however to check some dependencies problems
       that might not appear when compiling in single mode, so it has been
       kept as a possibility.


     b. Driver initialisation code :

       The source file `./src/base/ftinit.c' contains the implementation
       of the FT_Init_FreeType function which must, among other things,
       register all font drivers that are statically linked to the library.

       Controlling which drivers are registered at initialisation time is
	   performed by exploiting the state of the C-preprocessor in order to
	   build a linked list (a "chain") of driver interfaces.

       More precisely, each font driver interface file (like `ttdriver.h'
	   or `t1driver.h') has some special lines that look like this :
	   
  			
    			#ifdef FTINIT_DRIVER_CHAIN
 			
    			static
    			const FT_DriverChain  ftinit_<FORMAT>_driver_chain =
    			{
      			FT_INIT_LAST_DRIVER_CHAIN,
      			&<FORMAT>_driver_interface
    			};
  			
    			#undef  FT_INIT_LAST_DRIVER_CHAIN
    			#define FT_INIT_LAST_DRIVER_CHAIN   &ftinit_<FORMAT>_driver_chain
 			
    			#endif 
				
  		As one can see, this code is strictly reserved for `ftinit.c' which
		defines FTINIT_DRIVER_CHAIN before including all font driver header
		files.
		
		When the C-processor parses these headers, it builds a linked list of
		FT_DriverChain element. For exemple, the sequence :
		
		   #define FTINIT_DRIVER_CHAIN
		   #include <ttdriver.h>
		   #include <t1driver.h>
		   
		Will really generate something like:
		
    			static
    	*---->	const FT_DriverChain  ftinit_tt_driver_chain =
    	|		{
      	|		0,
      	|		&tt_driver_interface
    	|		};
  		|	
    	|		static
    	|		const FT_DriverChain  ftinit_t1_driver_chain =
    	|		{
      	*------	&ftinit_tt_driver_chain,
      			&t1_driver_interface
    			};
 	   
	   with the FT_INIT_LAST_DRIVER_CHAIN set to "&ftinit_t1_driver_chain"
	   
	   Hence, the last included driver will be registered first in the library