project('irssi', 'c',
  version : '1.4.3',
  meson_version : '>=0.49',
  default_options : ['warning_level=1'])

############################
############################

glib_internal_version = 'glib-2.58.3' # keep this in sync with subprojects/glib.wrap
cc = meson.get_compiler('c')
rootinc = include_directories('.')
dep = []
textui_dep = []
need_dl_cross_link = false
# The Android environment requires that all modules are linked to each other.
# See https://github.com/android/ndk/issues/201
if host_machine.system() == 'android'
  need_dl_cross_link = true
endif

includedir          = get_option('includedir')
incdir              = 'irssi'
moduledir           = get_option('libdir') / incdir / 'modules'
helpdir             = get_option('datadir') / incdir / 'help'
themedir            = get_option('datadir') / incdir / 'themes'
scriptdir           = get_option('datadir') / incdir / 'scripts'
docdir              = get_option('docdir') != '' ? get_option('docdir') : (get_option('datadir') / 'doc' / incdir)

want_textui         = get_option('without-textui') != 'yes'
want_bot            = get_option('with-bot') == 'yes'
want_fuzzer         = get_option('with-fuzzer') == 'yes'
fuzzer_lib          = get_option('with-fuzzer-lib')
fuzzer_link_language = get_option('fuzzer-link-language')
want_proxy          = get_option('with-proxy') == 'yes'
want_truecolor      = get_option('enable-true-color') == 'yes'
want_gregex         = get_option('disable-gregex') != 'yes'

require_capsicum    = get_option('with-capsicum') == 'yes'
want_capsicum       = get_option('with-capsicum') != 'no'

require_libutf8proc = get_option('disable-utf8proc') == 'no'
want_libutf8proc    = get_option('disable-utf8proc') != 'yes'

require_perl        = get_option('with-perl') == 'yes'
want_perl           = get_option('with-perl') != 'no'
with_perl_lib       = get_option('with-perl-lib')

require_otr         = get_option('with-otr') == 'yes'
want_otr            = get_option('with-otr') != 'no'

want_glib_internal  = get_option('install-glib') != 'no'
require_glib_internal = get_option('install-glib') == 'force'

want_static_dependency = get_option('static-dependency') == 'yes'

package_version     = get_option('PACKAGE_VERSION') != '' ? get_option('PACKAGE_VERSION') : meson.project_version()

UNSET = '=INVALID='
UNSET_ARR = [UNSET]

chat_modules = ['irc']

run_command('mkdir', meson.current_build_dir() / incdir)
run_command('ln', '-s', meson.current_source_dir() / 'src', meson.current_build_dir() / incdir)
run_command('ln', '-s', meson.current_build_dir() / 'irssi-config.h', meson.current_build_dir() / incdir)
run_command('ln', '-s', meson.current_build_dir() / 'irssi-version.h', meson.current_build_dir() / incdir)

def_moduledir  = '-D' + 'MODULEDIR'  + '="' + (get_option('prefix') / moduledir) + '"'
def_sysconfdir = '-D' + 'SYSCONFDIR' + '="' + (get_option('prefix') / get_option('sysconfdir')) + '"'
def_helpdir    = '-D' + 'HELPDIR'    + '="' + (get_option('prefix') / helpdir) + '"'
def_themesdir  = '-D' + 'THEMESDIR'  + '="' + (get_option('prefix') / themedir) + '"'
def_scriptdir  = '-D' + 'SCRIPTDIR'  + '="' + (get_option('prefix') / scriptdir) + '"'

def_suppress_printf_fallback = '-D' + 'SUPPRESS_PRINTF_FALLBACK'

##############
# Help files #
##############

build_perl = find_program('perl', native : true)
if meson.is_cross_build()
  cross_perl = find_program('perl')
else
  cross_perl = build_perl
endif
run_command(build_perl, files('utils/syntax.pl'))

###################
# irssi-version.h #
###################

