1project('harfbuzz', 'c', 'cpp', 2 meson_version: '>= 0.55.0', 3 version: '8.2.2', 4 default_options: [ 5 'cpp_eh=none', # Just to support msvc, we are passing -fno-exceptions also anyway 6 # 'cpp_rtti=false', # Do NOT enable, wraps inherit it and ICU needs RTTI 7 'cpp_std=c++11', 8 'wrap_mode=nofallback', # Use --wrap-mode=default to revert, https://github.com/harfbuzz/harfbuzz/pull/2548 9 ], 10) 11 12hb_version_arr = meson.project_version().split('.') 13hb_version_major = hb_version_arr[0].to_int() 14hb_version_minor = hb_version_arr[1].to_int() 15hb_version_micro = hb_version_arr[2].to_int() 16 17# libtool versioning 18hb_version_int = 60000 + hb_version_major*100 + hb_version_minor*10 + hb_version_micro 19hb_libtool_version_info = '@0@:0:@0@'.format(hb_version_int) 20 21pkgmod = import('pkgconfig') 22cpp = meson.get_compiler('cpp') 23null_dep = dependency('', required: false) 24 25# Enforce C++14 requirement for MSVC STL 26if ['clang', 'clang-cl'].contains(cpp.get_id()) and cpp.get_define('_MSC_FULL_VER') != '' 27 add_project_arguments('-std=c++14', language: 'cpp') 28endif 29 30if cpp.get_argument_syntax() == 'msvc' 31 # Ignore several spurious warnings for things HarfBuzz does very commonly. 32 # If a warning is completely useless and spammy, use '/wdXXXX' to suppress it 33 # If a warning is harmless but hard to fix, use '/woXXXX' so it's shown once 34 # NOTE: Only add warnings here if you are sure they're spurious 35 msvc_args = [ 36 '/wd4244', # lossy type conversion (e.g. double -> int) 37 cpp.get_supported_arguments(['/utf-8']), # set the input encoding to utf-8 38 ] 39 add_project_arguments(msvc_args, language: ['c', 'cpp']) 40 # Disable SAFESEH with MSVC for libs that use external deps that are built with MinGW 41 # noseh_link_args = ['/SAFESEH:NO'] 42endif 43 44add_project_link_arguments(cpp.get_supported_link_arguments([ 45 '-Bsymbolic-functions' 46]), language: 'c') 47 48add_project_arguments(cpp.get_supported_arguments([ 49 '-fno-exceptions', 50 '-fno-rtti', 51 '-fno-threadsafe-statics', 52 '-fvisibility-inlines-hidden', 53]), language: 'cpp') 54 55if host_machine.cpu_family() == 'arm' and cpp.alignment('struct { char c; }') != 1 56 if cpp.has_argument('-mstructure-size-boundary=8') 57 add_project_arguments('-mstructure-size-boundary=8', language: 'cpp') 58 endif 59endif 60 61if host_machine.system() == 'windows' 62 add_project_arguments(cpp.get_supported_arguments([ 63 '-Wa,-mbig-obj' 64 ]), language : 'cpp') 65endif 66 67check_headers = [ 68 ['unistd.h'], 69 ['sys/mman.h'], 70 ['stdbool.h'], 71 ['xlocale.h'], 72] 73 74check_funcs = [ 75 ['atexit'], 76 ['mprotect'], 77 ['sysconf'], 78 ['getpagesize'], 79 ['mmap'], 80 ['isatty'], 81 ['uselocale'], 82 ['newlocale'], 83 ['sincosf'], 84] 85 86m_dep = cpp.find_library('m', required: false) 87 88if meson.version().version_compare('>=0.60.0') 89 # pkg-config: freetype2, cmake: Freetype 90 freetype_dep = dependency('freetype2', 'Freetype', 91 required: get_option('freetype'), 92 default_options: ['harfbuzz=disabled'], 93 allow_fallback: true) 94else 95 # painful hack to handle multiple dependencies but also respect options 96 freetype_opt = get_option('freetype') 97 # we want to handle enabled manually after fallbacks, but also handle disabled normally 98 if freetype_opt.enabled() 99 freetype_opt = false 100 endif 101 # try pkg-config name 102 freetype_dep = dependency('freetype2', method: 'pkg-config', required: freetype_opt) 103 # when disabled, leave it not-found 104 if not freetype_dep.found() and not get_option('freetype').disabled() 105 # Try cmake name 106 freetype_dep = dependency('Freetype', method: 'cmake', required: false) 107 # Subproject fallback, `allow_fallback: true` means the fallback will be 108 # tried even if the freetype option is set to `auto`. 109 if not freetype_dep.found() 110 freetype_dep = dependency('freetype2', 111 method: 'pkg-config', 112 required: get_option('freetype'), 113 default_options: ['harfbuzz=disabled'], 114 allow_fallback: true) 115 endif 116 endif 117endif 118 119glib_dep = dependency('glib-2.0', required: get_option('glib')) 120gobject_dep = dependency('gobject-2.0', required: get_option('gobject')) 121graphite2_dep = dependency('graphite2', required: get_option('graphite2')) 122graphite_dep = dependency('graphite2', required: get_option('graphite')) 123wasm_dep = cpp.find_library('iwasm', required: get_option('wasm')) 124# How to check whether iwasm was built, and hence requires, LLVM? 125#llvm_dep = cpp.find_library('LLVM-15', required: get_option('wasm')) 126 127if meson.version().version_compare('>=0.60.0') 128 # pkg-config: icu-uc, cmake: ICU but with components 129 icu_dep = dependency('icu-uc', 'ICU', 130 components: 'uc', 131 required: get_option('icu'), 132 allow_fallback: true) 133else 134 # painful hack to handle multiple dependencies but also respect options 135 icu_opt = get_option('icu') 136 # we want to handle enabled manually after fallbacks, but also handle disabled normally 137 if icu_opt.enabled() 138 icu_opt = false 139 endif 140 # try pkg-config name 141 icu_dep = dependency('icu-uc', method: 'pkg-config', required: icu_opt) 142 # when disabled, leave it not-found 143 if not icu_dep.found() and not get_option('icu').disabled() 144 # Try cmake name 145 icu_dep = dependency('ICU', method: 'cmake', components: 'uc', required: false) 146 # Try again with subproject fallback. `allow_fallback: true` means the 147 # fallback will be tried even if the icu option is set to `auto`, but 148 # we cannot pass this option until Meson 0.59.0, because no wrap file 149 # is checked into git. 150 if not icu_dep.found() 151 icu_dep = dependency('icu-uc', 152 method: 'pkg-config', 153 required: get_option('icu')) 154 endif 155 endif 156endif 157 158if icu_dep.found() and icu_dep.type_name() == 'pkgconfig' 159 icu_defs = icu_dep.get_variable(pkgconfig: 'DEFS', default_value: '').split() 160 if icu_defs.length() > 0 161 add_project_arguments(icu_defs, language: ['c', 'cpp']) 162 endif 163endif 164 165cairo_dep = null_dep 166cairo_ft_dep = null_dep 167if not get_option('cairo').disabled() 168 cairo_dep = dependency('cairo', required: false) 169 cairo_ft_dep = dependency('cairo-ft', required: false) 170 171 if (not cairo_dep.found() and 172 cpp.get_argument_syntax() == 'msvc' and 173 cpp.has_header('cairo.h')) 174 cairo_dep = cpp.find_library('cairo', required: false) 175 if cairo_dep.found() and cpp.has_function('cairo_ft_font_face_create_for_ft_face', 176 prefix: '#include <cairo-ft.h>', 177 dependencies: cairo_dep) 178 cairo_ft_dep = cairo_dep 179 endif 180 endif 181 182 if not cairo_dep.found() 183 # Note that we don't have harfbuzz -> cairo -> freetype2 -> harfbuzz fallback 184 # dependency cycle here because we have configured freetype2 above with 185 # harfbuzz support disabled, so when cairo will lookup freetype2 dependency 186 # it will be forced to use that one. 187 cairo_dep = dependency('cairo', required: get_option('cairo')) 188 cairo_ft_required = get_option('cairo').enabled() and get_option('freetype').enabled() 189 cairo_ft_dep = dependency('cairo-ft', required: cairo_ft_required) 190 endif 191endif 192 193chafa_dep = dependency('chafa', version: '>= 1.6.0', required: get_option('chafa')) 194 195conf = configuration_data() 196incconfig = include_directories('.') 197 198add_project_arguments('-DHAVE_CONFIG_H', language: ['c', 'cpp']) 199 200warn_cflags = [ 201 '-Wno-non-virtual-dtor', 202] 203 204cpp_args = cpp.get_supported_arguments(warn_cflags) 205 206if glib_dep.found() 207 conf.set('HAVE_GLIB', 1) 208endif 209 210if gobject_dep.found() 211 conf.set('HAVE_GOBJECT', 1) 212endif 213 214if cairo_dep.found() 215 conf.set('HAVE_CAIRO', 1) 216 check_cairo_funcs = [ 217 ['cairo_user_font_face_set_render_color_glyph_func', {'deps': cairo_dep}], 218 ['cairo_font_options_get_custom_palette_color', {'deps': cairo_dep}], 219 ['cairo_user_scaled_font_get_foreground_source', {'deps': cairo_dep}], 220 ] 221 222 if cairo_dep.type_name() == 'internal' 223 foreach func: check_cairo_funcs 224 name = func[0] 225 conf.set('HAVE_@0@'.format(name.to_upper()), 1) 226 endforeach 227 else 228 check_funcs += check_cairo_funcs 229 endif 230endif 231 232if cairo_ft_dep.found() 233 conf.set('HAVE_CAIRO_FT', 1) 234endif 235 236if chafa_dep.found() 237 conf.set('HAVE_CHAFA', 1) 238endif 239 240if wasm_dep.found() 241 conf.set('HAVE_WASM', 1) 242 conf.set('HB_WASM_MODULE_DIR', '"'+get_option('prefix')+'/'+get_option('libdir')+'/harfbuzz/wasm"') 243endif 244 245if graphite2_dep.found() or graphite_dep.found() 246 conf.set('HAVE_GRAPHITE2', 1) 247endif 248 249if icu_dep.found() 250 conf.set('HAVE_ICU', 1) 251endif 252 253if get_option('icu_builtin') 254 conf.set('HAVE_ICU_BUILTIN', 1) 255endif 256 257if get_option('experimental_api') 258 conf.set('HB_EXPERIMENTAL_API', 1) 259endif 260 261if freetype_dep.found() 262 conf.set('HAVE_FREETYPE', 1) 263 check_freetype_funcs = [ 264 ['FT_Get_Var_Blend_Coordinates', {'deps': freetype_dep}], 265 ['FT_Set_Var_Blend_Coordinates', {'deps': freetype_dep}], 266 ['FT_Done_MM_Var', {'deps': freetype_dep}], 267 ['FT_Get_Transform', {'deps': freetype_dep}], 268 ] 269 270 if freetype_dep.type_name() == 'internal' 271 foreach func: check_freetype_funcs 272 name = func[0] 273 conf.set('HAVE_@0@'.format(name.to_upper()), 1) 274 endforeach 275 else 276 check_funcs += check_freetype_funcs 277 endif 278endif 279 280gdi_uniscribe_deps = [] 281# GDI (Uniscribe) (Windows) 282if host_machine.system() == 'windows' and not get_option('gdi').disabled() 283 if (get_option('directwrite').enabled() and 284 not (cpp.has_header('usp10.h') and cpp.has_header('windows.h'))) 285 error('GDI/Uniscribe was enabled explicitly, but required headers are missing.') 286 endif 287 288 gdi_deps_found = true 289 foreach usplib : ['usp10', 'gdi32', 'rpcrt4'] 290 dep = cpp.find_library(usplib, required: get_option('gdi')) 291 gdi_deps_found = gdi_deps_found and dep.found() 292 gdi_uniscribe_deps += dep 293 endforeach 294 295 if gdi_deps_found 296 conf.set('HAVE_UNISCRIBE', 1) 297 conf.set('HAVE_GDI', 1) 298 endif 299endif 300 301# DirectWrite (Windows) 302if host_machine.system() == 'windows' and not get_option('directwrite').disabled() 303 if get_option('directwrite').enabled() and not cpp.has_header('dwrite_1.h') 304 error('DirectWrite was enabled explicitly, but required header is missing.') 305 endif 306 307 conf.set('HAVE_DIRECTWRITE', 1) 308endif 309 310# CoreText (macOS) 311coretext_deps = [] 312if host_machine.system() == 'darwin' and not get_option('coretext').disabled() 313 app_services_dep = dependency('appleframeworks', modules: ['ApplicationServices'], required: false) 314 if cpp.has_type('CTFontRef', prefix: '#include <ApplicationServices/ApplicationServices.h>', dependencies: app_services_dep) 315 coretext_deps += [app_services_dep] 316 conf.set('HAVE_CORETEXT', 1) 317 # On iOS CoreText and CoreGraphics are stand-alone frameworks 318 # Check for a different symbol to avoid getting cached result 319 else 320 coretext_dep = dependency('appleframeworks', modules: ['CoreText'], required: false) 321 coregraphics_dep = dependency('appleframeworks', modules: ['CoreGraphics'], required: false) 322 corefoundation_dep = dependency('appleframeworks', modules: ['CoreFoundation'], required: false) 323 if cpp.has_type('CTRunRef', prefix: '#include <CoreText/CoreText.h>', dependencies: [coretext_dep, coregraphics_dep, corefoundation_dep]) 324 coretext_deps += [coretext_dep, coregraphics_dep, corefoundation_dep] 325 conf.set('HAVE_CORETEXT', 1) 326 elif get_option('coretext').enabled() 327 error('CoreText was enabled explicitly, but required headers or frameworks are missing.') 328 endif 329 endif 330endif 331 332# threads 333thread_dep = null_dep 334if host_machine.system() != 'windows' 335 thread_dep = dependency('threads', required: false) 336 337 if thread_dep.found() 338 conf.set('HAVE_PTHREAD', 1) 339 endif 340endif 341 342conf.set_quoted('PACKAGE_NAME', 'HarfBuzz') 343conf.set_quoted('PACKAGE_VERSION', meson.project_version()) 344 345foreach check : check_headers 346 name = check[0] 347 348 if cpp.has_header(name) 349 conf.set('HAVE_@0@'.format(name.to_upper().underscorify()), 1) 350 endif 351endforeach 352 353harfbuzz_extra_deps = [] 354foreach check : check_funcs 355 name = check[0] 356 opts = check.get(1, {}) 357 link_withs = opts.get('link_with', []) 358 check_deps = opts.get('deps', []) 359 extra_deps = [] 360 found = true 361 362 # First try without linking 363 found = cpp.has_function(name, dependencies: check_deps) 364 365 if not found and link_withs.length() > 0 366 found = true 367 368 foreach link_with : link_withs 369 dep = cpp.find_library(link_with, required: false) 370 if dep.found() 371 extra_deps += dep 372 else 373 found = false 374 endif 375 endforeach 376 377 if found 378 found = cpp.has_function(name, dependencies: check_deps + extra_deps) 379 endif 380 endif 381 382 if found 383 harfbuzz_extra_deps += extra_deps 384 conf.set('HAVE_@0@'.format(name.to_upper()), 1) 385 endif 386endforeach 387 388# CMake support (package install dir) 389 390# Equivalent to configure_package_config_file(INSTALL_DESTINATION ...), see 391# https://cmake.org/cmake/help/latest/module/CMakePackageConfigHelpers.html#command:configure_package_config_file. 392# In certain unusual packaging layouts such as Nixpkgs, the Harfbuzz package 393# is installed into two Nix store paths, "out" and "dev", where "out" contains 394# libraries only (i.e. lib/libharfbuzz.so) and "dev" contains development 395# files, i.e. include and lib/cmake. If CMake package files are installed to 396# "out", Nixpkgs will move them to "dev", which breaks assumptions about 397# our file paths. Since we need to figure out relative install paths here 398# to make a relocatable package, we do need to know the final path of our 399# CMake files to calculate the correct relative paths. 400# Of course, this still defaults to $libdir/cmake if unset, which works for 401# most packaging layouts. 402cmake_package_install_dir = get_option('cmakepackagedir') 403 404if cmake_package_install_dir == '' 405 cmake_package_install_dir = get_option('libdir') / 'cmake' 406endif 407 408subdir('src') 409 410if not get_option('utilities').disabled() 411 subdir('util') 412endif 413 414if not get_option('tests').disabled() 415 subdir('test') 416endif 417 418if not get_option('benchmark').disabled() 419 subdir('perf') 420endif 421 422if not get_option('docs').disabled() 423 subdir('docs') 424endif 425 426configure_file(output: 'config.h', configuration: conf) 427 428alias_target('lib', libharfbuzz) 429alias_target('libs', libharfbuzz, libharfbuzz_subset) 430 431build_summary = { 432 'Directories': 433 {'prefix': get_option('prefix'), 434 'bindir': get_option('bindir'), 435 'libdir': get_option('libdir'), 436 'includedir': get_option('includedir'), 437 'datadir': get_option('datadir'), 438 'cmakepackagedir': cmake_package_install_dir 439 }, 440 'Unicode callbacks (you want at least one)': 441 {'Builtin': true, 442 'Glib': conf.get('HAVE_GLIB', 0) == 1, 443 'ICU': conf.get('HAVE_ICU', 0) == 1, 444 }, 445 'Font callbacks (the more the merrier)': 446 {'Builtin' : true, 447 'FreeType': conf.get('HAVE_FREETYPE', 0) == 1, 448 }, 449 'Dependencies used for command-line utilities': 450 {'Cairo': conf.get('HAVE_CAIRO', 0) == 1, 451 'Chafa': conf.get('HAVE_CHAFA', 0) == 1, 452 }, 453 'Additional shapers': 454 {'Graphite2': conf.get('HAVE_GRAPHITE2', 0) == 1, 455 'WebAssembly (experimental)': conf.get('HAVE_WASM', 0) == 1, 456 }, 457 'Platform shapers (not normally needed)': 458 {'CoreText': conf.get('HAVE_CORETEXT', 0) == 1, 459 'DirectWrite (experimental)': conf.get('HAVE_DIRECTWRITE', 0) == 1, 460 'GDI/Uniscribe': (conf.get('HAVE_GDI', 0) == 1) and (conf.get('HAVE_UNISCRIBE', 0) == 1), 461 }, 462 'Other features': 463 {'Documentation': conf.get('HAVE_GTK_DOC', 0) == 1, 464 'GObject bindings': conf.get('HAVE_GOBJECT', 0) == 1, 465 'Cairo integration': conf.get('HAVE_CAIRO', 0) == 1, 466 'Introspection': conf.get('HAVE_INTROSPECTION', 0) == 1, 467 'Experimental APIs': conf.get('HB_EXPERIMENTAL_API', 0) == 1, 468 }, 469 'Testing': 470 {'Tests': get_option('tests').enabled(), 471 'Benchmark': get_option('benchmark').enabled(), 472 }, 473} 474foreach section_title, section : build_summary 475 summary(section, bool_yn: true, section: section_title) 476endforeach 477