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).wl/meson-logging
parent
ab6a21b733
commit
66978a5887
9 changed files with 1038 additions and 1 deletions
@ -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 |
@ -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.') |
@ -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()) |
@ -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()) |
@ -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()) |
@ -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()) |
@ -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