env = find_program('env')
irssi_version_sh = find_program('utils/irssi-version.sh')
irssi_version_h = custom_target('irssi-version.h',
  build_by_default : true,
  build_always_stale : true,
  capture : true,
  command : [env, 'VERSION=' + meson.project_version(),
    irssi_version_sh, meson.current_source_dir()],
  output : 'irssi-version.h',
  install : true,
  install_dir : includedir / incdir,
)

####################
# default-config.h #
####################

file2header = find_program('utils/file2header.sh')
default_config_h = custom_target('default-config.h',
  input : files('irssi.conf'),
  output : 'default-config.h',
  capture : true,
  command : [file2header, '@INPUT@', 'default_config'],
)

###################
# default-theme.h #
###################

default_theme_h = custom_target('default-theme.h',
  input : files('themes/default.theme'),
  output : 'default-theme.h',
  capture : true,
  command : [file2header, '@INPUT@', 'default_theme'],
)

################
# Dependencies #
################

#### inet_addr ####
inet_addr_found = false
foreach inet_addr_provider : ['', 'nsl']
  prov_lib = []
  if inet_addr_provider != ''
    prov_lib += cc.find_library(inet_addr_provider, required : false)
  endif
  if (prov_lib.length() == 0 or prov_lib[0].found()) and cc.has_function('inet_addr', dependencies : prov_lib)
    dep += prov_lib
    inet_addr_found = true
    break
  endif
endforeach
if not inet_addr_found
  error('inet_addr not found')
endif

#### socket ####
socket_found = false
foreach socket_provider : ['', 'socket', 'network']
  prov_lib = []
  if socket_provider != ''
    prov_lib += cc.find_library(socket_provider, required : false)
  endif
  if (prov_lib.length() == 0 or prov_lib[0].found()) and cc.has_function('socket', dependencies : prov_lib)
    dep += prov_lib
    socket_found = true
    break
  endif
endforeach
if not socket_found
  error('socket not found')
endif

built_src = []
glib_internal = false
message('*** If you don\'t have GLib, you can run meson ... -Dinstall-glib=yes')
message('*** to download and build it automatically')
message('*** Or alternatively install your distribution\'s package')
message('*** On Debian: sudo apt-get install libglib2.0-dev')
message('*** On Redhat: dnf install glib2-devel')
if not require_glib_internal
  glib_dep = dependency('glib-2.0', version : '>=2.32', required : not want_glib_internal, static : want_static_dependency, include_type : 'system')
else
  glib_dep = dependency('', required : false)
