Add Meson build project file.
Example usage: # Configure Meson build in directory `build-meson` to generate # release binaries comparable to to the ones from the # autotools/make build system. meson setup build-meson \ --prefix=/usr/local \ --buildtype=debugoptimized \ --strip \ -Db_ndebug=true # After configuring the Meson build with the above command, # compile and install to `/usr/local/`; this includes a pkg-config # file. ninja -C build-meson install # Alternatively, compile and install to `/tmp/aa/usr/local/...` # for packaging. DESTDIR=/tmp/aa ninja -C build-meson install # Generate documentation under `build-meson/docs`. ninja -C build-meson docs Library size comparison for stripped `libfreetype.so` generated by all three build systems: - Default build (autotools + libtool): 712 KiB - CMake build (RelWithDebInfo): 712 KiB - Meson build: 712 KiB * meson.build: New top-level Meson build file for the library. * meson_options.txt: New file. It holds user-selectable options for the build, which can be printed with `meson configure`, and selected at `meson setup` or `meson --reconfigure` time with `-D<option>=<value>`. * scripts/parse_modules_cfg.py: A script invoked by `meson.build` to parse `modules.cfg` and extract important information out of it (i.e., the list of modules). * scripts/process_ftoption_h.py: New script invoked by `meson.build` to process the original `ftoption.h` file. It enables or disables configuration macro variables based on the available dependencies. This is similar to what other build systems are using (i.e., Meson's `configure_file()` command is not used here). * scripts/extract_freetype_version.py: New script invoked by `meson.build` to extract the FreeType version number from `<freetype/freetype.h>`. * scripts/extract_libtool_version.py: New script invoked by `meson.build` to extract the libtool `revision_info` data from `builds/unix/configure.raw`, and to generate the corresponding shared library suffix. * scripts/generate_reference_docs.py: New script invoked by `meson.build` to generate the FreeType 2 reference documentation (using the `docwriter` and `mkdocs` packages, which must be already installed).
This commit is contained in:
parent
ab6a21b733
commit
66978a5887
66
ChangeLog
66
ChangeLog
@ -1,3 +1,69 @@
|
||||
2020-09-21 David Turner <david@freetype.org>
|
||||
|
||||
Add Meson build project file.
|
||||
|
||||
Example usage:
|
||||
|
||||
# Configure Meson build in directory `build-meson` to generate
|
||||
# release binaries comparable to to the ones from the
|
||||
# autotools/make build system.
|
||||
meson setup build-meson \
|
||||
--prefix=/usr/local \
|
||||
--buildtype=debugoptimized \
|
||||
--strip \
|
||||
-Db_ndebug=true
|
||||
|
||||
# After configuring the Meson build with the above command,
|
||||
# compile and install to `/usr/local/`; this includes a pkg-config
|
||||
# file.
|
||||
ninja -C build-meson install
|
||||
|
||||
# Alternatively, compile and install to `/tmp/aa/usr/local/...`
|
||||
# for packaging.
|
||||
DESTDIR=/tmp/aa ninja -C build-meson install
|
||||
|
||||
# Generate documentation under `build-meson/docs`.
|
||||
ninja -C build-meson docs
|
||||
|
||||
Library size comparison for stripped `libfreetype.so` generated by
|
||||
all three build systems:
|
||||
|
||||
- Default build (autotools + libtool): 712 KiB
|
||||
- CMake build (RelWithDebInfo): 712 KiB
|
||||
- Meson build: 712 KiB
|
||||
|
||||
|
||||
* meson.build: New top-level Meson build file for the library.
|
||||
|
||||
* meson_options.txt: New file. It holds user-selectable options for
|
||||
the build, which can be printed with `meson configure`, and selected
|
||||
at `meson setup` or `meson --reconfigure` time with
|
||||
`-D<option>=<value>`.
|
||||
|
||||
* scripts/parse_modules_cfg.py: A script invoked by `meson.build` to
|
||||
parse `modules.cfg` and extract important information out of it
|
||||
(i.e., the list of modules).
|
||||
|
||||
* scripts/process_ftoption_h.py: New script invoked by `meson.build`
|
||||
to process the original `ftoption.h` file. It enables or disables
|
||||
configuration macro variables based on the available dependencies.
|
||||
This is similar to what other build systems are using (i.e., Meson's
|
||||
`configure_file()` command is not used here).
|
||||
|
||||
* scripts/extract_freetype_version.py: New script invoked by
|
||||
`meson.build` to extract the FreeType version number from
|
||||
`<freetype/freetype.h>`.
|
||||
|
||||
* scripts/extract_libtool_version.py: New script invoked by
|
||||
`meson.build` to extract the libtool `revision_info` data from
|
||||
`builds/unix/configure.raw`, and to generate the corresponding
|
||||
shared library suffix.
|
||||
|
||||
* scripts/generate_reference_docs.py: New script invoked by
|
||||
`meson.build` to generate the FreeType 2 reference documentation
|
||||
(using the `docwriter` and `mkdocs` packages, which must be already
|
||||
installed).
|
||||
|
||||
2020-09-11 Alexei Podtelezhnikov <apodtele@gmail.com>
|
||||
|
||||
[raster] Improve the second pass (#58373).
|
||||
|
@ -18,7 +18,7 @@
|
||||
|
||||
#include <ft2build.h>
|
||||
/* we use our special ftconfig.h file, not the standard one */
|
||||
#include <ftconfig.h>
|
||||
#include FT_CONFIG_CONFIG_H
|
||||
#include <freetype/internal/ftdebug.h>
|
||||
#include <freetype/ftsystem.h>
|
||||
#include <freetype/fterrors.h>
|
||||
|
368
meson.build
Normal file
368
meson.build
Normal file
@ -0,0 +1,368 @@
|
||||
#
|
||||
# Meson project file for FreeType 2
|
||||
#
|
||||
|
||||
# Copyright (C) 2020 by
|
||||
# David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
#
|
||||
# This file is part of the FreeType project, and may only be used, modified,
|
||||
# and distributed under the terms of the FreeType project license,
|
||||
# LICENSE.TXT. By continuing to use, modify, or distribute this file you
|
||||
# indicate that you have read the license and understand and accept it
|
||||
# fully.
|
||||
|
||||
|
||||
project('freetype2', 'c',
|
||||
meson_version: '>= 0.55.0',
|
||||
default_options: ['default_library=both'],
|
||||
)
|
||||
|
||||
#
|
||||
# Rules to compile the FreeType 2 library itself
|
||||
#
|
||||
|
||||
|
||||
# Apparently meson doesn't provide a read_file() function, so instead
|
||||
# running an external command is required.
|
||||
|
||||
python = import('python')
|
||||
python_exe = python.find_installation(required: true)
|
||||
|
||||
ft2_version = run_command(python_exe,
|
||||
files('scripts/extract_freetype_version.py'),
|
||||
files('include/freetype/freetype.h')).stdout().strip()
|
||||
|
||||
ft2_libtool_version = run_command(python_exe,
|
||||
files('scripts/extract_libtool_version.py'),
|
||||
'--soversion',
|
||||
files('builds/unix/configure.raw')).stdout().strip()
|
||||
|
||||
ft2_includes = include_directories('include')
|
||||
|
||||
|
||||
# Generate a custom `ftmodule.h` version based on the content of
|
||||
# `modules.cfg`.
|
||||
|
||||
ftmodule_h = custom_target('ftmodule.h',
|
||||
output: 'ftmodule.h',
|
||||
input: 'modules.cfg',
|
||||
command: [python_exe, files('scripts/parse_modules_cfg.py'),
|
||||
'--format=ftmodule.h', '@INPUT@', '--output', '@OUTPUT@'],
|
||||
install: true,
|
||||
install_dir: 'include/freetype2/freetype/config',
|
||||
)
|
||||
ft2_sources = [ftmodule_h]
|
||||
|
||||
|
||||
# FreeType 2 modules.
|
||||
|
||||
ft_main_modules = run_command(python_exe,
|
||||
files('scripts/parse_modules_cfg.py'),
|
||||
'--format=main-modules',
|
||||
files('modules.cfg')).stdout().strip().split()
|
||||
|
||||
ft2_sources += files([
|
||||
'src/base/ftbase.c',
|
||||
'src/base/ftinit.c',
|
||||
])
|
||||
|
||||
foreach mod: ft_main_modules
|
||||
source = mod
|
||||
if mod == 'winfonts'
|
||||
source = 'winfnt'
|
||||
elif mod == 'cid'
|
||||
source = 'type1cid'
|
||||
endif
|
||||
ft2_sources += 'src/@0@/@1@.c'.format(mod, source)
|
||||
endforeach
|
||||
|
||||
# NOTE: The `gzip` and `bzip2` aux modules are handled through options.
|
||||
ft_aux_modules = run_command(python_exe,
|
||||
files('scripts/parse_modules_cfg.py'),
|
||||
'--format=aux-modules',
|
||||
files('modules.cfg')).stdout().strip().split()
|
||||
|
||||
foreach auxmod: ft_aux_modules
|
||||
source = auxmod
|
||||
# Most sources are named `src/<module>/<module>.c`, but there are a few
|
||||
# exceptions handled here.
|
||||
if auxmod == 'cache'
|
||||
source = 'ftcache'
|
||||
elif auxmod == 'lzw'
|
||||
source = 'ftlzw'
|
||||
elif auxmod == 'gzip' or auxmod == 'bzip2'
|
||||
# Handled through options instead, see below.
|
||||
continue
|
||||
endif
|
||||
ft2_sources += 'src/@0@/@1@.c'.format(auxmod, source)
|
||||
endforeach
|
||||
|
||||
|
||||
# FreeType 2 base extensions.
|
||||
# Normally configured through `modules.cfg`.
|
||||
|
||||
base_extensions = run_command(python_exe,
|
||||
files('scripts/parse_modules_cfg.py'),
|
||||
'--format=base-extensions-list',
|
||||
files('modules.cfg')).stdout().split()
|
||||
|
||||
foreach ext: base_extensions
|
||||
ft2_sources += files('src/base/' + ext)
|
||||
endforeach
|
||||
|
||||
|
||||
# Header files.
|
||||
|
||||
ft2_public_headers = files([
|
||||
'include/freetype/freetype.h',
|
||||
'include/freetype/ftadvanc.h',
|
||||
'include/freetype/ftbbox.h',
|
||||
'include/freetype/ftbdf.h',
|
||||
'include/freetype/ftbitmap.h',
|
||||
'include/freetype/ftbzip2.h',
|
||||
'include/freetype/ftcache.h',
|
||||
'include/freetype/ftchapters.h',
|
||||
'include/freetype/ftcolor.h',
|
||||
'include/freetype/ftdriver.h',
|
||||
'include/freetype/fterrdef.h',
|
||||
'include/freetype/fterrors.h',
|
||||
'include/freetype/ftfntfmt.h',
|
||||
'include/freetype/ftgasp.h',
|
||||
'include/freetype/ftglyph.h',
|
||||
'include/freetype/ftgxval.h',
|
||||
'include/freetype/ftgzip.h',
|
||||
'include/freetype/ftimage.h',
|
||||
'include/freetype/ftincrem.h',
|
||||
'include/freetype/ftlcdfil.h',
|
||||
'include/freetype/ftlist.h',
|
||||
'include/freetype/ftlzw.h',
|
||||
'include/freetype/ftmac.h',
|
||||
'include/freetype/ftmm.h',
|
||||
'include/freetype/ftmodapi.h',
|
||||
'include/freetype/ftmoderr.h',
|
||||
'include/freetype/ftotval.h',
|
||||
'include/freetype/ftoutln.h',
|
||||
'include/freetype/ftparams.h',
|
||||
'include/freetype/ftpfr.h',
|
||||
'include/freetype/ftrender.h',
|
||||
'include/freetype/ftsizes.h',
|
||||
'include/freetype/ftsnames.h',
|
||||
'include/freetype/ftstroke.h',
|
||||
'include/freetype/ftsynth.h',
|
||||
'include/freetype/ftsystem.h',
|
||||
'include/freetype/fttrigon.h',
|
||||
'include/freetype/fttypes.h',
|
||||
'include/freetype/ftwinfnt.h',
|
||||
'include/freetype/t1tables.h',
|
||||
'include/freetype/ttnameid.h',
|
||||
'include/freetype/tttables.h',
|
||||
'include/freetype/tttags.h',
|
||||
])
|
||||
|
||||
ft2_config_headers = files([
|
||||
'include/freetype/config/ftconfig.h',
|
||||
'include/freetype/config/ftheader.h',
|
||||
'include/freetype/config/ftstdlib.h',
|
||||
'include/freetype/config/integer-types.h',
|
||||
'include/freetype/config/mac-support.h',
|
||||
'include/freetype/config/public-macros.h',
|
||||
])
|
||||
|
||||
ft2_defines = []
|
||||
|
||||
|
||||
# System support file.
|
||||
|
||||
cc = meson.get_compiler('c')
|
||||
|
||||
# NOTE: msys2 on Windows has `unistd.h` and `fcntl.h` but not `sys/mman.h`!
|
||||
has_unistd_h = cc.has_header('unistd.h')
|
||||
has_fcntl_h = cc.has_header('fcntl.h')
|
||||
has_sys_mman_h = cc.has_header('sys/mman.h')
|
||||
|
||||
if has_unistd_h
|
||||
ft2_defines += ['-DHAVE_UNISTD_H=1']
|
||||
endif
|
||||
if has_fcntl_h
|
||||
ft2_defines += ['-DHAVE_FCNTL_H']
|
||||
endif
|
||||
|
||||
mmap_option = get_option('mmap')
|
||||
if mmap_option.auto()
|
||||
use_mmap = has_unistd_h and has_fcntl_h and has_sys_mman_h
|
||||
else
|
||||
use_mmap = mmap_option.enabled()
|
||||
endif
|
||||
if use_mmap
|
||||
# This version of ftsystem.c uses mmap() to read input font files.
|
||||
ft2_sources += files(['builds/unix/ftsystem.c',])
|
||||
else
|
||||
ft2_sources += files(['src/base/ftsystem.c',])
|
||||
endif
|
||||
|
||||
|
||||
# Debug support file
|
||||
#
|
||||
# NOTE: Some specialized versions exist for other platforms not supported by
|
||||
# Meson. Most implementation differences are extremely minor, i.e., in the
|
||||
# implementation of FT_Message() and FT_Panic(), and getting the `FT2_DEBUG`
|
||||
# value from the environment, when this is supported. A smaller refactor
|
||||
# might make these platform-specific files much smaller, and could be moved
|
||||
# into `ftsystem.c` as well.
|
||||
#
|
||||
if host_machine.system() == 'windows'
|
||||
ft2_debug_src = 'builds/windows/ftdebug.c'
|
||||
else
|
||||
ft2_debug_src = 'src/base/ftdebug.c'
|
||||
endif
|
||||
ft2_sources += files([ft2_debug_src])
|
||||
|
||||
ft2_deps = []
|
||||
|
||||
|
||||
# Generate `ftoption.h` based on available dependencies.
|
||||
|
||||
ftoption_command = [python_exe,
|
||||
files('scripts/process_ftoption_h.py'),
|
||||
'@INPUT@', '--output=@OUTPUT@']
|
||||
|
||||
# GZip support
|
||||
zlib_option = get_option('zlib')
|
||||
if zlib_option == 'disabled'
|
||||
ftoption_command += ['--disable=FT_CONFIG_OPTION_USE_ZLIB']
|
||||
else
|
||||
ftoption_command += ['--enable=FT_CONFIG_OPTION_USE_ZLIB']
|
||||
if zlib_option == 'builtin'
|
||||
ftoption_command += ['--disable=FT_CONFIG_OPTION_SYSTEM_ZLIB']
|
||||
else
|
||||
# Probe for the system version.
|
||||
zlib_system = dependency('zlib', required: zlib_option == 'system')
|
||||
ft2_deps += [zlib_system]
|
||||
ftoption_command += ['--enable=FT_CONFIG_OPTION_SYSTEM_ZLIB']
|
||||
endif
|
||||
ft2_sources += files(['src/gzip/ftgzip.c',])
|
||||
endif
|
||||
|
||||
# BZip2 support
|
||||
#
|
||||
# IMPORTANT NOTE: Without `static: false` here, Meson will find both the
|
||||
# static library version and the shared library version when they are
|
||||
# installed on the system, and will try to link them *both* to the final
|
||||
# library!
|
||||
bzip2_dep = meson.get_compiler('c').find_library('bz2',
|
||||
static: false,
|
||||
required: get_option('bzip2'))
|
||||
if bzip2_dep.found()
|
||||
ftoption_command += ['--enable=FT_CONFIG_OPTION_USE_BZIP2']
|
||||
ft2_sources += files(['src/bzip2/ftbzip2.c',])
|
||||
ft2_deps += [bzip2_dep]
|
||||
endif
|
||||
|
||||
# PNG support
|
||||
libpng_dep = dependency('libpng', required: get_option('png'))
|
||||
ftoption_command += ['--enable=FT_CONFIG_OPTION_USE_PNG']
|
||||
ft2_deps += [libpng_dep]
|
||||
|
||||
# Harfbuzz support
|
||||
harfbuzz_dep = dependency('harfbuzz',
|
||||
version: '>= 1.8.0',
|
||||
required: get_option('harfbuzz'))
|
||||
ftoption_command += ['--enable=FT_CONFIG_OPTION_USE_HARFBUZZ']
|
||||
ft2_deps += [harfbuzz_dep]
|
||||
|
||||
# Brotli decompression support
|
||||
brotli_dep = dependency('libbrotlidec', required: get_option('brotli'))
|
||||
ftoption_command += ['--enable=FT_CONFIG_OPTION_USE_BROTLI']
|
||||
ft2_deps += [brotli_dep]
|
||||
|
||||
# We can now generate `ftoption.h`.
|
||||
ftoption_h = custom_target('ftoption.h',
|
||||
input: 'include/freetype/config/ftoption.h',
|
||||
output: 'ftoption.h',
|
||||
command: ftoption_command,
|
||||
install: true,
|
||||
install_dir: 'include/freetype2/freetype/config',
|
||||
)
|
||||
ft2_sources += ftoption_h
|
||||
|
||||
|
||||
# QUESTION: What if the compiler doesn't support `-D` but uses `/D` instead
|
||||
# as on Windows?
|
||||
#
|
||||
# Other build systems have something like c_defines to list defines in a
|
||||
# more portable way. For now assume the compiler supports `-D` (hint: Visual
|
||||
# Studio does).
|
||||
ft2_defines += ['-DFT2_BUILD_LIBRARY=1']
|
||||
|
||||
|
||||
# Ensure that the `ftoption.h` file generated above will be used to build
|
||||
# FreeType. Unfortunately, and very surprisingly, configure_file() does not
|
||||
# support putting the output file in a sub-directory, so we have to override
|
||||
# the default which is `<freetype/config/ftoption.h>`.
|
||||
#
|
||||
# It would be cleaner to generate the file directly into
|
||||
# `${MESON_BUILD_DIR}/freetype/config/ftoption.h`. See
|
||||
# 'https://github.com/mesonbuild/meson/issues/2320' for details.
|
||||
ft2_defines += ['-DFT_CONFIG_OPTIONS_H=<ftoption.h>']
|
||||
|
||||
ft2_c_args = ft2_defines
|
||||
if cc.has_function_attribute('visibility:hidden')
|
||||
ft2_c_args += ['-fvisibility=hidden']
|
||||
endif
|
||||
|
||||
ft2_lib = library('freetype',
|
||||
sources: ft2_sources + [ftmodule_h],
|
||||
c_args: ft2_c_args,
|
||||
include_directories: ft2_includes,
|
||||
dependencies: ft2_deps,
|
||||
install: true,
|
||||
version: ft2_libtool_version,
|
||||
)
|
||||
|
||||
|
||||
# To be used by other projects including this one through subproject().
|
||||
freetype2_dep = declare_dependency(
|
||||
include_directories: ft2_includes,
|
||||
link_with: ft2_lib,
|
||||
version: ft2_libtool_version)
|
||||
|
||||
|
||||
# NOTE: Using both `install_dir` and `subdir` doesn't seem to work below,
|
||||
# i.e., the subdir value seems to be ignored, contrary to examples in the
|
||||
# Meson documentation.
|
||||
install_headers('include/ft2build.h',
|
||||
install_dir: 'include/freetype2')
|
||||
install_headers(ft2_public_headers,
|
||||
install_dir: 'include/freetype2/freetype')
|
||||
install_headers(ft2_config_headers,
|
||||
install_dir: 'include/freetype2/freetype/config')
|
||||
|
||||
|
||||
# TODO(david): Declare_dependency() for using this in a Meson subproject
|
||||
#
|
||||
pkgconfig = import('pkgconfig')
|
||||
pkgconfig.generate(ft2_lib,
|
||||
filebase: 'freetype2',
|
||||
name: 'FreeType 2',
|
||||
description: 'A free, high-quality, and portable font engine.',
|
||||
url: 'https://freetype.org',
|
||||
subdirs: 'freetype2',
|
||||
version: ft2_libtool_version,
|
||||
)
|
||||
|
||||
|
||||
# NOTE: Unlike the old `make refdoc` command, this generates the
|
||||
# documentation under `$BUILD/docs/` since Meson doesn't support modifying
|
||||
# the source root directory (which is a good thing).
|
||||
gen_docs = custom_target('freetype2 reference documentation',
|
||||
output: 'docs',
|
||||
input: ft2_public_headers + ft2_config_headers,
|
||||
command: [python_exe,
|
||||
files('scripts/generate_reference_docs.py'),
|
||||
'--version=' + ft2_version,
|
||||
'--input-dir=' + meson.source_root(),
|
||||
'--output-dir=@OUTPUT@'
|
||||
],
|
||||
)
|
||||
|
||||
# EOF
|
47
meson_options.txt
Normal file
47
meson_options.txt
Normal file
@ -0,0 +1,47 @@
|
||||
#
|
||||
# meson_options.txt
|
||||
#
|
||||
|
||||
# Copyright (C) 2020 by
|
||||
# David Turner, Robert Wilhelm, and Werner Lemberg.
|
||||
#
|
||||
# This file is part of the FreeType project, and may only be used, modified,
|
||||
# and distributed under the terms of the FreeType project license,
|
||||
# LICENSE.TXT. By continuing to use, modify, or distribute this file you
|
||||
# indicate that you have read the license and understand and accept it
|
||||
# fully.
|
||||
|
||||
|
||||
option('zlib',
|
||||
type: 'combo',
|
||||
choices: ['disabled', 'auto', 'builtin', 'system'],
|
||||
value: 'auto',
|
||||
description: 'Support reading gzip-compressed font files.')
|
||||
|
||||
option('bzip2',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Support reading bzip2-compressed font files.')
|
||||
|
||||
option('png',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Support color bitmap glyph formats in the PNG format.'
|
||||
+ 'Requires libpng.')
|
||||
|
||||
option('harfbuzz',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Use Harfbuzz library to improve auto-hinting.'
|
||||
+ ' If available, many glyphs not directly addressable'
|
||||
+ ' by a font\'s character map will be hinted also.')
|
||||
|
||||
option('brotli',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Use Brotli library to support decompressing WOFF2 fonts.')
|
||||
|
||||
option('mmap',
|
||||
type: 'feature',
|
||||
value: 'auto',
|
||||
description: 'Use mmap() to open font files for faster parsing.')
|
107
scripts/extract_freetype_version.py
Normal file
107
scripts/extract_freetype_version.py
Normal file
@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python
|
||||
"""Extract the FreeType version numbers from `<freetype/freetype.h>`.
|
||||
|
||||
This script parses the header to extract the version number defined there.
|
||||
By default, the full dotted version number is printed, but `--major`,
|
||||
`--minor` or `--patch` can be used to only print one of these values
|
||||
instead.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Expected input:
|
||||
#
|
||||
# ...
|
||||
# #define FREETYPE_MAJOR 2
|
||||
# #define FREETYPE_MINOR 10
|
||||
# #define FREETYPE_PATCH 2
|
||||
# ...
|
||||
|
||||
RE_MAJOR = re.compile(r"^ #define \s+ FREETYPE_MAJOR \s+ (.*) $", re.X)
|
||||
RE_MINOR = re.compile(r"^ #define \s+ FREETYPE_MINOR \s+ (.*) $", re.X)
|
||||
RE_PATCH = re.compile(r"^ #define \s+ FREETYPE_PATCH \s+ (.*) $", re.X)
|
||||
|
||||
|
||||
def parse_freetype_header(header):
|
||||
major = None
|
||||
minor = None
|
||||
patch = None
|
||||
|
||||
for line in header.splitlines():
|
||||
line = line.rstrip()
|
||||
m = RE_MAJOR.match(line)
|
||||
if m:
|
||||
assert major == None, "FREETYPE_MAJOR appears more than once!"
|
||||
major = m.group(1)
|
||||
continue
|
||||
|
||||
m = RE_MINOR.match(line)
|
||||
if m:
|
||||
assert minor == None, "FREETYPE_MINOR appears more than once!"
|
||||
minor = m.group(1)
|
||||
continue
|
||||
|
||||
m = RE_PATCH.match(line)
|
||||
if m:
|
||||
assert patch == None, "FREETYPE_PATCH appears more than once!"
|
||||
patch = m.group(1)
|
||||
continue
|
||||
|
||||
assert (
|
||||
major and minor and patch
|
||||
), "This header is missing one of FREETYPE_MAJOR, FREETYPE_MINOR or FREETYPE_PATCH!"
|
||||
|
||||
return (major, minor, patch)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
"--major",
|
||||
action="store_true",
|
||||
help="Only print the major version number.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--minor",
|
||||
action="store_true",
|
||||
help="Only print the minor version number.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--patch",
|
||||
action="store_true",
|
||||
help="Only print the patch version number.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"input",
|
||||
metavar="FREETYPE_H",
|
||||
help="The input freetype.h header to parse.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
with open(args.input) as f:
|
||||
header = f.read()
|
||||
|
||||
version = parse_freetype_header(header)
|
||||
|
||||
if args.major:
|
||||
print(version[0])
|
||||
elif args.minor:
|
||||
print(version[1])
|
||||
elif args.patch:
|
||||
print(version[2])
|
||||
else:
|
||||
print("%s.%s.%s" % version)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
105
scripts/extract_libtool_version.py
Normal file
105
scripts/extract_libtool_version.py
Normal file
@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python
|
||||
"""Extract the libtool version from `configure.raw`.
|
||||
|
||||
This script parses the `configure.raw` file to extract the libtool version
|
||||
number. By default, the full dotted version number is printed, but
|
||||
`--major`, `--minor` or `--patch` can be used to only print one of these
|
||||
values instead.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Expected input:
|
||||
#
|
||||
# ...
|
||||
# version_info='23:2:17'
|
||||
# ...
|
||||
|
||||
RE_VERSION_INFO = re.compile(r"^version_info='(\d+):(\d+):(\d+)'")
|
||||
|
||||
|
||||
def parse_configure_raw(header):
|
||||
major = None
|
||||
minor = None
|
||||
patch = None
|
||||
|
||||
for line in header.splitlines():
|
||||
line = line.rstrip()
|
||||
m = RE_VERSION_INFO.match(line)
|
||||
if m:
|
||||
assert major == None, "version_info appears more than once!"
|
||||
major = m.group(1)
|
||||
minor = m.group(2)
|
||||
patch = m.group(3)
|
||||
continue
|
||||
|
||||
assert (
|
||||
major and minor and patch
|
||||
), "This input file is missing a version_info definition!"
|
||||
|
||||
return (major, minor, patch)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
group = parser.add_mutually_exclusive_group()
|
||||
group.add_argument(
|
||||
"--major",
|
||||
action="store_true",
|
||||
help="Only print the major version number.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--minor",
|
||||
action="store_true",
|
||||
help="Only print the minor version number.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--patch",
|
||||
action="store_true",
|
||||
help="Only print the patch version number.",
|
||||
)
|
||||
group.add_argument(
|
||||
"--soversion",
|
||||
action="store_true",
|
||||
help="Only print the libtool library suffix.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"input",
|
||||
metavar="CONFIGURE_RAW",
|
||||
help="The input configure.raw file to parse.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
with open(args.input) as f:
|
||||
raw_file = f.read()
|
||||
|
||||
version = parse_configure_raw(raw_file)
|
||||
|
||||
if args.major:
|
||||
print(version[0])
|
||||
elif args.minor:
|
||||
print(version[1])
|
||||
elif args.patch:
|
||||
print(version[2])
|
||||
elif args.soversion:
|
||||
# Convert libtool version_info to the library suffix.
|
||||
# (current,revision, age) -> (current - age, age, revision)
|
||||
print(
|
||||
"%d.%s.%s"
|
||||
% (int(version[0]) - int(version[2]), version[2], version[1])
|
||||
)
|
||||
else:
|
||||
print("%s.%s.%s" % version)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
79
scripts/generate_reference_docs.py
Normal file
79
scripts/generate_reference_docs.py
Normal file
@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python
|
||||
"""Generate FreeType reference documentation."""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument(
|
||||
"--input-dir",
|
||||
required=True,
|
||||
help="Top-level FreeType source directory.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--version", required=True, help='FreeType version (e.g. "2.x.y").'
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--output-dir", required=True, help="Output directory."
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get the list of input files of interest.
|
||||
include_dir = os.path.join(args.input_dir, "include")
|
||||
include_config_dir = os.path.join(include_dir, "config")
|
||||
include_cache_dir = os.path.join(include_dir, "cache")
|
||||
|
||||
all_headers = (
|
||||
glob.glob(os.path.join(args.input_dir, "include", "freetype", "*.h"))
|
||||
+ glob.glob(
|
||||
os.path.join(
|
||||
args.input_dir, "include", "freetype", "config", "*.h"
|
||||
)
|
||||
)
|
||||
+ glob.glob(
|
||||
os.path.join(
|
||||
args.input_dir, "include", "freetype", "cache", "*.h"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
if not os.path.exists(args.output_dir):
|
||||
os.makedirs(args.output_dir)
|
||||
else:
|
||||
assert os.path.isdir(args.output_dir), (
|
||||
"Not a directory: " + args.output_dir
|
||||
)
|
||||
|
||||
cmds = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
"docwriter",
|
||||
"--prefix=ft2",
|
||||
"--title=FreeType-" + args.version,
|
||||
"--site=reference",
|
||||
"--output=" + args.output_dir,
|
||||
] + all_headers
|
||||
|
||||
print("Running docwriter...")
|
||||
subprocess.check_call(cmds)
|
||||
|
||||
print("Building static site...")
|
||||
subprocess.check_call(
|
||||
[sys.executable, "-m", "mkdocs", "build"], cwd=args.output_dir
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
160
scripts/parse_modules_cfg.py
Normal file
160
scripts/parse_modules_cfg.py
Normal file
@ -0,0 +1,160 @@
|
||||
#!/usr/bin/env python
|
||||
"""Parse modules.cfg and dump its output either as ftmodule.h or a list of
|
||||
base extensions.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Expected input:
|
||||
#
|
||||
# ...
|
||||
# FONT_MODULES += <name>
|
||||
# HINTING_MODULES += <name>
|
||||
# RASTER_MODULES += <name>
|
||||
# AUX_MODULES += <name>
|
||||
# BASE_EXTENSIONS += <name>
|
||||
# ...
|
||||
|
||||
|
||||
def parse_modules_cfg(input_file):
|
||||
|
||||
lists = {
|
||||
"FONT_MODULES": [],
|
||||
"HINTING_MODULES": [],
|
||||
"RASTER_MODULES": [],
|
||||
"AUX_MODULES": [],
|
||||
"BASE_EXTENSIONS": [],
|
||||
}
|
||||
|
||||
for line in input_file.splitlines():
|
||||
line = line.rstrip()
|
||||
# Ignore empty lines and those that start with a comment.
|
||||
if not line or line[0] == "#":
|
||||
continue
|
||||
|
||||
items = line.split()
|
||||
assert len(items) == 3 and items[1] == "+=", (
|
||||
"Unexpected input line [%s]" % line
|
||||
)
|
||||
assert items[0] in lists, (
|
||||
"Unexpected configuration variable name " + items[0]
|
||||
)
|
||||
|
||||
lists[items[0]].append(items[2])
|
||||
|
||||
return lists
|
||||
|
||||
|
||||
def generate_ftmodule(lists):
|
||||
result = "/* This is a generated file. */\n"
|
||||
for driver in lists["FONT_MODULES"]:
|
||||
if driver == "sfnt": # Special case for the sfnt 'driver'.
|
||||
result += "FT_USE_MODULE( FT_Module_Class, sfnt_module_class )\n"
|
||||
continue
|
||||
|
||||
name = {
|
||||
"truetype": "tt",
|
||||
"type1": "t1",
|
||||
"cid": "t1cid",
|
||||
"type42": "t42",
|
||||
"winfonts": "winfnt",
|
||||
}.get(driver, driver)
|
||||
result += (
|
||||
"FT_USE_MODULE( FT_Driver_ClassRec, %s_driver_class )\n" % name
|
||||
)
|
||||
|
||||
for module in lists["HINTING_MODULES"]:
|
||||
result += (
|
||||
"FT_USE_MODULE( FT_Module_Class, %s_module_class )\n" % module
|
||||
)
|
||||
|
||||
for module in lists["RASTER_MODULES"]:
|
||||
name = {
|
||||
"raster": "ft_raster1",
|
||||
"smooth": "ft_smooth",
|
||||
}.get(module)
|
||||
result += (
|
||||
"FT_USE_MODULE( FT_Renderer_Class, %s_renderer_class )\n" % name
|
||||
)
|
||||
|
||||
for module in lists["AUX_MODULES"]:
|
||||
if module in ("psaux", "psnames", "otvalid", "gxvalid"):
|
||||
result += (
|
||||
"FT_USE_MODULE( FT_Module_Class, %s_module_class )\n" % module
|
||||
)
|
||||
|
||||
result += "/* EOF */\n"
|
||||
return result
|
||||
|
||||
|
||||
def generate_main_modules(lists):
|
||||
return "\n".join(
|
||||
lists["FONT_MODULES"]
|
||||
+ lists["HINTING_MODULES"]
|
||||
+ lists["RASTER_MODULES"]
|
||||
)
|
||||
|
||||
|
||||
def generate_aux_modules(lists):
|
||||
return "\n".join(lists["AUX_MODULES"])
|
||||
|
||||
|
||||
def generate_base_extensions(lists):
|
||||
return "\n".join(lists["BASE_EXTENSIONS"])
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument(
|
||||
"--format",
|
||||
required=True,
|
||||
choices=(
|
||||
"ftmodule.h",
|
||||
"main-modules",
|
||||
"aux-modules",
|
||||
"base-extensions-list",
|
||||
),
|
||||
help="Select output format.",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"input",
|
||||
metavar="CONFIGURE_RAW",
|
||||
help="The input configure.raw file to parse.",
|
||||
)
|
||||
|
||||
parser.add_argument("--output", help="Output file (default is stdout).")
|
||||
|
||||
args = parser.parse_args()
|
||||
with open(args.input) as f:
|
||||
input_data = f.read()
|
||||
|
||||
lists = parse_modules_cfg(input_data)
|
||||
|
||||
if args.format == "ftmodule.h":
|
||||
result = generate_ftmodule(lists)
|
||||
elif args.format == "main-modules":
|
||||
result = generate_main_modules(lists)
|
||||
elif args.format == "aux-modules":
|
||||
result = generate_aux_modules(lists)
|
||||
elif args.format == "base-extensions-list":
|
||||
result = generate_base_extensions(lists)
|
||||
else:
|
||||
assert False, "Invalid output format!"
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
f.write(result)
|
||||
else:
|
||||
print(result)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
105
scripts/process_ftoption_h.py
Normal file
105
scripts/process_ftoption_h.py
Normal file
@ -0,0 +1,105 @@
|
||||
#!/usr/bin/python
|
||||
"""Toggle settings in `ftoption.h` file based on command-line arguments.
|
||||
|
||||
This script takes an `ftoption.h` file as input and rewrites
|
||||
`#define`/`#undef` lines in it based on `--enable=CONFIG_VARNAME` or
|
||||
`--disable=CONFIG_VARNAME` arguments passed to it, where `CONFIG_VARNAME` is
|
||||
configuration variable name, such as `FT_CONFIG_OPTION_USE_LZW`, that may
|
||||
appear in the file.
|
||||
|
||||
Note that if one of `CONFIG_VARNAME` is not found in the input file, this
|
||||
script exits with an error message listing the missing variable names.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
|
||||
parser.add_argument(
|
||||
"input", metavar="FTOPTION_H", help="Path to input ftoption.h file."
|
||||
)
|
||||
|
||||
parser.add_argument("--output", help="Output to file instead of stdout.")
|
||||
|
||||
parser.add_argument(
|
||||
"--enable",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Enable a given build option (e.g. FT_CONFIG_OPTION_USE_LZW).",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--disable",
|
||||
action="append",
|
||||
default=[],
|
||||
help="Disable a given build option.",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
common_options = set(args.enable) & set(args.disable)
|
||||
if common_options:
|
||||
parser.error(
|
||||
"Options cannot be both enabled and disabled: %s"
|
||||
% sorted(common_options)
|
||||
)
|
||||
return 1
|
||||
|
||||
with open(args.input) as f:
|
||||
input_file = f.read()
|
||||
|
||||
options_seen = set()
|
||||
|
||||
new_lines = []
|
||||
for line in input_file.splitlines():
|
||||
# Expected formats:
|
||||
# #define <CONFIG_VAR>
|
||||
# /* #define <CONFIG_VAR> */
|
||||
# #undef <CONFIG_VAR>
|
||||
line = line.rstrip()
|
||||
if line.startswith("/* #define ") and line.endswith(" */"):
|
||||
option_name = line[11:-3].strip()
|
||||
option_enabled = False
|
||||
elif line.startswith("#define "):
|
||||
option_name = line[8:].strip()
|
||||
option_enabled = True
|
||||
elif line.startswith("#undef "):
|
||||
option_name = line[7:].strip()
|
||||
option_enabled = False
|
||||
else:
|
||||
new_lines.append(line)
|
||||
continue
|
||||
|
||||
options_seen.add(option_name)
|
||||
if option_enabled and option_name in args.disable:
|
||||
line = "#undef " + option_name
|
||||
elif not option_enabled and option_name in args.enable:
|
||||
line = "#define " + option_name
|
||||
new_lines.append(line)
|
||||
|
||||
result = "\n".join(new_lines)
|
||||
|
||||
# Sanity check that all command-line options were actually processed.
|
||||
cmdline_options = set(args.enable) | set(args.disable)
|
||||
assert cmdline_options.issubset(
|
||||
options_seen
|
||||
), "Could not find options in input file: " + ", ".join(
|
||||
sorted(cmdline_options - options_seen)
|
||||
)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
f.write(result)
|
||||
else:
|
||||
print(result)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
Loading…
Reference in New Issue
Block a user