Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
meson.build 19.22 KiB
############################################################
# meson build file for binary_c
# 
# (c) Robert Izzard 10/11/2019
#
# Known to work with binary_c 2.1.4 using gcc and clang.
#
############################################################

############################################################
# TODO:
#
# 'gprof'/'gcov' builds
# profile-guided optimization
# build data_objects in builddir, not in source tree
# update binary_grid2 to handle install in prefix
# (perhaps? we really want these to be persistent...)
#
############################################################
#
# See also
# https://github.com/mesonbuild/meson/blob/master/docs/markdown/howtox.md
# as a useful cookbook.
#
############################################################
#
# A typical clean build with gcc and "time make" (without ccache) gives 
#
# 82.20user 19.66system 0:30.18elapsed 337%CPU (0avgtext+0avgdata 330368maxresident)k
#
# While with ninja (no ccache) we get
# 55.83user 15.86system 0:20.14elapsed 355%CPU (0avgtext+0avgdata 327448maxresident)k
############################################################
# define the binary_c project
project(
    'binary_c','c',
    version : run_command('sh','-c','meson/binary_c_version.sh').stdout().strip(),
    default_options : [
        'c_std=gnu99',
    ]
)
binary_c_version = run_command('sh','-c','meson/binary_c_version.sh').stdout().strip()
compiler = meson.get_compiler('c')

############################################################
# System information
cpufreq = run_command('meson/cpu_frequency.sh').stdout().strip()

############################################################
# get home directory
homedir = run_command('sh','-c','echo $HOME').stdout().strip()

############################################################
# get binary_c root and src locations
binary_c = meson.source_root()
binary_c_src = meson.source_root() + '/src'

############################################################
# SVN and git information

git_revision = run_command('sh','-c','meson/git_revision.sh').stdout().strip()
svn_revision = run_command('sh','-c','meson/svn_revision.sh').stdout().strip()
git_url = run_command('sh','-c','meson/git_url.sh').stdout().strip()
svn_url = run_command('sh','-c','meson/svn_url.sh').stdout().strip()

############################################################
# default include and library search directories
incdirs = [ '.', './src' ]
libdirs = [ './src' ]
absolute_incdirs = [ binary_c, binary_c_src ]
absolute_libdirs = [ ]

############################################################
# default C flags
#
cflags = [
    '-DALIGNSIZE=16'
]

############################################################
# cflags required for generic builds and other builds
#
_other_cflags = [
    '-std=gnu99', # required for PCH (and cannot hurt if specified twice)
]

foreach cflag : _other_cflags
    if compiler.has_argument(cflag) 
        cflags += [ cflag ]
    endif
endforeach

############################################################
# C flags that are non-generic, i.e. machine-specific.
#
if get_option('generic') == false
    _non_generic_cflags = [
        # put list here
    ]                               
    foreach cflag : _non_generic_cflags
        __cflag = '-m' + cflag
        if compiler.has_argument( __cflag )
            cflags += [ __cflag ]
        endif
    endforeach
endif
############################################################
# optional C flags

optional_flags = [ ]

# generic build
if get_option('generic') == true
    optional_flags += [
        '-mtune=generic',
    ]
else   
    optional_flags += [
        '-march=native',
        '-mtune=native',
    ]
endif

# accurate mathematical options
if get_option('accurate') == true
    optional_flags += [
        '-frounding-math',
        '-fno-stack-protector',
        '-ffloat-store',
        '-fno-fast-math'
    ]
else
    optional_flags += [
        '-ffast-math',
        '-fno-associative-math',
    ]
endif

optional_flags += [
    '-fno-finite-math-only',
    '-fsignaling-nans',
    '-fomit-frame-pointer',
    '-fvisibility=hidden',
]

############################################################
# debug C flags
if get_option('buildtype').startswith('debug')
    optional_flags += [ '-rdynamic', '-O0' ]
endif

############################################################
# Now go through the optional_flags, and use those that
# are supported by the compiler.
#
foreach _flag : optional_flags 
    if compiler.has_argument(_flag)
        cflags += _flag
    endif
endforeach


############################################################
# per-compiler options
#
if compiler.get_id() == 'clang'
    # clang-speific flags
    cflags += [ '-fbracket-depth=512' ]
else
    # gcc-specific flags
    cflags += []
endif

############################################################
# system options
#
cflags += '-DCPUFREQ=' + cpufreq

############################################################
# operating system flags
#
os = host_machine.system()
cflags += [
    '-DOPERATING_SYSTEM=' + os
    ]
if os == 'linux'
    # Linux system
    cflags += [
        '-DLINUX',
        '-DLARGEFILE_SOURCE'
    ]
elif os == 'windows'
    # windows
    cflags += [
        '-DWINDOWS'
    ]
elif os == 'darwin'
    # darwin (MacOSX)
    cflags += [
        '-DDARWIN'
    ]