endif
if not glib_dep.found()
  glib_internal = true
  meson_cmd = find_program('meson')
  ninja = find_program('ninja')

  glib_internal_download_t = custom_target('glib-internal-download',
    command : [ meson_cmd, 'subprojects', 'download', 'glib', '--sourcedir', meson.current_source_dir() ],
    console : true,
    output : ['glib-internal-download'],
  )

  glib_internal_dependencies = [
    dependency('threads'),
  ]
  glib_internal_configure_args = []

  glib_internal_usr_local = false
  if not cc.has_function('iconv_open')
    prov_lib = cc.find_library('iconv', required : false)
    if not prov_lib.found()
      prov_lib = cc.find_library('iconv', dirs : '/usr/local/lib')
      glib_internal_usr_local = true
    endif
    if cc.has_function('libiconv_open', dependencies : prov_lib)
      glib_internal_configure_args += '-Diconv=gnu'
    else
      glib_internal_configure_args += '-Diconv=native'
    endif
    glib_internal_dependencies += prov_lib
  endif

  if not cc.has_function('ngettext')
    prov_lib = cc.find_library('intl', required : false)
    if not prov_lib.found()
      prov_lib = cc.find_library('intl', dirs : '/usr/local/lib')
      glib_internal_usr_local = true
    endif
    glib_internal_dependencies += prov_lib
  endif

  if glib_internal_usr_local
    glib_internal_configure_args += ['-Dc_args=-I/usr/local/include', '-Dc_link_args=-L/usr/local/lib']
  endif

  if not cc.has_function('getxattr') or not cc.has_header('sys/xattr.h')
    if cc.has_header_symbol('attr/xattr.h', 'getxattr')
      prov_lib = cc.find_library('xattr', required : false)
    else
      prov_lib = dependency('', required : false)
    endif
    if prov_lib.found()
      glib_internal_dependencies += prov_lib
    else
      glib_internal_configure_args += '-Dxattr=false'
    endif
  endif

  glib_internal_configure_t = custom_target('glib-internal-configure',
    command : [ meson_cmd, 'setup', '--prefix=/irssi-glib-internal',
      '--buildtype=' + get_option('buildtype'),
      '-Dlibmount=false', '-Dselinux=false', '-Ddefault_library=static', '-Dinternal_pcre=true',
      glib_internal_configure_args,
      (meson.current_build_dir() / 'build-subprojects' / 'glib'),
      (meson.current_source_dir() / 'subprojects' / glib_internal_version) ],
    console : true,
    output : ['glib-internal-configure'],
    depends : glib_internal_download_t,)
  glib_internal_build_t = custom_target('glib-internal-build',
    command : [ ninja, '-C', meson.current_build_dir() / 'build-subprojects' / 'glib',
      'glib' / 'libglib-2.0.a',
      'gmodule' / 'libgmodule-2.0.a'],
    console : true,
    output : ['glib-internal-build'],
    depends : glib_internal_configure_t,)
  glib_dep = declare_dependency(
    dependencies : glib_internal_dependencies,
    sources : glib_internal_build_t,
    compile_args : [
      '-isystem' + (meson.current_source_dir() / 'subprojects' / glib_internal_version / 'glib'),
      '-isystem' + (meson.current_source_dir() / 'subprojects' / glib_internal_version),
      '-isystem' + (meson.current_build_dir() / 'build-subprojects' / 'glib' / 'glib'),
    ],
    link_args : [ meson.current_build_dir() / 'build-subprojects' / 'glib' / 'glib' / 'libglib-2.0.a' ],
  )
  built_src += glib_internal_build_t
  libdl_dep = []
  prov_lib = cc.find_library('dl', required : false)
  if prov_lib.found() and cc.has_function('dlopen', dependencies : prov_lib)
    libdl_dep += prov_lib
  endif
  gmodule_dep = declare_dependency(sources : glib_internal_build_t,
    dependencies : libdl_dep,
    compile_args : [
      '-isystem' + (meson.current_source_dir() / 'subprojects' / glib_internal_version / 'gmodule'),
    ],
    link_args : [ meson.current_build_dir() / 'build-subprojects' / 'glib' / 'gmodule' / 'libgmodule-2.0.a' ],
  )
else
  gmodule_dep = dependency('gmodule-2.0', static : want_static_dependency, include_type : 'system')
endif
dep += glib_dep
dep += gmodule_dep

if glib_internal and want_static_dependency and want_fuzzer
  openssl_proj = subproject('openssl', default_options : ['default_library=static', 'asm=disabled'])
  openssl_dep = openssl_proj.get_variable('openssl_dep')
else
  openssl_dep = dependency('openssl', static : want_static_dependency, include_type : 'system')
endif
dep += openssl_dep

############
# utf8proc #
############

have_libutf8proc = false
libutf8proc = []
if want_libutf8proc
  libutf8proc = cc.find_library('utf8proc', required : require_libutf8proc)
  have_libutf8proc = cc.has_function('utf8proc_version', dependencies : libutf8proc)
  if have_libutf8proc
    dep += libutf8proc
  endif
endif

############################
############################

############
# terminfo #
############

if want_textui
  setupterm_found = false
  foreach setupterm_provider : ['tinfo', 'ncursesw', 'ncurses', 'terminfo']
    prov_lib = cc.find_library(setupterm_provider, required : false)
    if prov_lib.found() and cc.has_function('setupterm', dependencies : prov_lib)
      textui_dep += prov_lib
      setupterm_found = true
      break
    endif
  endforeach
  if not setupterm_found
    error('Terminfo not found')
  endif
