meson.build (14544B)
1 # 2 # Meson project file for FreeType 2 3 # 4 5 # Copyright (C) 2020-2025 by 6 # David Turner, Robert Wilhelm, and Werner Lemberg. 7 # 8 # This file is part of the FreeType project, and may only be used, modified, 9 # and distributed under the terms of the FreeType project license, 10 # LICENSE.TXT. By continuing to use, modify, or distribute this file you 11 # indicate that you have read the license and understand and accept it 12 # fully. 13 14 # 15 # Say 16 # 17 # meson configure 18 # 19 # to see all configuration options and their default values. For example, 20 # to build only a shared version of FreeType, override the default value 21 # with 22 # 23 # meson setup -Ddefault_library=shared 24 # 25 26 project('freetype2', 'c', 27 meson_version: '>= 0.55.0', 28 version: run_command('builds/meson/extract_freetype_version.py', 29 'include/freetype/freetype.h', 30 check: true).stdout().strip(), 31 ) 32 33 34 # Only meson >= 0.57 can read a file and assign its contents to a 35 # variable; we thus use an external command to have this functionality 36 # with older versions, too. 37 38 python_exe = find_program('python3') 39 40 ft2_so_version = run_command(python_exe, 41 files('builds/meson/extract_libtool_version.py'), 42 '--soversion', 43 files('builds/unix/configure.raw'), 44 check: true).stdout().strip() 45 46 ft2_pkgconfig_version = run_command(python_exe, 47 files('builds/meson/extract_libtool_version.py'), 48 files('builds/unix/configure.raw'), 49 check: true).stdout().strip() 50 51 ft2_includes = include_directories('include') 52 53 freetype_includedir = join_paths(get_option('includedir'), 'freetype2') 54 55 ft2_defines = [] 56 57 freetype_aclocaldir = join_paths(get_option('datadir'), 'aclocal') 58 59 60 # Generate a custom `ftmodule.h` version based on the content of 61 # `modules.cfg`. 62 63 ftmodule_h = custom_target('ftmodule.h', 64 output: 'ftmodule.h', 65 input: 'modules.cfg', 66 command: [python_exe, files('builds/meson/parse_modules_cfg.py'), 67 '--format=ftmodule.h', '@INPUT@', '--output', '@OUTPUT@'], 68 install: true, 69 install_dir: join_paths(freetype_includedir, 'freetype/config'), 70 ) 71 ft2_sources = [ftmodule_h] 72 ft2_defines += ['-DFT_CONFIG_MODULES_H=<ftmodule.h>'] 73 74 75 # FreeType 2 modules. 76 77 ft_main_modules = run_command(python_exe, 78 files('builds/meson/parse_modules_cfg.py'), 79 '--format=main-modules', 80 files('modules.cfg'), 81 check: true).stdout().strip().split() 82 83 ft2_sources += files([ 84 'src/base/ftbase.c', 85 'src/base/ftinit.c', 86 ]) 87 88 foreach mod: ft_main_modules 89 source = mod 90 if mod == 'winfonts' 91 source = 'winfnt' 92 elif mod == 'cid' 93 source = 'type1cid' 94 endif 95 ft2_sources += 'src/@0@/@1@.c'.format(mod, source) 96 endforeach 97 98 # NOTE: The `bzip2` aux module is handled through options. 99 ft_aux_modules = run_command(python_exe, 100 files('builds/meson/parse_modules_cfg.py'), 101 '--format=aux-modules', 102 files('modules.cfg'), 103 check: true).stdout().strip().split() 104 105 foreach auxmod: ft_aux_modules 106 source = auxmod 107 # Most sources are named `src/<module>/<module>.c`, but there are a few 108 # exceptions handled here. 109 if auxmod == 'cache' 110 source = 'ftcache' 111 elif auxmod == 'lzw' 112 source = 'ftlzw' 113 elif auxmod == 'gzip' 114 source = 'ftgzip' 115 elif auxmod == 'bzip2' 116 # Handled through options instead, see below. 117 continue 118 endif 119 ft2_sources += 'src/@0@/@1@.c'.format(auxmod, source) 120 endforeach 121 122 123 # FreeType 2 base extensions. 124 # To be configured in `modules.cfg`. 125 126 base_extensions = run_command(python_exe, 127 files('builds/meson/parse_modules_cfg.py'), 128 '--format=base-extensions-list', 129 files('modules.cfg'), 130 check: true).stdout().split() 131 132 foreach ext: base_extensions 133 ft2_sources += files('src/base/' + ext) 134 endforeach 135 136 137 # Header files. 138 139 ft2_public_headers = files([ 140 'include/freetype/freetype.h', 141 'include/freetype/ftadvanc.h', 142 'include/freetype/ftbbox.h', 143 'include/freetype/ftbdf.h', 144 'include/freetype/ftbitmap.h', 145 'include/freetype/ftbzip2.h', 146 'include/freetype/ftcache.h', 147 'include/freetype/ftchapters.h', 148 'include/freetype/ftcid.h', 149 'include/freetype/ftcolor.h', 150 'include/freetype/ftdriver.h', 151 'include/freetype/fterrdef.h', 152 'include/freetype/fterrors.h', 153 'include/freetype/ftfntfmt.h', 154 'include/freetype/ftgasp.h', 155 'include/freetype/ftglyph.h', 156 'include/freetype/ftgxval.h', 157 'include/freetype/ftgzip.h', 158 'include/freetype/ftimage.h', 159 'include/freetype/ftincrem.h', 160 'include/freetype/ftlcdfil.h', 161 'include/freetype/ftlist.h', 162 'include/freetype/ftlogging.h', 163 'include/freetype/ftlzw.h', 164 'include/freetype/ftmac.h', 165 'include/freetype/ftmm.h', 166 'include/freetype/ftmodapi.h', 167 'include/freetype/ftmoderr.h', 168 'include/freetype/ftotval.h', 169 'include/freetype/ftoutln.h', 170 'include/freetype/ftparams.h', 171 'include/freetype/ftpfr.h', 172 'include/freetype/ftrender.h', 173 'include/freetype/ftsizes.h', 174 'include/freetype/ftsnames.h', 175 'include/freetype/ftstroke.h', 176 'include/freetype/ftsynth.h', 177 'include/freetype/ftsystem.h', 178 'include/freetype/fttrigon.h', 179 'include/freetype/fttypes.h', 180 'include/freetype/ftwinfnt.h', 181 'include/freetype/otsvg.h', 182 'include/freetype/t1tables.h', 183 'include/freetype/ttnameid.h', 184 'include/freetype/tttables.h', 185 'include/freetype/tttags.h', 186 ]) 187 188 ft2_config_headers = files([ 189 'include/freetype/config/ftconfig.h', 190 'include/freetype/config/ftheader.h', 191 'include/freetype/config/ftstdlib.h', 192 'include/freetype/config/integer-types.h', 193 'include/freetype/config/mac-support.h', 194 'include/freetype/config/public-macros.h', 195 ]) 196 197 ft2_defines += ['-DFT2_BUILD_LIBRARY=1'] 198 199 200 # System support file. 201 202 cc = meson.get_compiler('c') 203 204 # NOTE: msys2 on Windows has `unistd.h` and `fcntl.h` but not `sys/mman.h`! 205 has_unistd_h = cc.has_header('unistd.h') 206 has_fcntl_h = cc.has_header('fcntl.h') 207 has_sys_mman_h = cc.has_header('sys/mman.h') 208 209 mmap_option = get_option('mmap') 210 211 use_unix_ftsystem_c = false 212 if mmap_option.disabled() 213 ft2_sources += files(['src/base/ftsystem.c',]) 214 elif host_machine.system() == 'windows' 215 ft2_sources += files(['builds/windows/ftsystem.c',]) 216 else 217 if has_unistd_h and has_fcntl_h and has_sys_mman_h 218 # This version of `ftsystem.c` uses `mmap` to read input font files. 219 ft2_sources += files(['builds/unix/ftsystem.c',]) 220 use_unix_ftsystem_c = true 221 elif mmap_option.enabled() 222 error('mmap was enabled via options but is not available,' 223 + ' required headers were not found!') 224 else 225 ft2_sources += files(['src/base/ftsystem.c',]) 226 endif 227 endif 228 229 230 # Debug support file 231 # 232 # NOTE: Some specialized versions exist for other platforms not supported by 233 # Meson. Most implementation differences are extremely minor, i.e., in the 234 # implementation of `FT_Message` and `FT_Panic`, and getting the `FT2_DEBUG` 235 # value from the environment, when this is supported. A smaller refactor 236 # might make these platform-specific files much smaller, and could be moved 237 # into `ftsystem.c` as well. 238 239 if host_machine.system() == 'windows' 240 winmod = import('windows') 241 ft2_sources += [ 242 'builds/windows/ftdebug.c', 243 winmod.compile_resources('src/base/ftver.rc'), 244 ] 245 else 246 ft2_sources += 'src/base/ftdebug.c' 247 endif 248 249 250 ft2_deps = [] 251 common_ldflags = [] 252 253 254 # Correct compatibility version for OS x. 255 # 256 # OSX sets the compatibility_version (aka libtools version) differently from 257 # the library name. 258 259 if host_machine.system() == 'darwin' 260 # maintain compatibility with autotools on macOS 261 common_ldflags = [ 262 '-compatibility_version', ft2_pkgconfig_version.split('.')[0], 263 '-current_version', ft2_pkgconfig_version 264 ] 265 endif 266 267 268 # Generate `ftoption.h` based on available dependencies. 269 270 process_header_command = [python_exe, 271 files('builds/meson/process_ftoption_h.py'), 272 '@INPUT@', '--output=@OUTPUT@'] 273 274 ftoption_command = process_header_command 275 276 # External GZip support. 277 zlib_option = get_option('zlib') 278 279 # Backward-compatible aliases. 280 if zlib_option == 'disabled' 281 zlib_option = 'none' 282 elif zlib_option == 'enabled' 283 zlib_option = 'auto' 284 endif 285 286 if zlib_option == 'auto' 287 # First try to find a system installation, otherwise fall back to 288 # the subproject. 289 zlib_dep = dependency('zlib', 290 required: false) 291 if zlib_dep.found() 292 zlib_option = 'system' 293 else 294 zlib_option = 'external' 295 endif 296 endif 297 298 if zlib_option == 'none' 299 ftoption_command += [ '--disable=FT_CONFIG_OPTION_USE_ZLIB' ] 300 elif zlib_option == 'internal' 301 ftoption_command += [ '--enable=FT_CONFIG_OPTION_USE_ZLIB' ] 302 elif zlib_option == 'external' 303 ftoption_command += [ '--enable=FT_CONFIG_OPTION_USE_ZLIB' ] 304 zlib_project = subproject('zlib', 305 required: true, 306 default_options: 'default_library=static') 307 zlib_dep = zlib_project.get_variable('zlib_dep') 308 ft2_deps += [zlib_dep] 309 elif zlib_option == 'system' 310 zlib_dep = dependency('zlib', 311 required: true) 312 assert(zlib_dep.found(), 'Could not find system zlib installation!') 313 ftoption_command += [ 314 '--enable=FT_CONFIG_OPTION_USE_ZLIB', 315 '--enable=FT_CONFIG_OPTION_SYSTEM_ZLIB', 316 ] 317 ft2_deps += [zlib_dep] 318 else 319 assert(false, 'Invalid zlib option ' + zlib_option) 320 endif 321 322 # BZip2 support. 323 bzip2_dep = dependency( 324 'bzip2', 325 required: get_option('bzip2').disabled() ? get_option('bzip2') : false, 326 ) 327 if not bzip2_dep.found() 328 bzip2_dep = cc.find_library( 329 'bz2', 330 has_headers: ['bzlib.h'], 331 required: get_option('bzip2'), 332 ) 333 endif 334 335 if bzip2_dep.found() 336 ftoption_command += ['--enable=FT_CONFIG_OPTION_USE_BZIP2'] 337 ft2_sources += files(['src/bzip2/ftbzip2.c',]) 338 ft2_deps += [bzip2_dep] 339 endif 340 341 # PNG support. 342 libpng_dep = dependency('libpng', 343 required: get_option('png'), 344 fallback: 'libpng') 345 346 if libpng_dep.found() 347 ftoption_command += ['--enable=FT_CONFIG_OPTION_USE_PNG'] 348 ft2_deps += [libpng_dep] 349 endif 350 351 # HarfBuzz support. 352 harfbuzz_opt = get_option('harfbuzz') 353 harfbuzz_dep = disabler() 354 355 if harfbuzz_opt == 'enabled' or harfbuzz_opt == 'auto' 356 harfbuzz_dep = dependency('harfbuzz', 357 version: '>= 2.0.0', 358 required: harfbuzz_opt == 'enabled', 359 default_options: ['freetype=disabled']) 360 if harfbuzz_dep.found() 361 harfbuzz_opt = 'YES' 362 ftoption_command += ['--enable=FT_CONFIG_OPTION_USE_HARFBUZZ'] 363 ft2_deps += [harfbuzz_dep] 364 endif 365 endif 366 367 if not harfbuzz_dep.found() and \ 368 (harfbuzz_opt == 'dynamic' or harfbuzz_opt == 'auto') 369 # On Windows we don't need libdl, but on other platforms we need it. 370 if host_machine.system() == 'windows' 371 harfbuzz_opt = 'dynamic' 372 ftoption_command += [ 373 '--enable=FT_CONFIG_OPTION_USE_HARFBUZZ', 374 '--enable=FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC', 375 ] 376 else 377 harfbuzz_dep = meson.get_compiler('c').find_library('dl', 378 required: harfbuzz_opt == 'dynamic', 379 ) 380 if harfbuzz_dep.found() 381 harfbuzz_opt = 'dynamic' 382 ftoption_command += [ 383 '--enable=FT_CONFIG_OPTION_USE_HARFBUZZ', 384 '--enable=FT_CONFIG_OPTION_USE_HARFBUZZ_DYNAMIC', 385 ] 386 ft2_deps += [harfbuzz_dep] 387 endif 388 endif 389 endif 390 391 if harfbuzz_opt == 'disabled' or harfbuzz_opt == 'auto' 392 harfbuzz_opt = 'NO' 393 endif 394 395 # Brotli decompression support. 396 brotli_dep = dependency('libbrotlidec', 397 required: get_option('brotli')) 398 399 if brotli_dep.found() 400 ftoption_command += ['--enable=FT_CONFIG_OPTION_USE_BROTLI'] 401 ft2_deps += [brotli_dep] 402 endif 403 404 if get_option('error_strings') 405 ftoption_command += ['--enable=FT_CONFIG_OPTION_ERROR_STRINGS'] 406 endif 407 408 # We can now generate `ftoption.h`. 409 ftoption_h = custom_target('ftoption.h', 410 input: 'include/freetype/config/ftoption.h', 411 output: 'ftoption.h', 412 command: ftoption_command, 413 install: true, 414 install_dir: join_paths(freetype_includedir, 'freetype/config'), 415 ) 416 ft2_sources += ftoption_h 417 ft2_defines += ['-DFT_CONFIG_OPTIONS_H=<ftoption.h>'] 418 419 if host_machine.system() == 'windows' and \ 420 get_option('default_library') == 'shared' 421 ft2_defines += ['-DDLL_EXPORT=1'] 422 endif 423 424 425 # Generate `ftconfig.h`. 426 427 ftconfig_command = process_header_command 428 429 if has_unistd_h 430 ftconfig_command += '--enable=HAVE_UNISTD_H' 431 endif 432 if has_fcntl_h 433 ftconfig_command += '--enable=HAVE_FCNTL_H' 434 endif 435 436 if use_unix_ftsystem_c 437 ftconfig_h_in = files('builds/unix/ftconfig.h.in') 438 ftconfig_h = custom_target('ftconfig.h', 439 input: ftconfig_h_in, 440 output: 'ftconfig.h', 441 command: ftconfig_command, 442 install: true, 443 install_dir: join_paths(freetype_includedir, 'freetype/config'), 444 ) 445 ft2_sources += ftconfig_h 446 ft2_defines += ['-DFT_CONFIG_CONFIG_H=<ftconfig.h>'] 447 endif 448 449 450 ft2_lib = library('freetype', 451 sources: ft2_sources + [ftmodule_h], 452 c_args: ft2_defines, 453 gnu_symbol_visibility: 'hidden', 454 include_directories: ft2_includes, 455 dependencies: ft2_deps, 456 install: true, 457 version: ft2_so_version, 458 link_args: common_ldflags, 459 ) 460 461 462 # To be used by other projects including this one via `subproject`. 463 464 freetype_dep = declare_dependency( 465 include_directories: ft2_includes, 466 link_with: ft2_lib, 467 version: ft2_pkgconfig_version) 468 469 meson.override_dependency('freetype2', freetype_dep) 470 471 472 # NOTE: Using both `install_dir` and `subdir` doesn't seem to work 473 # below, i.e., the `subdir` value seems to be ignored, contrary to 474 # examples in the Meson documentation. 475 install_headers('include/ft2build.h', 476 install_dir: freetype_includedir) 477 install_headers(ft2_public_headers, 478 install_dir: join_paths(freetype_includedir, 'freetype')) 479 install_headers(ft2_config_headers, 480 install_dir: join_paths(freetype_includedir, 'freetype/config')) 481 482 483 install_data('builds/unix/freetype2.m4', 484 install_dir: freetype_aclocaldir) 485 486 487 pkgconfig = import('pkgconfig') 488 489 pkgconfig.generate(ft2_lib, 490 filebase: 'freetype2', 491 name: 'FreeType 2', 492 description: 'A free, high-quality, and portable font engine.', 493 url: 'https://freetype.org', 494 subdirs: 'freetype2', 495 version: ft2_pkgconfig_version, 496 ) 497 498 if get_option('tests').enabled() 499 subdir('tests') 500 endif 501 502 503 # NOTE: Unlike the old `make refdoc` command, this generates the 504 # documentation under `$BUILD/docs/` since Meson doesn't support modifying 505 # the source root directory (which is a good thing). 506 507 gen_docs = custom_target('freetype2 reference documentation', 508 output: 'docs', 509 input: ft2_public_headers + ft2_config_headers, 510 command: [python_exe, 511 files('builds/meson/generate_reference_docs.py'), 512 '--version=' + meson.project_version(), 513 '--input-dir=' + meson.current_source_dir(), 514 '--output-dir=@OUTPUT@' 515 ], 516 ) 517 518 519 summary({'OS': host_machine.system(), 520 }, section: 'Operating System') 521 522 summary({'Zlib': zlib_option, 523 'Bzip2': bzip2_dep.found(), 524 'Png': libpng_dep.found(), 525 'HarfBuzz': harfbuzz_opt, 526 'Brotli': brotli_dep.found(), 527 }, bool_yn: true, section: 'Used Libraries') 528 529 # EOF