project('exess', ['c'],
        version: '0.0.1',
        license: 'ISC',
        meson_version: '>= 0.49.0',
        default_options: [
          'b_ndebug=if-release',
          'buildtype=release',
          'c_std=c99',
          'cpp_std=c++11',
          'default_library=shared',
          'warning_level=3',
        ])

exess_src_root = meson.current_source_dir()
major_version = meson.project_version().split('.')[0]
version_suffix = '-@0@'.format(major_version)
versioned_name = 'exess' + version_suffix

# Load build tools
pkg = import('pkgconfig')
cc = meson.get_compiler('c')
add_languages(['cpp'])
cpp = meson.get_compiler('cpp')

# Set ultra strict warnings for developers, if requested
c_warnings = []
c_suppressions = []
if get_option('strict')
  if meson.is_subproject()
    c_warnings = []
  else
    subdir('meson')
    add_project_arguments(all_c_warnings, language: ['c'])
  endif

  if cc.get_id() == 'clang'
    c_suppressions = [
      '-Wno-bad-function-cast',
      '-Wno-nullability-extension',
      '-Wno-padded',
    ]

  elif cc.get_id() == 'gcc'
    c_suppressions = [
      '-Wno-aggregate-return',
      '-Wno-inline',
      '-Wno-padded',
      '-Wno-strict-overflow',
      '-Wno-switch-default',
      '-Wno-unsuffixed-float-constants',
      '-Wno-unused-const-variable',
    ]

  elif cc.get_id() == 'msvc'
    c_suppressions = [
      '/wd4028',  # formal parameter different from declaration
      '/wd4204',  # nonstandard extension: non-constant aggregate initializer
      '/wd4706',  # assignment within conditional expression
      '/wd4710',  # function not inlined
      '/wd4711',  # function selected for automatic inline expansion
      '/wd4820',  # padding added after data member
      '/wd5045',  # will insert Spectre mitigation
    ]
  endif
else
  if cc.get_id() == 'clang'
    c_suppressions = [
      '-Wno-nullability-extension',
    ]
  endif
endif

exess_c_args = cc.get_supported_arguments(c_suppressions)

message(exess_c_args)

if cc.get_id() == 'msvc'
  # Suppress warnings in system headers
  add_project_arguments(['/experimental:external',
                         '/external:W0',
                         '/external:anglebrackets'],
                        language: ['c'])
endif

# Detect compiler features

feature_checks = {
  'builtin_clz': 'return __builtin_clz(1);',
  'builtin_clzll': 'return __builtin_clzll(1);',
}

checks = get_option('checks')
foreach name, fragment : feature_checks
  opt = get_option('use_@0@'.format(name))
  code = 'int main(void) { @0@ }'.format(fragment)
  define_name = 'HAVE_@0@'.format(name.to_upper())

  if opt.enabled()
    add_project_arguments(['-D@0@=1'.format(define_name)], language: ['c'])
  elif opt.disabled()
    add_project_arguments(['-D@0@=0'.format(define_name)], language: ['c'])
  elif checks
    if cc.links(code, name: name)
      add_project_arguments(['-D@0@=1'.format(define_name)], language: ['c'])
    else
      add_project_arguments(['-D@0@=0'.format(define_name)], language: ['c'])
    endif
  endif
endforeach

# System libraries
m_dep = cc.find_library('m', required: false)

# Determine library type and the flags needed to build it
if get_option('default_library') == 'both'
  if host_machine.system() == 'windows'
    error('default_library=both is not supported on Windows')
  endif

  library_type = 'both_libraries'
  library_args = ['-DEXESS_INTERNAL']
  prog_args = []
elif get_option('default_library') == 'shared'
  library_type = 'shared_library'
  library_args = ['-DEXESS_INTERNAL']
  prog_args = []
else
  library_type = 'static_library'
  library_args = ['-DEXESS_INTERNAL', '-DEXESS_STATIC']
  prog_args = ['-DEXESS_STATIC']
endif

c_headers = ['include/exess/exess.h']
c_header_files = files(c_headers)

sources = [
  'src/base64.c',
  'src/bigint.c',
  'src/boolean.c',
  'src/byte.c',
  'src/canonical.c',
  'src/coerce.c',
  'src/datatype.c',
  'src/date.c',
  'src/datetime.c',
  'src/decimal.c',
  'src/digits.c',
  'src/double.c',
  'src/duration.c',
  'src/float.c',
  'src/hex.c',
  'src/int.c',
  'src/int_math.c',
  'src/long.c',
  'src/read_utils.c',
  'src/scientific.c',
  'src/short.c',
  'src/soft_float.c',
  'src/strerror.c',
  'src/strtod.c',
  'src/time.c',
  'src/timezone.c',
  'src/ubyte.c',
  'src/uint.c',
  'src/ulong.c',
  'src/ushort.c',
  'src/variant.c',
  'src/write_utils.c',
  'src/year.c',
]

# Build shared and/or static library/libraries
libexess = build_target(
  versioned_name,
  sources,
  c_args: exess_c_args + library_args,
  dependencies: [m_dep],
  gnu_symbol_visibility: 'hidden',
  include_directories: include_directories(['include']),
  install: true,
  target_type: library_type,
  version: meson.project_version())

# Generage pkg-config file
pkg.generate(
  libexess,
  name: 'Exess',
  filebase: versioned_name,
  subdirs: [versioned_name],
  version: meson.project_version(),
  description: 'A simple and efficient regular expression implementation')

# Install header to a versioned include directory
install_headers(c_headers, subdir: versioned_name / 'exess')

exess_dep = declare_dependency(
  link_with: libexess,
  include_directories: include_directories(['include']))

subdir('bindings/cpp')

if not get_option('docs').disabled()
  subdir('doc')
endif

if get_option('tests')
  if library_type == 'both_libraries'
    libexess_static = libexess.get_static_lib()
  elif library_type == 'shared_library'
    libexess_static = static_library(
      versioned_name,
      sources,
      include_directories: include_directories(['include', 'src']),
      c_args: exess_c_args + library_args,
      dependencies: [m_dep],
      gnu_symbol_visibility: 'default')
  else
    libexess_static = libexess
  endif

  exess_static_dep = declare_dependency(
    include_directories: include_directories(['include']),
    dependencies: [m_dep],
    link_with: libexess_static)

  subdir('test')
endif

if meson.version().version_compare('>=0.53.0')
  summary('Tests', get_option('tests'), bool_yn: true)

  summary('Install prefix', get_option('prefix'))
  summary('Headers', get_option('prefix') / get_option('includedir'))
  summary('Libraries', get_option('prefix') / get_option('libdir'))
endif