endif

########
# perl #
########

have_perl = false
if want_perl
  perl_cflags = []
  perl_ldflags = []
  perl_rpath_flags = []
  perl_rpath = ''

  #### ccopts ####
  perl_ccopts = meson.get_cross_property('perl_ccopts', UNSET_ARR)
  if perl_ccopts == UNSET_ARR
    res = run_command(cross_perl, '-MExtUtils::Embed', '-e', 'ccopts')
    perl_ccopts = res.stdout().strip().split()
  endif
  foreach fl : perl_ccopts
    if fl.startswith('-D') or fl.startswith('-U') or fl.startswith('-I') or fl.startswith('-i') or fl.startswith('-f') or fl.startswith('-m')
      if fl.startswith('-I')
        fl = '-isystem' + fl.split('-I')[1]
      endif
      perl_cflags += fl
    endif
  endforeach

  perl_cflags += cc.get_supported_arguments('-fPIC')

  #### ldopts ####
  perl_ldopts = meson.get_cross_property('perl_ldopts', UNSET_ARR)
  if perl_ldopts == UNSET_ARR
    res = run_command(cross_perl, '-MExtUtils::Embed', '-e', 'ldopts')
    perl_ldopts = res.stdout().strip().split()
  endif
  skip_libs = ['-ldb', '-ldbm', '-lndbm', '-lgdbm', '-lc', '-lposix', '-rdynamic']
  foreach fl : perl_ldopts
    if not fl.startswith('-A') and not skip_libs.contains(fl)
      if fl.startswith('-Wl,-rpath,')
        perl_rpath = fl.split(',')[2]
        perl_rpath_flags += fl
      else
        perl_ldflags += fl
      endif
    endif
  endforeach

  perl_version = meson.get_cross_property('perl_version', UNSET)
  if perl_version == UNSET
    perl_version = run_command(cross_perl, '-V::version:').stdout().split('\'')[1]
  endif
  perl_dep = declare_dependency(compile_args : perl_cflags, link_args : perl_ldflags,
    version : perl_version)

  ####
  if not cc.links('''
#include <EXTERN.h>
#include <perl.h>
int main()
{
  perl_alloc();
  return 0;
}
''', args : perl_cflags + perl_ldflags + perl_rpath_flags,
     name : 'working Perl support')
    if require_perl
      error('error linking with perl libraries')
    else
      warning('error linking with perl libraries')
    endif
  else
    xsubpp_file_c = meson.get_cross_property('perl_xsubpp', UNSET)
    if xsubpp_file_c == UNSET
      xsubpp_file_c = run_command(build_perl, '-MExtUtils::ParseXS', '-Eprint $INC{"ExtUtils/ParseXS.pm"} =~ s{ParseXS\\.pm$}{xsubpp}r').stdout()
    endif
    xsubpp = generator(build_perl,
      output : '@BASENAME@.c',
      capture : true,
      arguments : [ xsubpp_file_c, '@EXTRA_ARGS@', '@INPUT@' ],
    )
    xsubpp_file = files(xsubpp_file_c)

    if with_perl_lib == 'module'
      perl_install_base = run_command(build_perl, '-MText::ParseWords=shellwords', '-e', 'grep { s/^INSTALL_BASE=// && print && exit } shellwords $ENV{PERL_MM_OPT}').stdout()
      if perl_install_base == ''
        with_perl_lib = ''
      endif
    endif
    if with_perl_lib == ''
      if get_option('prefix') in ['/usr/local', 'C:/']
        with_perl_lib = 'site'
      elif get_option('prefix') in ['/usr']
        with_perl_lib = 'vendor'
      endif
    endif
    perlmoddir = ''
    if with_perl_lib in ['site', 'vendor', 'module']
      set_perl_use_lib = false
      perl_library_dir = with_perl_lib + ' default'
      if with_perl_lib in ['site', 'vendor']
        perlmoddir = meson.get_cross_property('perl_install' + with_perl_lib + 'arch', UNSET)
        if perlmoddir == UNSET
          perlmoddir = run_command(cross_perl, '-V::install' + with_perl_lib + 'arch:').stdout().split('\'')[1]
        endif
      elif with_perl_lib == 'module'
        perl_archname = meson.get_cross_property('perl_archname', UNSET)
        if perl_archname == UNSET
          perl_archname = run_command(cross_perl, '-V::archname:').stdout().split('\'')[1]
        endif
        perlmoddir = perl_install_base / 'lib' / 'perl5' / perl_archname
      endif
    elif with_perl_lib == ''
      set_perl_use_lib = true
      perl_library_dir = 'in prefix'
      perlmoddir = get_option('libdir') / incdir / 'perl'
    elif with_perl_lib.startswith('/')
      set_perl_use_lib = true
      perl_library_dir = 'custom'
      perlmoddir = with_perl_lib
    endif
    if perlmoddir == ''
      error('Unrecognised with-perl-lib value: ' + with_perl_lib)
    endif

    perl_use_lib = get_option('prefix') / perlmoddir
    if set_perl_use_lib
      perl_inc = meson.get_cross_property('perl_inc', UNSET_ARR)
      if perl_inc == UNSET_ARR
        set_perl_use_lib = run_command(cross_perl, '-e', 'exit ! grep $_ eq $ARGV[0], grep /^\\//, @INC', perl_use_lib).returncode() != 0
      else
        set_perl_use_lib = not perl_inc.contains(perl_use_lib)
      endif
      if not set_perl_use_lib
        perl_library_dir += ' - other path in @INC'
      else
        perl_library_dir += ' - prepends to @INC with /set perl_use_lib'
      endif
    endif
    def_perl_use_lib = '-D' + 'PERL_USE_LIB' + '="'
    if set_perl_use_lib
      def_perl_use_lib += perl_use_lib
    endif
    def_perl_use_lib += '"'

    have_perl = true
  endif
