-
Izzard, Robert Dr (Maths & Physics) authoredIzzard, Robert Dr (Maths & Physics) authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
meson.build 26.75 KiB
############################################################
# meson build file for binary_c
#
# (c) Robert Izzard 25/11/2019
#
# Known to work with binary_c 2.1.4 using gcc and clang.
#
# Supporting scripts are in the meson directory of binary_c.
#
# You may well require: bash, gawk, cp, grep, wc, tr, head
# and perl. These are standard tools on
# most systems.
#
# You certainly require a C compiler (e.g. gcc or clang),
# python3 and ninja.
#
# Please see the binary_c installation documentation
# in the doc/ directory of binary_c or
# http://personal.ph.surrey.ac.uk/~ri0005/doc/binary_c/binary_c.html
#
############################################################
############################################################
# 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.
#
############################################################
# define the binary_c project
#
project(
'binary_c','c',
default_options : [
'c_std=gnu99',
],
version : '2.1.5', # should agree with binary_c_version.h
)
############################################################
# require Meson 0.52.0 or later
#
find_program('meson',
version: '>=0.52.0')
############################################################
# require Ninja 1.8.2 or later
#
find_program('ninja',
version: '>=1.8.2')
############################################################
# compiler object
#
compiler = meson.get_compiler('c')
############################################################
# exit if compiler is buggy/unsupported
#
if compiler.get_id() == 'gcc' and \
compiler.version() == '4.7.4'
error('gcc 4.7.4 is buggy and hence unsupported : please upgrade!')
endif
############################################################
# System information
#
# e.g. cpu frequency
#
if(host_machine.cpu_family() == 'x86_64')
# test on 64-bit Intels
_cpufreq = compiler.run( '''
#include <stdio.h>
#include <cpuid.h>
int main(void)
{
/* code based on Linux' turbostat.c by Intel (GPL2) */
unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0, max_level = 0;
__get_cpuid(0, &max_level, &ebx, &ecx, &edx);
/* only works on Skylake (level 0x16) and later */
if (max_level >= 0x16)
{
unsigned int base_mhz = 0, max_mhz = 0, bus_mhz = 0;
__get_cpuid(0x16, &base_mhz, &max_mhz, &bus_mhz, &edx);
fprintf(stdout,"%u",max_mhz);
return 1;
}
else
{
fprintf(stdout,"0");
return 0;
}
}''')
else
_cpufreq = compiler.run('this should error')
endif
if _cpufreq.compiled() == true and _cpufreq.returncode() != 0
# use result of above code to extract CPU Mhz
cpufreq = _cpufreq.stdout()
else
# fallback script to use system tools
cpufreq = run_command('meson/cpu_frequency.sh').stdout().strip()
endif
message('CPU frequency ' + cpufreq + 'MHz')
############################################################
# 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'
############################################################
# binary_c version
#
binary_c_version = compiler.run('''
#include <stdio.h>
#include "''' + binary_c_src + '''/binary_c_version.h"
int main(int argc,char **argv)
{
fprintf(stdout,"%s",BINARY_C_VERSION);
return 0;
}''').stdout().strip()
############################################################
# the binary_c version should be the same as the project
# version: exit if not!
#
if binary_c_version != meson.project_version()
error('binary_c_version and meson.project_version are not the same : they should be!')
else
message('binary_c version is ' + binary_c_version)
endif
############################################################
# 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 = [
]
############################################################
# Data alignment: the longest native data type binary_c
# uses is double, so select this for alignment.
#
alignsize = '-DALIGNSIZE=' + compiler.alignment('double').to_string()
############################################################
# default C flags
#
cflags = [
alignsize
]
executable_build_flags = [
]
############################################################
# 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
foreach _flag : [ '-fbracket-depth=512' ]
if compiler.has_argument(_flag)
cflags += _flag
else
error('clang requires ' + _flag + ' to build, but this flag is not available')
endif
endforeach
# for some reason we require an extra fPIC
# when building the executable *only*
executable_build_flags += '-fPIC'
else
# gcc-specific flags
cflags += []
endif
############################################################
# system options
#
if cpufreq != ''
cflags += '-DCPUFREQ=' + cpufreq
endif
############################################################
# operating system flags
#
os = host_machine.system()
cflags += [
'-DOPERATING_SYSTEM=' + os
]
if os == 'linux'
# Linux system
cflags += [
'-DLINUX',
'-DPOSIX',
'-DLARGEFILE_SOURCE'
]
unix = true
posix = true
elif os == 'darwin'
# darwin (MacOSX)
cflags += [
'-DDARWIN',
'-DPOSIX',
]
unix = true
posix = true
elif os == 'windows'
# windows
cflags += [
'-DWINDOWS',
]
unix = false
posix = false
else
cflags += [
'-DUNKNOWN_OPERATING_SYSTEM'
]
unix = false
posix = false
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 (but gsl requires it)
'memoize',
'rinterpolate',
]
libs = [] # list sent to the compiler
foreach libname : _required_libraries
_dep = compiler.find_library(libname,
required:true)
if _dep.found()
libs += '-l' + libname
cflags += '-D__HAVE_LIB' + libname.to_upper() +'__'
# 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 = []
foreach gsl_libdir :run_command('sh','-c','gsl-config --libs').stdout().strip().split(' ')
if gsl_libdir.startswith('-L')
gsl_libdirs += gsl_libdir
endif
endforeach
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
############################################################
# 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
if run_command('sh','-c','meson/directory_exists.sh',idir).returncode() == 0
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
endif
endforeach
incdirs += [ my_incdirs ]
############################################################
# features which are converted into preprocessor flags (-D)
#
###########
# drand48 #
#
if compiler.has_header('stdlib.h',
args: cflags,
include_directories: include_directories(incdirs)\
) and \
compiler.sizeof('drand48_r',
prefix : '#include <stdlib.h>') > 0
cflags += '-D__HAVE_DRAND48__'
endif
############
# malloc.h #
#
if compiler.has_header('malloc.h',
args: cflags,
include_directories: include_directories(incdirs))
cflags += '-D__HAVE_MALLOC_H__'
endif
############
# setitimer
#
if compiler.has_header('sys/time.h',
args: cflags,
include_directories: include_directories(incdirs)) 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',
args: cflags,
include_directories: include_directories(incdirs))
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
##########################
# location of libiberty.h
#
if compiler.has_header('libiberty.h',
args: cflags,
include_directories: include_directories(incdirs))
# Fedora
cflags += '-D__HAVE_LIBIBERTYH__'
elif compiler.has_header('libiberty/libiberty.h',
args: cflags,
include_directories: include_directories(incdirs))
# Debian and derivatives e.g. Ubuntu
cflags += '-D__HAVE_LIBIBERTY_LIBIBERTYH__'
else
error('cannot find libiberty.h at either <libiberty.h> or <libiberty/libiberty.h>')
endif
############################################################
# 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
# 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 ],
)
# append usage flags for normal build
cflags += use_pch_cflags
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)
#
# NB will only work on Unix-compatibles
#
# TODO use meson's installation instead of my own
if unix
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'
]
else
binary_c_legacy_install_cmd = [
'echo','"Legacy install not supported on this operating system"'
]
endif
############################################################
#
# Convenience for Rob: try to locate binary_grid2.pm and
# hence touch (and rebuild) it when the shared library is
# rebuild
#
if os == 'linux' and \
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
#
# NB only works if we can symlink, i.e. unix systems
#
if unix
libbinary_c_symlink = custom_target(
'libbinary_c_symlink',
build_by_default: false,
output: [ 'libbinary_c_symlink' ],
command: [
'sh',
'-c',
'../meson/symlink_libbinary_c.sh'
],
)
endif
############################################################
#
# make binary_c objects
#
# We put the objects in this build target so they can be
# shared by the executable and library builds.
#
# We're not really interested in the static libraries that
# result, but they are joined later into one big library.
#
# Note: in theory this step is not required, we could just
# link all the .o files. The problem is that there are
# many .o files and this makes the command line too long
# (even in Linux) which means the linking fails.
#
binary_c_subdir_objects = []
src_subdirs = run_command('meson/source_directories.sh').stdout().strip().split('\n')
src_root_sourcefiles = run_command('meson/source_files.sh','./src').stdout().strip().split('\n')
# build the objects from each subdir into their own static library
foreach src_subdir : src_subdirs
_target_name = 'binary_c_objects_' + src_subdir.underscorify()
#message('src subdir : ' + src_subdir)
# count the number of source and header files in this subdir
_source_count = run_command('meson/count_source_files.sh',src_subdir).stdout().strip().to_int()
_header_count = run_command('meson/count_header_files.sh',src_subdir).stdout().strip().to_int()
#message('source file count ' + _source_count.to_string())
#message('header file count ' + _header_count.to_string())
# if we have source files, proceed
if _source_count > 0
# get a list of the source files for compilation
_sources = run_command('meson/source_files.sh',src_subdir).stdout().strip().split('\n')
#message('sources : ' + ' '.join(_sources))
# get the headers (or an empty list if there are none)
if _header_count > 0
_headers = run_command('meson/header_files.sh',src_subdir).stdout().strip().split('\n')
else
_headers = []
endif
#message('headers : ' + ' '.join(_sources))
# make the build target and append to binary_c_subdir_objects
binary_c_subdir_objects += build_target(
_target_name,
build_by_default: false,
pic : true,
target_type : 'static_library',
sources : [
precompiled_headers,
_sources,
_headers,
],
include_directories: include_directories(incdirs),
c_args : [
cflags,
quoted_cflags_list,
],
link_language: 'c',
)
endif
endforeach
############################################################
#
# binary_c objects references all the binary_c_subdir_objects,
# as well as the src/*.c files, so makes one static library
# with everything in it.
#
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 : [
precompiled_headers,
src_root_sourcefiles,
h_sourcefiles,
],
include_directories: include_directories(incdirs),
dependencies : [
dependencies,
],
c_args : [
cflags,
],
objects : [
data_objects,
],
link_args : [
libs,
],
link_with : [
binary_c_subdir_objects,
],
link_language: 'c',
)
############################################################
#
# 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 : [
precompiled_headers,
c_main_sourcefiles,
],
include_directories: include_directories(incdirs),
dependencies : [
dependencies
],
c_args : [
cflags,
quoted_cflags_list,
executable_build_flags,
],
objects : data_objects,
link_args : [
libs,
],
link_with: [
binary_c_objects
],
)
############################################################
# install both binary_c and libbinary_c.so in their legacy
# locations. run: ninja binary_c_install_legacy
#
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,
],
)