1project('pugl', ['c'],
2        version: '0.3.0',
3        license: 'ISC',
4        meson_version: '>= 0.49.2',
5        default_options: [
6          'c_std=c99',
7          'cpp_std=c++11',
8          'default_library=shared'
9        ])
10
11pugl_src_root = meson.current_source_dir()
12major_version = meson.project_version().split('.')[0]
13version_suffix = '-@0@'.format(major_version)
14versioned_name = 'pugl' + version_suffix
15
16# Load build tools
17pkg = import('pkgconfig')
18cc = meson.get_compiler('c')
19
20# Enable C++ support if we're building the examples
21if get_option('examples')
22  add_languages(['cpp'])
23  cpp = meson.get_compiler('cpp')
24endif
25
26# Enable Objective C support if we're building for MacOS
27if host_machine.system() == 'darwin'
28  add_languages(['objc'])
29  objcc = meson.get_compiler('objc')
30endif
31
32# Set ultra strict warnings for developers, if requested
33if get_option('strict')
34  subdir('meson')
35
36  # C warnings
37  c_warnings = all_c_warnings
38  if cc.get_id() == 'clang'
39    c_warnings += [
40      '-Wno-bad-function-cast',
41      '-Wno-documentation', # Cairo
42      '-Wno-documentation-unknown-command', # Cairo
43      '-Wno-float-equal',
44      '-Wno-implicit-fallthrough',
45      '-Wno-padded',
46      '-Wno-reserved-id-macro',
47      '-Wno-switch-default',
48      '-Wno-switch-enum',
49      '-Wno-unused-macros', # Mac
50    ]
51  elif cc.get_id() == 'gcc'
52    c_warnings += [
53      '-Wno-bad-function-cast',
54      '-Wno-float-equal',
55      '-Wno-inline',
56      '-Wno-padded',
57      '-Wno-pedantic',
58      '-Wno-suggest-attribute=const',
59      '-Wno-suggest-attribute=malloc',
60      '-Wno-suggest-attribute=pure',
61      '-Wno-switch-default',
62      '-Wno-switch-enum',
63      '-Wno-unsuffixed-float-constants',
64    ]
65  elif cc.get_id() == 'msvc'
66    c_warnings += [
67      '/wd4028',  # formal parameter different from declaration
68      '/wd4061',  # enumerator in switch is not explicitly handled
69      '/wd4191',  # unsafe conversion from type to type
70      '/wd4514',  # unreferenced inline function has been removed
71      '/wd4706',  # assignment within conditional expression
72      '/wd4710',  # function not inlined
73      '/wd4711',  # function selected for automatic inline expansion
74      '/wd4800',  # implicit conversion from int to bool
75      '/wd4820',  # padding added after construct
76      '/wd4996',  # function or variable may be unsafe
77      '/wd5045',  # will insert Spectre mitigation for memory load
78    ]
79  endif
80
81  add_project_arguments(cc.get_supported_arguments(c_warnings),
82                        language: ['c', 'objc'])
83
84  # C++ warnings
85  cpp_warnings = all_cpp_warnings
86  if is_variable('cpp')
87    if cpp.get_id() == 'clang'
88      cpp_warnings += [
89        '-Wno-documentation-unknown-command', # Cairo
90        '-Wno-old-style-cast',
91        '-Wno-padded',
92        '-Wno-reserved-id-macro',
93        '-Wno-switch-enum',
94        '-Wno-unused-macros', # Mac
95      ]
96    elif cpp.get_id() == 'gcc'
97      cpp_warnings += [
98        '-Wno-effc++',
99        '-Wno-inline',
100        '-Wno-old-style-cast',
101        '-Wno-padded',
102        '-Wno-suggest-attribute=const',
103        '-Wno-suggest-attribute=malloc',
104        '-Wno-suggest-attribute=pure',
105        '-Wno-suggest-final-methods',
106        '-Wno-switch-default',
107        '-Wno-switch-enum',
108        '-Wno-unused-const-variable',
109        '-Wno-useless-cast',
110      ]
111    elif cpp.get_id() == 'msvc'
112      cpp_warnings += [
113        '/wd4061',  # enumerator in switch is not explicitly handled
114        '/wd4191',  # unsafe conversion from type to type
115        '/wd4355',  # 'this' used in base member initializer list
116        '/wd4514',  # unreferenced inline function has been removed
117        '/wd4571',  # structured exceptions (SEH) are no longer caught
118        '/wd4625',  # copy constructor implicitly deleted
119        '/wd4626',  # assignment operator implicitly deleted
120        '/wd4706',  # assignment within conditional expression
121        '/wd4710',  # function not inlined
122        '/wd4711',  # function selected for automatic inline expansion
123        '/wd4800',  # implicit conversion from int to bool
124        '/wd4820',  # padding added after construct
125        '/wd4868',  # compiler may not enforce left-to-right evaluation order
126        '/wd4996',  # function or variable may be unsafe
127        '/wd5026',  # move constructor implicitly deleted
128        '/wd5027',  # move assignment operator implicitly deleted
129        '/wd5039',  # potentially throwing function passed to C
130        '/wd5045',  # will insert Spectre mitigation for memory load
131      ]
132    endif
133
134    add_project_arguments(cpp.get_supported_arguments(cpp_warnings),
135                          language: ['cpp'])
136  endif
137
138  # Objective C warnings
139  if is_variable('objcc')
140    add_project_arguments(objcc.get_supported_arguments(all_objc_warnings),
141                          language: ['objc'])
142  endif
143endif
144
145# Disable deprecated API which is not used by tests or examples
146add_project_arguments(['-DPUGL_DISABLE_DEPRECATED'],
147                      language: ['c', 'cpp', 'objc'])
148
149c_headers = [
150  'include/pugl/pugl.h',
151
152  'include/pugl/cairo.h',
153  'include/pugl/gl.h',
154  'include/pugl/stub.h',
155  'include/pugl/vulkan.h',
156]
157
158c_header_files = files(c_headers)
159
160cpp_headers = [
161  'bindings/cxx/include/pugl/pugl.hpp',
162
163  'bindings/cxx/include/pugl/cairo.hpp',
164  'bindings/cxx/include/pugl/gl.hpp',
165  'bindings/cxx/include/pugl/stub.hpp',
166  'bindings/cxx/include/pugl/vulkan.hpp',
167]
168
169cpp_header_files = files(cpp_headers)
170
171core_sources = [
172  'src/implementation.c'
173]
174
175# System libraries
176m_dep = cc.find_library('m', required: false)
177dl_dep = cc.find_library('dl', required: false)
178thread_dep = dependency('threads')
179
180# Cairo (optional backend)
181cairo_dep = dependency('cairo',
182                       required: get_option('cairo'))
183
184# OpenGL (optional backend)
185opengl_dep = dependency('GL',
186                        required: get_option('opengl'))
187
188# Vulkan (optional backend)
189vulkan_dep = dependency('vulkan',
190                        required: get_option('vulkan'))
191
192core_args = []
193
194# MacOS
195if host_machine.system() == 'darwin'
196  cocoa_dep = dependency('Cocoa', required: false, modules: 'foundation')
197  corevideo_dep = dependency('CoreVideo', required: false)
198
199  platform = 'mac'
200  platform_sources = ['src/mac.m', 'src/mac_stub.m']
201  core_deps = [cocoa_dep, corevideo_dep]
202  extension = '.m'
203
204  add_project_arguments(['-Wno-deprecated-declarations'], language: ['objc'])
205  add_project_arguments(['-Wno-direct-ivar-access'], language: ['objc'])
206
207  add_project_arguments(['-DGL_SILENCE_DEPRECATION'],
208                        language: ['c', 'objc'])
209
210  add_project_link_arguments(['-Wl,-framework,Cocoa'],
211                             language: ['c', 'objc'])
212
213# Windows
214elif host_machine.system() == 'windows'
215  if cpp.get_id() == 'msvc'
216    msvc_args = [
217      '/TP',
218      '/experimental:external',
219      '/external:W0',
220      '/external:anglebrackets',
221    ]
222
223    add_project_arguments(msvc_args, language: ['c', 'cpp'])
224  endif
225
226  win_args = [
227    '-DWIN32_LEAN_AND_MEAN',
228    '-D_CRT_SECURE_NO_WARNINGS',
229  ]
230
231  add_project_arguments(win_args, language: ['c', 'cpp'])
232
233  platform = 'win'
234  platform_sources = ['src/win.c']
235  core_deps = []
236  extension = '.c'
237
238else # X11
239  x11_dep = cc.find_library('X11')
240
241  xcursor_dep = cc.find_library('Xcursor', required: false)
242  if xcursor_dep.found()
243    core_args += ['-DHAVE_XCURSOR']
244  endif
245
246  xrandr_dep = cc.find_library('Xrandr', required: false)
247  if xrandr_dep.found()
248    core_args += ['-DHAVE_XRANDR']
249  endif
250
251  xext_dep = cc.find_library('Xext', required: false)
252  if xext_dep.found()
253    xsync_fragment = '''#include <X11/Xlib.h>
254      #include <X11/extensions/sync.h>
255      int main(void) { XSyncQueryExtension(0, 0, 0); return 0; }'''
256    if cc.compiles(xsync_fragment, name: 'Xsync')
257      core_args += ['-DHAVE_XSYNC']
258    endif
259  endif
260
261  platform = 'x11'
262  platform_sources = ['src/x11.c']
263  core_deps = [x11_dep, xcursor_dep, xrandr_dep, xext_dep]
264  extension = '.c'
265endif
266
267# Build core library
268
269core_deps += [m_dep]
270core_sources += platform_sources
271core_name = 'pugl_@0@@1@'.format(platform, version_suffix)
272
273library_args = ['-DPUGL_INTERNAL']
274if get_option('default_library') == 'both'
275  if host_machine.system() == 'windows'
276    error('default_library=both is not supported on Windows')
277  endif
278
279  library_type = 'both_libraries'
280elif get_option('default_library') == 'shared'
281  library_type = 'shared_library'
282else
283  library_type = 'static_library'
284  add_project_arguments(['-DPUGL_STATIC'], language: ['c', 'cpp', 'objc'])
285endif
286
287libpugl = build_target(
288  core_name, core_sources,
289  version: meson.project_version(),
290  include_directories: include_directories(['include']),
291  c_args: library_args + core_args,
292  dependencies: core_deps,
293  gnu_symbol_visibility: 'hidden',
294  install: true,
295  target_type: library_type)
296
297pugl_dep = declare_dependency(link_with: libpugl, dependencies: core_deps)
298
299pkg.generate(libpugl,
300             name: 'Pugl',
301             filebase: versioned_name,
302             subdirs: [versioned_name],
303             version: meson.project_version(),
304             description: 'Pugl GUI library core')
305
306# Build stub backend
307
308name = 'pugl_' + platform + '_stub' + version_suffix
309sources = 'src/' + platform + '_stub' + extension
310
311stub_backend = build_target(
312  name, sources,
313  version: meson.project_version(),
314  include_directories: include_directories(['include']),
315  c_args: library_args,
316  dependencies: [pugl_dep],
317  gnu_symbol_visibility: 'hidden',
318  install: true,
319  target_type: library_type)
320
321stub_backend_dep = declare_dependency(link_with: stub_backend)
322
323pkg.generate(stub_backend,
324             name: 'Pugl Stub',
325             filebase: 'pugl-stub-@0@'.format(major_version),
326             subdirs: [name],
327             version: meson.project_version(),
328             description: 'Native window pugl graphics backend')
329
330# Build GL backend
331if opengl_dep.found()
332  name = 'pugl_' + platform + '_gl' + version_suffix
333  sources = 'src/' + platform + '_gl' + extension
334
335  gl_backend = build_target(
336    name, sources,
337    version: meson.project_version(),
338    include_directories: include_directories(['include']),
339    c_args: library_args,
340    dependencies: [pugl_dep, opengl_dep],
341    gnu_symbol_visibility: 'hidden',
342    install: true,
343    target_type: library_type)
344
345  gl_backend_dep = declare_dependency(link_with: gl_backend,
346                                      dependencies: [pugl_dep, opengl_dep])
347
348  pkg.generate(gl_backend,
349               name: 'Pugl OpenGL',
350               filebase: 'pugl-gl-@0@'.format(major_version),
351               subdirs: [name],
352               version: meson.project_version(),
353               description: 'Pugl GUI library with OpenGL backend')
354endif
355
356# Build Cairo backend
357if cairo_dep.found()
358  name = 'pugl_' + platform + '_cairo' + version_suffix
359  sources = ['src/' + platform + '_cairo' + extension,
360             'src/' + platform + '_stub' + extension]
361
362  cairo_backend = build_target(
363    name, sources,
364    version: meson.project_version(),
365    include_directories: include_directories(['include']),
366    c_args: library_args,
367    dependencies: [pugl_dep, cairo_dep, stub_backend_dep],
368    gnu_symbol_visibility: 'hidden',
369    install: true,
370    target_type: library_type)
371
372  cairo_backend_dep = declare_dependency(
373    link_with: cairo_backend,
374    dependencies: [pugl_dep, cairo_dep, stub_backend_dep])
375
376  pkg.generate(cairo_backend,
377               name: 'Pugl Cairo',
378               filebase: 'pugl-cairo-@0@'.format(major_version),
379               subdirs: [name],
380               version: meson.project_version(),
381               description: 'Pugl GUI library with Cairo backend')
382endif
383
384# Build Vulkan backend
385if vulkan_dep.found()
386  name = 'pugl_' + platform + '_vulkan' + version_suffix
387  sources = ['src/' + platform + '_vulkan' + extension,
388             'src/' + platform + '_stub' + extension]
389
390  vulkan_deps = [pugl_dep, vulkan_dep, dl_dep]
391  vulkan_c_args = library_args
392  vulkan_link_args = []
393  if platform == 'mac'
394    metal_dep = dependency('Metal', modules: 'foundation')
395    quartzcore_dep = dependency('QuartzCore', modules: 'foundation')
396
397    vulkan_deps += [metal_dep, quartzcore_dep]
398  endif
399
400  vulkan_backend = build_target(
401    name, sources,
402    version: meson.project_version(),
403    include_directories: include_directories(['include']),
404    c_args: library_args,
405    dependencies: vulkan_deps,
406    gnu_symbol_visibility: 'hidden',
407    install: true,
408    target_type: library_type)
409
410  vulkan_backend_dep = declare_dependency(
411    link_with: vulkan_backend,
412    dependencies: [pugl_dep, vulkan_dep, thread_dep])
413
414  pkg.generate(vulkan_backend,
415               name: 'Pugl Vulkan',
416               filebase: 'pugl-vulkan-@0@'.format(major_version),
417               subdirs: [name],
418               version: meson.project_version(),
419               description: 'Pugl GUI library with Vulkan backend')
420endif
421
422install_headers(c_headers, subdir: versioned_name / 'pugl')
423install_headers(cpp_headers, subdir: 'puglxx' + version_suffix)
424
425if not get_option('docs').disabled()
426  subdir('doc')
427else
428  build_docs = false
429endif
430
431if get_option('examples')
432  subdir('examples')
433endif
434
435if get_option('tests')
436  subdir('test')
437endif
438
439if meson.version().version_compare('>=0.53.0')
440  summary('Platform', platform)
441  summary('Cairo backend', cairo_dep.found(), bool_yn: true)
442  summary('OpenGL backend', opengl_dep.found(), bool_yn: true)
443  summary('Vulkan backend', vulkan_dep.found(), bool_yn: true)
444  summary('Tests', get_option('tests'), bool_yn: true)
445  summary('Examples', get_option('examples'), bool_yn: true)
446  summary('Documentation', build_docs, bool_yn: true)
447
448  summary('Install prefix', get_option('prefix'))
449  summary('Headers', get_option('prefix') / get_option('includedir'))
450  summary('Libraries', get_option('prefix') / get_option('libdir'))
451
452  if get_option('examples')
453    summary('Executables', get_option('prefix') / get_option('bindir'))
454  endif
455endif
456