meson.build 18 KB

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