else
    cflags += [
        '-DUNKNOWN_OPERATING_SYSTEM'
    ]
endif

############################################################
# 64- or 32-bit build
#
if compiler.compiles('int main(int argc,char **argv){return 0;}',
                     args : '-m64',
                     name : '64-bit check')  
    # 64-bit system
    cflags += '-D_FILE_OFFSET_BITS=64'
else
    # assume 32-bit system
endif

############################################################
# version control options

if git_revision != ''
    _flag = '-DGIT_REVISION="' + git_revision + '"'
    cflags += _flag
endif  
if svn_revision != ''
    _flag = '-DSVN_REVISION="' + svn_revision + '"'
    cflags += _flag
endif

if git_url != ''
    _flag = '-DGIT_URL="' + git_url + '"'
    cflags += _flag
endif  
if svn_url != ''
    _flag = '-DSVN_URL="' + svn_url + '"'
    cflags += _flag
endif  


############################################################
# dependencies are put in the list called dependencies
#
dependencies = [ ]

############################################################
# dependencies that usually have no pkg-config
# THESE NEED TO BE SET IN $LIBRARY_PATH (or $LIBPATH on Windoze)
#
# system libraries : note that libm may not be required
#                    on some systems (GSL may also specify)
dependencies += [
    compiler.find_library('c', required: true),
    compiler.find_library('m', required: false),
]


############################################################
# libraries : required and optional.
#
# Note that optional libraries are not built in
# on a generic build.
#
_required_libraries  = [
    'c',
    'gsl',
    'gslcblas',
]
_optional_libraries = [
    'backtrace',
    'bfd',
    'bsd',
    'iberty',
    'm', # optional on some platforms (gsl probably required is)
    'memoize',
    'rinterpolate',
]
libs = [] # list sent to the compiler
foreach libname : _required_libraries
    _dep = compiler.find_library(libname,
                                 required:true) 
    if _dep.found()
        cflags += '-D__HAVE_LIB' + libname.to_upper() +'__'
        libs += '-l' + libname

        # extras
        if libname == 'gsl'
            # use gsl-config to find cflags, libraries, etc.
            message('Adding GSL-specific flags from gsl-config')
            cflags += run_command('sh','-c','gsl-config --cflags').stdout().strip().split(' ')
            cflags += '-DUSE_GSL'
            libs += run_command('sh','-c','gsl-config --libs').stdout().strip().split(' ')
            gsl_libdirs = run_command('sh','-c','gsl-config  --libs | tr " " "\n"|grep ^-L | tr "\n" " "').stdout().strip().split(' ')
            libdirs += gsl_libdirs
            incdirs += run_command('sh','-c','gsl-config --prefix').stdout().strip()+'/include'
        endif
    endif
    dependencies += _dep
endforeach

# Note : this should work, but does not :(
#
#gsl_dep = dependency(
#    'gsl',
#    required : true,
#    version : '>=2.4',
#    method : 'config-tool',
#             )

#############################
# optional libraries       
# libbacktrace, libbfd etc.
# (generic builds should not use these)
#
if get_option('generic') == false
    foreach libname : _optional_libraries
        _dep = compiler.find_library(libname,required:false) 
        if _dep.found()
            cflags += '-D__HAVE_LIB' + libname.to_upper() +'__'
            libs += '-l' + libname
        endif
    endforeach
endif

############################################################
# features which are converted into preprocessor flags (-D)
#

###########
# drand48 #
#
if compiler.has_header('stdlib.h') and \
   compiler.sizeof('drand48_r',
                   prefix : '#include <stdlib.h>') > 0
    cflags += '-D__HAVE_DRAND48__'
endif

############
# malloc.h #
#
if compiler.has_header('malloc.h')
    cflags += '-D__HAVE_MALLOC_H__'
endif

############ 
# setitimer 
#
if compiler.has_header('sys/time.h') and \
   compiler.has_function('setitimer')
    cflags += '-D__HAVE_SETITIMER__'
endif

#################################
# pkg-config (external command) #
#
if run_command('pkg-config','--version').returncode() == 0
    cflags += '-D__HAVE_PKG_CONFIG__'
endif

###########################
# valgrind (header files) #
#
if compiler.has_header('valgrind/valgrind.h')
    cflags += '-D__HAVE_VALGRIND__'
endif

###################
# show_starstruct #
#
if run_command('sh','-c','meson/make_starstruct.sh').returncode() == 0
    cflags += '-D__SHOW_STARDATA__'
endif

###################
# diff_starstruct #
#
if run_command('sh','-c','meson/diff_starstruct.sh').returncode() == 0
    cflags += '-D__DIFF_STARDATA__'
endif

########################
# Unsupported features #
#
if compiler.get_id() != 'gcc' and compiler.get_id() != 'clang'
    cflags += '-UBACKTRACE'