endif

#######
# OTR #
#######

have_otr = false
if want_otr
  libgcrypt = dependency('libgcrypt', version : '>=1.2.0', required : require_otr, static : want_static_dependency, include_type : 'system')
  libotr = dependency('libotr', version : '>=4.1.0', required : require_otr, static : want_static_dependency, include_type : 'system')
  if libgcrypt.found() and libotr.found()
    dep += libgcrypt
    dep += libotr
    have_otr = true
  endif
endif

############
# capsicum #
############

have_capsicum = false
if want_capsicum
  if cc.has_function('cap_enter', dependencies : cc.find_library('c'))
    libnv = cc.find_library('nv', required : require_capsicum)
    nvlist_create_found = libnv.found() and cc.has_function('nvlist_create', dependencies : libnv, prefix : '#include <sys/nv.h>')
    if nvlist_create_found
      dep += libnv
      have_capsicum = true
    else
      if require_capsicum
        error('nvlist_create not found')
      endif
    endif
  else
    if require_capsicum
      error('cap_enter not found')
    endif
  endif
endif

# dependency helper sets
dep_cflagsonly = []
foreach d : dep
  dep_cflagsonly += d.partial_dependency(includes : true, compile_args : true)
endforeach
dl_cross_dep = []
if need_dl_cross_link
  dl_cross_dep = dep
endif

##################
# irssi-config.h #
##################

conf = configuration_data()

conf.set('HAVE_CAPSICUM', have_capsicum, description : 'Build with Capsicum support')
conf.set('HAVE_GMODULE', true)
conf.set('HAVE_SOCKS', false, description : 'Build with socks support')
conf.set('TERM_TRUECOLOR', want_truecolor, description : 'true color support in terminal')
conf.set('USE_GREGEX', want_gregex, description : 'use GRegex for regular expressions')
conf.set10('_DARWIN_USE_64_BIT_INODE', true, description : 'Enable large inode numbers on Mac OS X 10.5.')
conf.set_quoted('FHS_PREFIX', get_option('fhs-prefix'))

