1project('fontconfig', 'c',
2  version: '2.15.0',
3  meson_version : '>= 0.60.0',
4  default_options: [ 'buildtype=debugoptimized'],
5)
6
7fc_version = meson.project_version()
8version_arr = fc_version.split('.')
9fc_version_major = version_arr[0].to_int()
10fc_version_minor = version_arr[1].to_int()
11fc_version_micro = version_arr[2].to_int()
12
13# Try and maintain compatibility with the previous libtool versioning
14# (this is a bit of a hack, but it should work fine for our case where
15# API is added, in which case LT_AGE and LIBT_CURRENT are both increased)
16soversion = fc_version_major - 1
17curversion = fc_version_minor - 1
18libversion = '@0@.@1@.0'.format(soversion, curversion)
19defversion = '@0@.@1@'.format(curversion, fc_version_micro)
20osxversion = curversion + 1
21
22freetype_req = '>= 21.0.15'
23freetype_req_cmake = '>= 2.8.1'
24
25cc = meson.get_compiler('c')
26
27
28freetype_dep = dependency('freetype2', method: 'pkg-config', version: freetype_req, required: false)
29
30# Give another shot using CMake
31if not freetype_dep.found()
32  freetype_dep = dependency('freetype', method: 'cmake', version: freetype_req_cmake,
33    fallback: ['freetype2', 'freetype_dep'], default_options: 'werror=false')
34endif
35
36# Linking expat should not be so difficult... see: https://github.com/mesonbuild/meson/issues/10516
37expat_dep = dependency('expat', required: false)
38if not expat_dep.found()
39  expat_dep = cc.find_library('expat', required : false)
40  if not expat_dep.found()
41    expat_dep = dependency('expat', method: 'system', fallback: ['expat', 'expat_dep'])
42  endif
43endif
44
45i18n = import('i18n')
46pkgmod = import('pkgconfig')
47python3 = import('python').find_installation()
48
49check_headers = [
50  ['dirent.h'],
51  ['fcntl.h'],
52  ['stdlib.h'],
53  ['string.h'],
54  ['unistd.h'],
55  ['sys/statvfs.h'],
56  ['sys/vfs.h'],
57  ['sys/statfs.h'],
58  ['sys/param.h'],
59  ['sys/mount.h'],
60]
61
62check_funcs = [
63  ['link'],
64  ['mkstemp'],
65  ['mkostemp'],
66  ['_mktemp_s'],
67  ['mkdtemp'],
68  ['getopt'],
69  ['getopt_long'],
70  ['getprogname'],
71  ['getexecname'],
72  ['rand'],
73  ['random'],
74  ['lrand48'],
75  ['random_r'],
76  ['rand_r'],
77  ['readlink'],
78  ['fstatvfs'],
79  ['fstatfs'],
80  ['lstat'],
81  ['mmap'],
82  ['vprintf'],
83]
84
85check_freetype_funcs = [
86  ['FT_Get_BDF_Property', {'dependencies': freetype_dep}],
87  ['FT_Get_PS_Font_Info', {'dependencies': freetype_dep}],
88  ['FT_Has_PS_Glyph_Names', {'dependencies': freetype_dep}],
89  ['FT_Get_X11_Font_Format', {'dependencies': freetype_dep}],
90  ['FT_Done_MM_Var', {'dependencies': freetype_dep}],
91]
92
93check_header_symbols = [
94  ['posix_fadvise', 'fcntl.h']
95]
96
97check_struct_members = [
98  ['struct statvfs', 'f_basetype', ['sys/statvfs.h']],
99  ['struct statvfs', 'f_fstypename', ['sys/statvfs.']],
100  ['struct statfs', 'f_flags', []],
101  ['struct statfs', 'f_fstypename', []],
102  ['struct dirent', 'd_type', ['sys/types.h', 'dirent.h']],
103]
104
105check_sizeofs = [
106  ['void *', {'conf-name': 'SIZEOF_VOID_P'}],
107]
108
109check_alignofs = [
110  ['void *', {'conf-name': 'ALIGNOF_VOID_P'}],
111  ['double'],
112]
113
114add_project_arguments('-DHAVE_CONFIG_H', language: 'c')
115
116c_args = []
117
118conf = configuration_data()
119deps = [freetype_dep, expat_dep]
120incbase = include_directories('.')
121
122# We cannot try compiling against an internal dependency
123if freetype_dep.type_name() == 'internal'
124  foreach func: check_freetype_funcs
125    name = func[0]
126    conf.set('HAVE_@0@'.format(name.to_upper()), 1)
127  endforeach
128else
129  check_funcs += check_freetype_funcs
130endif
131
132foreach check : check_headers
133  name = check[0]
134
135  if cc.has_header(name)
136    conf.set('HAVE_@0@'.format(name.to_upper().underscorify()), 1)
137  endif
138endforeach
139
140foreach check : check_funcs
141  name = check[0]
142  opts = check.length() > 1 ? check[1] : {}
143  extra_deps = opts.get('dependencies', [])
144
145  if cc.has_function(name, dependencies: extra_deps)
146    conf.set('HAVE_@0@'.format(name.to_upper()), 1)
147  endif
148endforeach
149
150foreach check : check_header_symbols
151  name = check[0]
152  header = check[1]
153
154  if cc.has_header_symbol(header, name)
155    conf.set('HAVE_@0@'.format(name.to_upper()), 1)
156  endif
157endforeach
158
159foreach check : check_struct_members
160  struct_name = check[0]
161  member_name = check[1]
162  headers = check[2]
163
164  prefix = ''
165
166  foreach header : headers
167    prefix += '#include <@0@>\n'.format(header)
168  endforeach
169
170  if cc.has_member(struct_name, member_name, prefix: prefix)
171    conf.set('HAVE_@0@_@1@'.format(struct_name, member_name).to_upper().underscorify(), 1)
172  endif
173endforeach
174
175foreach check : check_sizeofs
176  type = check[0]
177  opts = check.length() > 1 ? check[1] : {}
178
179  conf_name = opts.get('conf-name', 'SIZEOF_@0@'.format(type.to_upper()))
180
181  conf.set(conf_name, cc.sizeof(type))
182endforeach
183
184foreach check : check_alignofs
185  type = check[0]
186  opts = check.length() > 1 ? check[1] : {}
187
188  conf_name = opts.get('conf-name', 'ALIGNOF_@0@'.format(type.to_upper()))
189
190  conf.set(conf_name, cc.alignment(type))
191endforeach
192
193if cc.compiles(files('meson-cc-tests/flexible-array-member-test.c'))
194  conf.set('FLEXIBLE_ARRAY_MEMBER', true)
195else
196  conf.set('FLEXIBLE_ARRAY_MEMBER', 1)
197endif
198
199if cc.links(files('meson-cc-tests/stdatomic-primitives-test.c'), name: 'stdatomic.h atomics')
200  conf.set('HAVE_STDATOMIC_PRIMITIVES', 1)
201endif
202
203if cc.links(files('meson-cc-tests/intel-atomic-primitives-test.c'), name: 'Intel atomics')
204  conf.set('HAVE_INTEL_ATOMIC_PRIMITIVES', 1)
205endif
206
207if cc.links(files('meson-cc-tests/solaris-atomic-operations.c'), name: 'Solaris atomic ops')
208  conf.set('HAVE_SOLARIS_ATOMIC_OPS', 1)
209endif
210
211
212# Check iconv support
213iconv_dep = []
214found_iconv = 0
215if host_machine.system() != 'windows'
216  iconv_dep = dependency('iconv', required: get_option('iconv'))
217  found_iconv = iconv_dep.found().to_int()
218else
219  if get_option('iconv').enabled()
220    warning('-Diconv was set but this is not functional on Windows.')
221  endif
222endif
223conf.set('USE_ICONV', found_iconv)
224deps += [iconv_dep]
225
226prefix = get_option('prefix')
227
228fonts_conf = configuration_data()
229
230default_fonts_dirs = get_option('default-fonts-dirs')
231if default_fonts_dirs == ['yes']
232  if host_machine.system() == 'windows'
233    fc_fonts_paths = ['WINDOWSFONTDIR', 'WINDOWSUSERFONTDIR']
234  elif host_machine.system() == 'darwin'
235    fc_fonts_paths = ['/System/Library/Fonts', '/Library/Fonts', '~/Library/Fonts', '/System/Library/Assets/com_apple_MobileAsset_Font3', '/System/Library/Assets/com_apple_MobileAsset_Font4']
236  else
237    fc_fonts_paths = ['/usr/share/fonts', '/usr/local/share/fonts']
238  endif
239else
240  fc_fonts_paths = default_fonts_dirs
241endif
242xml_path = ''
243escaped_xml_path = ''
244foreach p : fc_fonts_paths
245  s = '\t<dir>' + p + '</dir>\n'
246  xml_path += s
247  # No substitution method for string
248  s = '\\t<dir>' + p + '</dir>\\n'
249  escaped_xml_path += s
250endforeach
251conf.set_quoted('FC_DEFAULT_FONTS', escaped_xml_path)
252fonts_conf.set('FC_DEFAULT_FONTS', xml_path)
253
254# Add more fonts if available.  By default, add only the directories
255# with outline fonts; those with bitmaps can be added as desired in
256# local.conf or ~/.fonts.conf
257fc_add_fonts = []
258additional_fonts_dirs = get_option('additional-fonts-dirs')
259if additional_fonts_dirs == ['yes']
260  fs = import('fs')
261  foreach dir : ['/usr/X11R6/lib/X11', '/usr/X11/lib/X11', '/usr/lib/X11']
262    if fs.is_dir(dir / 'fonts')
263      fc_add_fonts += [dir / 'fonts']
264    endif
265  endforeach
266elif additional_fonts_dirs == ['no']
267  # nothing to do
268else
269  fc_add_fonts = additional_fonts_dirs
270endif
271xml_path = ''
272escaped_xml_path = ''
273foreach p : fc_add_fonts
274  s = '\t<dir>' + p + '</dir>\n'
275  xml_path += s
276  # No substitution method for string
277  s = '\\t<dir>' + p + '</dir>\\n'
278  escaped_xml_path += s
279endforeach
280conf.set_quoted('FC_FONTPATH', escaped_xml_path)
281fonts_conf.set('FC_FONTPATH', xml_path)
282
283fc_cachedir = get_option('cache-dir')
284if fc_cachedir in ['yes', 'no', 'default']
285  if host_machine.system() == 'windows'
286    fc_cachedir = 'LOCAL_APPDATA_FONTCONFIG_CACHE'
287  else
288    fc_cachedir = join_paths(prefix, get_option('localstatedir'), 'cache', meson.project_name())
289  endif
290endif
291
292if host_machine.system() != 'windows'
293  thread_dep = dependency('threads')
294  conf.set('HAVE_PTHREAD', 1)
295  deps += [thread_dep]
296endif
297
298fc_templatedir = get_option('template-dir')
299if fc_templatedir in ['default', 'yes', 'no']
300  fc_templatedir = prefix / get_option('datadir') / 'fontconfig/conf.avail'
301endif
302
303fc_baseconfigdir = get_option('baseconfig-dir')
304if fc_baseconfigdir in ['default', 'yes', 'no']
305  fc_baseconfigdir = prefix / get_option('sysconfdir') / 'fonts'
306endif
307
308fc_configdir = get_option('config-dir')
309if fc_configdir in ['default', 'yes', 'no']
310  fc_configdir = fc_baseconfigdir / 'conf.d'
311endif
312
313fc_xmldir = get_option('xml-dir')
314if fc_xmldir in ['default', 'yes', 'no']
315  fc_xmldir = prefix / get_option('datadir') / 'xml/fontconfig'
316endif
317
318conf.set_quoted('CONFIGDIR', fc_configdir)
319conf.set_quoted('FC_CACHEDIR', fc_cachedir)
320conf.set_quoted('FC_TEMPLATEDIR', fc_templatedir)
321conf.set_quoted('FONTCONFIG_PATH', fc_baseconfigdir)
322conf.set_quoted('FC_FONTPATH', '')
323
324fonts_conf.set('FC_FONTPATH', '')
325fonts_conf.set('FC_CACHEDIR', fc_cachedir)
326fonts_conf.set('CONFIGDIR', fc_configdir)
327# strip off fc_baseconfigdir prefix if that is the prefix
328if fc_configdir.startswith(fc_baseconfigdir + '/')
329  fonts_conf.set('CONFIGDIR', fc_configdir.split(fc_baseconfigdir + '/')[1])
330endif
331
332gperf = find_program('gperf', required: false)
333gperf_len_type = ''
334
335if gperf.found()
336  gperf_test_format = '''
337  #include <string.h>
338  const char * in_word_set(const char *, @0@);
339  @1@
340  '''
341  gperf_snippet = run_command(gperf, '-L', 'ANSI-C', files('meson-cc-tests/gperf.txt'),
342                              check: true).stdout()
343
344  foreach type : ['size_t', 'unsigned']
345    if cc.compiles(gperf_test_format.format(type, gperf_snippet))
346      gperf_len_type = type
347      break
348    endif
349  endforeach
350
351  if gperf_len_type == ''
352    error('unable to determine gperf len type')
353  endif
354else
355  # Fallback to subproject
356  gperf = find_program('gperf')
357  # assume if we are compiling from the wrap, the size is just size_t
358  gperf_len_type = 'size_t'
359endif
360
361message('gperf len type is @0@'.format(gperf_len_type))
362
363conf.set('FC_GPERF_SIZE_T', gperf_len_type,
364         description : 'The type of gperf "len" parameter')
365
366conf.set('_GNU_SOURCE', true)
367
368conf.set_quoted('GETTEXT_PACKAGE', meson.project_name())
369
370incsrc = include_directories('src')
371
372# We assume stdint.h is available
373foreach t : ['uint64_t', 'int32_t', 'uintptr_t', 'intptr_t']
374  if not cc.has_type(t, prefix: '#include <stdint.h>')
375    error('Sanity check failed: type @0@ not provided via stdint.h'.format(t))
376  endif
377endforeach
378
379fcstdint_h = configure_file(
380  input: 'src/fcstdint.h.in',
381  output: 'fcstdint.h',
382  copy: true)
383
384makealias = files('src/makealias.py')[0]
385
386alias_headers = custom_target('alias_headers',
387                              output: ['fcalias.h', 'fcaliastail.h'],
388                              input: ['fontconfig/fontconfig.h', 'src/fcdeprecate.h', 'fontconfig/fcprivate.h'],
389                              command: [python3, makealias, join_paths(meson.current_source_dir(), 'src'), '@OUTPUT@', '@INPUT@'],
390                             )
391
392ft_alias_headers = custom_target('ft_alias_headers',
393                                 output: ['fcftalias.h', 'fcftaliastail.h'],
394                                 input: ['fontconfig/fcfreetype.h'],
395                                 command: [python3, makealias, join_paths(meson.current_source_dir(), 'src'), '@OUTPUT@', '@INPUT@']
396                                )
397
398tools_man_pages = []
399
400# Do not reorder
401subdir('fc-case')
402subdir('fc-lang')
403subdir('src')
404
405if not get_option('tools').disabled()
406  subdir('fc-cache')
407  subdir('fc-cat')
408  subdir('fc-conflist')
409  subdir('fc-list')
410  subdir('fc-match')
411  subdir('fc-pattern')
412  subdir('fc-query')
413  subdir('fc-scan')
414  subdir('fc-validate')
415endif
416
417if not get_option('tests').disabled()
418  subdir('test')
419endif
420
421subdir('conf.d')
422subdir('its')
423
424# xgettext is optional (on Windows for instance)
425if find_program('xgettext', required : get_option('nls')).found()
426  subdir('po')
427  subdir('po-conf')
428endif
429
430if not get_option('doc').disabled()
431  subdir('doc')
432endif
433
434configure_file(output: 'config.h', configuration: conf)
435
436configure_file(output: 'fonts.conf',
437               input: 'fonts.conf.in',
438               configuration: fonts_conf,
439               install_dir: fc_baseconfigdir,
440               install: true)
441
442install_data('fonts.dtd',
443             install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'xml/fontconfig')
444            )
445
446fc_headers = [
447  'fontconfig/fontconfig.h',
448  'fontconfig/fcfreetype.h',
449  'fontconfig/fcprivate.h',
450]
451
452install_headers(fc_headers, subdir: meson.project_name())
453
454# Summary
455doc_targets = get_variable('doc_targets', [])
456
457summary({
458  'Documentation': (doc_targets.length() > 0 ? doc_targets : false),
459  'NLS': not get_option('nls').disabled(),
460  'Tests': not get_option('tests').disabled(),
461  'Tools': not get_option('tools').disabled(),
462  'iconv': found_iconv == 1,
463}, section: 'General', bool_yn: true, list_sep: ', ')
464summary({
465  'Hinting': preferred_hinting,
466  'Font directories': fc_fonts_paths,
467  'Additional font directories': fc_add_fonts,
468}, section: 'Defaults', bool_yn: true, list_sep: ', ')
469summary({
470  'Cache directory': fc_cachedir,
471  'Template directory': fc_templatedir,
472  'Base config directory': fc_baseconfigdir,
473  'Config directory': fc_configdir,
474  'XML directory': fc_xmldir,
475}, section: 'Paths', bool_yn: true, list_sep: ', ')
476