endif

############################################################
# make a list of include directories
# We do this so that $HOME/include is searched (e.g. for
# libgsl)
#
my_incdirs = []
found = false
_include_search_paths = [
    homedir + '/include',
    '/usr/include',
    '/usr/local/include',
] 
foreach idir : _include_search_paths
    inc_arg = idir
    _Inc_arg = '-I' + idir
    if not found and compiler.has_header('gsl/gsl_blas.h',
                                         args: _Inc_arg)
        my_incdirs += [inc_arg]
        found = true
    endif
endforeach

incdirs += [ my_incdirs ]

############################################################
# data objects
#
# list and build them
message('Checking and building data objects')
data_objects = run_command('meson/data_object_list_and_build.sh').stdout().strip().split(' ')

if get_option('clean_data_objects') == true
    run_command('meson/clean_data_objects.sh')
endif

############################################################
# make binary_c_version_macros.h
#
run_command('meson/make_version_macros.pl')

############################################################
# source files
#
c_sourcefiles = run_command('meson/c_sourcefiles.sh').stdout().strip().split('\n')
c_main_sourcefiles = run_command('meson/c_main_sourcefiles.sh').stdout().strip().split('\n')
h_sourcefiles = run_command('meson/h_sourcefiles.sh').stdout().strip().split('\n')

############################################################
# extra quoted flags to pass into binary_c
#
cflags_with_O = cflags + [ '-O' + get_option('optimization') ] # cflags with -O<n>
cflags_quoted = ' '.join(cflags_with_O) # turn to string
cflags_quoted = ''.join(cflags_quoted.split('"'))  # remove "
cflags_quoted = '-DCFLAGS=' + ''.join(['"', cflags_quoted , '"']) # surround in " ... "
cc_quoted = '-DCC="' + compiler.get_id() + '"'
ld_quoted = '-DLD="' + compiler.get_id() + '"'
incdirs_quoted = '-DINCDIRS=' + ''.join(['"-I', ' -I'.join(absolute_incdirs),'"']) # make -I... -I...
incdirs_quoted = '_slash_'.join(incdirs_quoted.split('/')) # deslash (convert / to _slash_)
libdirs_quoted = '-DLIBDIRS=' + ''.join(['"-L', ' -L'.join(absolute_libdirs),'"']) # make -L... -L...
libdirs_quoted = '_slash_'.join(libdirs_quoted.split('/')) # deslash
libs_quoted = '-DLIBS=' + ''.join(['"', ' '.join(libs),'"']) 
libs_quoted = '_slash_'.join(libs_quoted.split('/'))

# hence a quoted version of the cflags
quoted_cflags_list = [
    cflags_quoted,
    cc_quoted,
    incdirs_quoted,
    ld_quoted,
    libdirs_quoted,
    libs_quoted,
]

############################################################
# compiler warning flags
############################################################
warn_flags = []
foreach _arg : [
    'all',
    'format',
    'strict-prototypes',
    'format-signedness'
]
    _warn_arg = '-W' + _arg
    if compiler.has_argument(_warn_arg)
        warn_flags += _warn_arg
    endif
endforeach
cflags += warn_flags


############################################################
# make precompiled headers (PCH)
#
# PCH is (sort of) supported by Meson, but not really.
# Meson supports it if you have a pre-made .pch (or .gch
# for gcc) file, but that's not the case here. We should
# generate our own pch/gch file on the file, and *also*
# its dependencies in a pch.d/gch.d file. This is a bit
# complicated, as you will see below.
#
if get_option('usepch') == true
    # get optimization level, required for __OPTIMIZE__
    _opt = '-O' + get_option('optimization')

    # make sure we precompile with fPIC and symbols
    _pic = ['-g','-fPIC']
    
    # normal dep file
    _depfile_path = binary_c_src + '/binary_c.h.gch.d'
    
    # make a list of incdirs each prefixed by -I
    _incdirs = []
    foreach _i : absolute_incdirs
        _I = '-I' + _i
        _incdirs += _I
    endforeach
    _incdirs += [ '-I.' ]

    # choose your compiler...
    if compiler.get_id() == 'clang'
        # clang PCH
        
        _pch = 'binary_c.h.pch'
        use_pch_cflags = [
            '-include-pch',
            _pch ]
        pch_cflags = [
            _opt,
            _pic,
            _incdirs,
        ]             
        pch_sourcefiles = [ _pch ]
        pch_post = [ '-o', _pch ]
        pch_depfile =  ''

    else
        # GCC PCH
        
        _gch = 'binary_c.h.gch'
        _gchd = '../src/' + _gch + '.d'
        use_pch_cflags = [  ] # -include is not required
        pch_cflags = [
              _opt,
              _pic,
              _incdirs,
              '-MT', 'binary_c.h.gch',
              '-MMD',
              '-MP',
              '-MF', _depfile_path,
              '-x', 'c-header'
            ]
        pch_sourcefiles = [ _gch ]
        cflags += [
            '-fpch-deps',
            '-fpch-preprocess'
        ]
        pch_post = [ ]
        pch_depfile = _gch + '.d'
    endif
    
    # make cflags for pch build
    pch_cflags_array = cflags + pch_cflags

    # append usage flags
    cflags += use_pch_cflags

    # make PCH compiler: this is just the normal compiler with PCH flags
    pch_compiler = compiler.cmd_array() + pch_cflags_array

    message('building precompiled headers')
    precompiled_headers = custom_target(
        'binary_c.h.pch',
        build_by_default : true,
        input : ['src/binary_c.h'],
        output :  pch_sourcefiles,
        command : [
            pch_compiler,
            '-gdwarf-2',
            '@INPUT@',
            pch_post
        ],
        depend_files : [ h_sourcefiles ],
    )