headers = [
  'sys/ioctl.h',
  'sys/resource.h',
  'sys/time.h',
  'sys/utsname.h',
  'dirent.h',
  'term.h',
  'unistd.h',
]
foreach h : headers
  if cc.has_header(h)
    conf.set('HAVE_' + h.underscorify().to_upper(), 1, description : 'Define to 1 if you have the <' + h + '> header file.')
  endif
endforeach

conf.set('HAVE_LIBUTF8PROC', have_libutf8proc)
conf.set_quoted('PACKAGE_VERSION', package_version)
conf.set_quoted('PACKAGE_TARNAME', meson.project_name())

configure_file(output : 'irssi-config.h',
  configuration : conf,
  install_dir : includedir / incdir)

##########
# CFLAGS #
##########

#### warnings ####
add_project_arguments(cc.get_supported_arguments('-Werror=declaration-after-statement'), language : 'c')

#### personality ####
add_project_arguments(cc.get_supported_arguments('-fno-strict-aliasing'), language : 'c')
if get_option('buildtype').contains('debug')
  add_project_arguments(cc.get_supported_arguments('-fno-omit-frame-pointer'), language : 'c')
endif

if want_fuzzer
  if fuzzer_lib.startswith('-fsanitize=fuzzer')
    if not cc.has_argument('-fsanitize=fuzzer-no-link')
      error('compiler does not support -fsanitize=fuzzer-no-link, try clang?')
    endif
    add_project_arguments('-fsanitize=fuzzer-no-link', language : 'c')
  endif
  if fuzzer_link_language != 'c'
    add_languages(fuzzer_link_language)
  endif
endif

##############
# irssi-1.pc #
##############

pc = import('pkgconfig')
pc_requires = []
if not glib_internal
  pc_requires += glib_dep
endif
pc.generate(filebase : 'irssi-1',
  name : 'Irssi',
  description : 'Irssi chat client',
  version : package_version,
  requires : pc_requires,
  variables : ['irssimoduledir=${libdir}' / incdir / 'modules'])

###########
# irssi.1 #
###########

install_man('docs/irssi.1')

###########
# subdirs #
###########

subdir('src')
subdir('tests')
subdir('docs')
subdir('scripts')
subdir('themes')
# subdir('utils')

############################
############################

message('*** Irssi configured ***')
message('')
message('Building text frontend ........... : ' + want_textui.to_string('yes', 'no'))
message('Building irssi bot ............... : ' + want_bot.to_string('yes', 'no'))
message('Building irssi proxy ............. : ' + want_proxy.to_string('yes', 'no'))
if want_perl and not have_perl
  message('Building with Perl support ....... : NO!')
  message(' - Try: sudo apt-get install libperl-dev')
  message(' -  Or: dnf install perl-devel')
else
  message('Building with Perl support ....... : ' + have_perl.to_string('yes', 'no'))
endif
if have_perl
  message('Perl library directory ........... : ' + perl_use_lib)
  message('                                     ' + perl_library_dir)
endif
message('Install prefix ................... : ' + get_option('prefix'))
message('')
message('Building with true color support.. : ' + want_truecolor.to_string('yes', 'no'))
message('Building with GRegex ............. : ' + want_gregex.to_string('yes', 'no'))
message('Building with Capsicum ........... : ' + have_capsicum.to_string('yes', 'no'))
message('Building with utf8proc ........... : ' + have_libutf8proc.to_string('yes', 'no'))
message('Building with OTR support ........ : ' + have_otr.to_string('yes', 'no'))
message('')
message('If there are any problems, read the INSTALL file.')
message('Now type ninja -C ' + meson.current_build_dir() + ' to build Irssi')
message('')

############################
############################