else
    # disable PCH by removing the files it would have generated
    
    all_pch_sourcefiles = [ 'binary_c.h.gch', 'binary_c.h.pch' ]
    precompiled_headers = custom_target(
        'binary_c.h.pch',
        build_by_default : true,
        input : [ 'src/binary_c.h' ],
        output :  all_pch_sourcefiles,
        command : [ 'rm', '-f', all_pch_sourcefiles ],
        depend_files : [h_sourcefiles],
    )

endif

############################################################
# command to install binary_c in its 'legacy' locations
# (required for binary_grid2)
binary_c_legacy_install_cmd = [
    'cp','--remove-destination','binary_c','../',
    '&&',
    'cp','--remove-destination','libbinary_c.so','../src/',
    '&&',
    'echo','libbinary_c.so built with symbols:',
    '&&',
    '../meson/list_shared_symbols.sh','libbinary_c.so'
]

############################################################
#
# Convenience for Rob: try to locate binary_grid2.pm and
# hence touch (and rebuild) it when the shared library is
# rebuild
#
if run_command('sh','-c','meson/check_binary_grid2.sh').returncode() == 0
    binary_grid2_file = run_command(
        'sh',
        '-c',
        'meson/check_binary_grid2.sh'
    ).stdout().strip()

    message('legacy touch')
    binary_c_legacy_install_cmd += [
        '&&',
        'touch',
        binary_grid2_file
    ]
endif    

############################################################
#
# symlink shared_library to binary_c/src directory
# run: ninja libbinary_c_symlink
#
libbinary_c_symlink = custom_target(
    'libbinary_c_symlink',
    build_by_default: false,
    output: [ 'libbinary_c_symlink' ],
    command: [
        'sh',
        '-c',
        '../meson/symlink_libbinary_c.sh'
    ],
)


############################################################
#
# make binary_c objects
#
# We put the objects in here so they can be shared by
# the executable and library builds. We're not really
# interested in the static library, but we have to call
# this build something.
#
binary_c_objects = build_target(
    'binary_c_objects',
    build_by_default: true,
    pic: true,
    target_type : 'static_library', # pretend we're a static library!
    sources : [
        c_sourcefiles,
        h_sourcefiles,
        precompiled_headers
    ],
    include_directories: include_directories(incdirs),
    dependencies : dependencies,
    c_args : [
        cflags,
        quoted_cflags_list,
    ],
    objects : data_objects,
    link_args : [
        libs,
    ],
    )

############################################################
#
# make shared library
# run: ninja libbinary_c.so
#
binary_c_shared_library = shared_library(
    get_option('libname'),
    build_by_default : false,    
    install: true,
    include_directories: include_directories(incdirs),
    dependencies : [ dependencies ],
    c_args : [
        cflags,
        quoted_cflags_list,
        '--whole-archive',
    ],
    link_whole: [
        binary_c_objects,
    ],
    link_args : [
        libs,
        '-fvisibility=hidden',
    ],
)

############################################################
#
# make executable
# run: ninja binary_c
#
binary_c_executable = executable(
    'binary_c',
    build_by_default : true,    
    install: true,
    sources : [
        c_main_sourcefiles,
    ],
    include_directories: include_directories(incdirs),
    dependencies : [ dependencies ],
    c_args : [
        cflags,
        quoted_cflags_list,
    ],
    objects : data_objects,
    link_args : [
        libs,
    ],
    link_with: [ binary_c_objects ],    
)

############################################################
# install both binary_c and libbinary_c.so in their legacy
# locations
#
binary_c_install_legacy = custom_target(
    'binary_c_install_legacy',
    build_by_default: false,
    output: [ 'binary_c_install_legacy' ],
    command: binary_c_legacy_install_cmd,
    depends : [
        binary_c_executable,
        binary_c_shared_library,
    ],
)