diff --git a/SConstruct b/SConstruct index 1bb09da..d9c1c41 100644 --- a/SConstruct +++ b/SConstruct @@ -1,304 +1,304 @@ # ------------------------------------------------------------------------------ # Imports # ------------------------------------------------------------------------------ from __future__ import print_function import os from os.path import join, abspath, basename from version import write_info_file # ------------------------------------------------------------------------------ EnsurePythonVersion(2, 7) EnsureSConsVersion(2, 4) # ------------------------------------------------------------------------------ # Helper functions # ------------------------------------------------------------------------------ def detect_fftw(env): """Detect fftw on clusters""" fftw_include = "" fftw_library = "" # If FFTW is provided by module system (on clusters) if 'FFTW_ROOT' in env['ENV']: fftw_include = join(env['ENV']['FFTW_ROOT'], 'include') fftw_library = join(env['ENV']['FFTW_ROOT'], 'lib') # Setting up FFTW env['FFTW_LIBRARY_WISH'] = ['main', 'omp'] env['FFTW_INCLUDE_DIR'] = fftw_include env['FFTW_LIBRARY_DIR'] = fftw_library env.Tool(fftw) # ------------------------------------------------------------------------------ def detect_cuda(env): """Detect cuda on clusters""" if 'CUDA_ROOT' in env['ENV']: env['CUDA_TOOLKIT_PATH'] = env['ENV']['CUDA_ROOT'] else: env['CUDA_TOOLKIT_PATH'] = '/opt/cuda' env['CUDA_COMPONENTS'] = ['cufft'] env['CUDA_ARCH_FLAG'] = '-arch=sm_60' colors = env['COLOR_DICT'] if not env['verbose']: - env['NVCCCOMSTR'] = u'{0}[Compiling (cuda)] {1}$SOURCE{2}'.format(colors['green'], - colors['blue'], - colors['end']) + env['NVCCCOMSTR'] = env['SHCXXCOMSTR'] env['SHLINKCOMSTR'] = u'{0}[Linking (cuda)] {1}$TARGET{2}'.format(colors['purple'], colors['blue'], colors['end']) env.AppendUnique(CXXFLAGS="-expt-extended-lambda") # experimental lambda support env.AppendUnique(CXXFLAGS="-expt-relaxed-constexpr") # experimental lambda support if env['build_type'] == 'debug': env.AppendUnique(CXXFLAGS="-G") env.Tool('nvcc') # ------------------------------------------------------------------------------ def detect_boost(env): """Detect boost on clusters""" if 'BOOST_ROOT' in env['ENV']: env['BOOST_INCLUDE_DIR'] = join(env['ENV']['BOOST_ROOT'], 'include') env.Tool(boost) # ------------------------------------------------------------------------------ def detect_thrust(env): """Detect cuda on clusters""" if 'CUDA_ROOT' in env['ENV']: env['THRUST_INCLUDE_DIR'] = join(env['ENV']['CUDA_ROOT'], 'include') else: env['THRUST_INCLUDE_DIR'] = '/opt/cuda/include' if basename(env['CXX']) == "clang++": env.AppendUnique(CXXFLAGS=["-Wno-unused-local-typedef"]) env.Tool(thrust) # ------------------------------------------------------------------------------ def gen_print(action_string, color_string, env): """Generic function for creating pretty compile output""" if env['verbose']: return None def print_fun(command, target, source, env): colors = env['COLOR_DICT'] print("{}[{}] {}{}".format(colors[color_string], action_string, colors['end'], target[0])) return print_fun # ------------------------------------------------------------------------------ # Main compilation # ------------------------------------------------------------------------------ # Compilation colors colors = { 'cyan': '\033[96m', 'purple': '\033[95m', 'blue': '\033[94m', 'green': '\033[92m', 'yellow': '\033[93m', 'red': '\033[91m', 'end': '\033[0m' } # Inherit all environment variables (for CXX detection, etc.) main_env = Environment(ENV=os.environ) main_env['COLOR_DICT'] = colors # Compiler detection compiler_default = 'g++' if 'CXX' in os.environ: compiler_default = os.environ['CXX'] + print("Detected default compiler {} from environment".format(compiler_default)) # Build variables vars = Variables('build-setup.conf') vars.Add(EnumVariable('build_type', 'Build type', 'release', allowed_values=('release', 'profiling', 'debug'), ignorecase=2)) vars.Add(EnumVariable('backend', 'Thrust backend', 'omp', allowed_values=('omp', 'cuda'), ignorecase=2)) vars.Add(EnumVariable('sanitizer', 'Sanitizer type', 'none', allowed_values=('none', 'memory', 'leaks', 'address'), ignorecase=2)) vars.Add('prefix', 'Prefix where to install', '/usr/local') vars.Add('CXX', 'Compiler', compiler_default) vars.Add('py_exec', 'Python executable', 'python') + +# Cosmetic vars.Add(BoolVariable('verbose', 'Activate verbosity', False)) -vars.Add(BoolVariable('build_doc', 'Build documentation', False)) -vars.Add(BoolVariable('use_criterion', 'Use criterion tests', True)) vars.Add(BoolVariable('color', 'Color the non-verbose compilation output', False)) + +# Tamaas components +vars.Add(BoolVariable('build_doc', 'Build documentation', False)) +vars.Add(BoolVariable('build_tests', 'Build test suite', False)) +vars.Add(BoolVariable('build_python', 'Build python wrapper', False)) + +# Dependencies +vars.Add(BoolVariable('use_googletest', 'Build tests using GTest', False)) + vars.Update(main_env) Help(vars.GenerateHelpText(main_env)) # Save all options, not just those that differ from default with open('build-setup.conf', 'w') as setup: for key in vars.keys(): setup.write("{} = '{}'\n".format(key, main_env[key])) build_type = main_env['build_type'] build_dir = 'build-' + main_env['build_type'] print("Building in " + build_dir) verbose = main_env['verbose'] # Remove colors if not set if not main_env['color']: for key in colors: colors[key] = '' # Setting object suffix main_env['SHOBJSUFFIX'] = '.o' if not verbose: - main_env['SHCXXCOMSTR'] = u'{0}[Compiling] {1}$SOURCE'.format(colors['green'], - colors['end']) + main_env['SHCXXCOMSTR'] = u'{0}[Compiling ($SHCXX)] {1}$SOURCE'.format(colors['green'], + colors['end']) main_env['SHLINKCOMSTR'] = u'{0}[Linking] {1}$TARGET'.format(colors['purple'], colors['end']) - main_env['SWIGCOMSTR'] = u'{0}[Swig] {1}$SOURCE'.format(colors['yellow'], - colors['end']) # Include paths main_env.AppendUnique(CPPPATH=['#/src', '#/src/core', '#/src/bem', '#/src/surface', '#/src/python', '#/src/percolation', '#/src/model', '#/src/solvers', '#/src/gpu', '#/python']) # Changing the shared object extension main_env['SHOBJSUFFIX'] = '.o' # Back to gcc if cuda is activated if main_env['backend'] == "cuda": main_env['CXX'] = "g++" # OpenMP flags - compiler dependent omp_libs = { "g++": ["gomp"], + "c++": ["gomp"], "clang++": [], "icpc": [] } omp_flags = { "g++": ["-fopenmp"], + "c++": ["-fopenmp"], "clang++": ["-fopenmp=libomp"], "icpc": ["-qopenmp"] } omp_lib = omp_libs[main_env['CXX']] omp_flag = omp_flags[main_env['CXX']] main_env.AppendUnique(LIBS=omp_lib) main_env.AppendUnique(LINKFLAGS=omp_flag) # Flags and options main_env.AppendUnique(CXXFLAGS=['-std=c++11', '-Wall', omp_flag]) if main_env['backend'] == 'omp': main_env.AppendUnique(CPPDEFINES=["THRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_OMP"]) elif main_env['backend'] == 'cuda': main_env.AppendUnique(CPPDEFINES=["THRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_CUDA"]) main_env.AppendUnique(CPPDEFINES=['USE_CUDA']) # Adding compile flags defined in evironment if 'CXXFLAGS' in os.environ: main_env.AppendUnique(CXXFLAGS=Split(os.environ['CXXFLAGS'])) if build_type == 'debug': main_env.AppendUnique(CPPDEFINES=['TAMAAS_DEBUG']) # Compilation flags cxxflags_dict = { "debug": Split("-g -O0"), "profiling": Split("-g -pg -O2 -fno-omit-frame-pointer"), "release": Split("-O3") } # Link flags for shared libs shlinkflags_dict = { "debug": Split(""), "profiling": ['-pg'], "release": [] } if main_env['sanitizer'] != 'none': if main_env['backend'] == 'cuda': print("Sanitizers with cuda are not yet supported!") Exit(1) cxxflags_dict[build_type].append('-fsanitize={}'.format(main_env['sanitizer'])) shlinkflags_dict[build_type].append('-fsanitize={}'.format(main_env['sanitizer'])) main_env.AppendUnique(CXXFLAGS=cxxflags_dict[build_type]) main_env.AppendUnique(SHLINKFLAGS=shlinkflags_dict[build_type]) main_env.AppendUnique(LINKFLAGS=shlinkflags_dict[build_type]) main_env['LIBPATH'] = [abspath(join(build_dir, 'src'))] main_env['RPATH'] = "$LIBPATH" detect_fftw(main_env) detect_boost(main_env) detect_thrust(main_env) # Activate cuda if needed if main_env['backend'] == 'cuda': detect_cuda(main_env) # Writing information file write_info_file("src/tamaas_info.cpp") # Saving the env file env_content = """export PYTHONPATH=$PYTHONPATH:{0}/python export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{0}/src """ def write_env_file(target, source, env): """Builder to write content to file""" with open(str(target[0]), 'w') as env_file: env_file.write(env_content.format(abspath(build_dir))) main_env['gen_print'] = gen_print env_file_env = main_env.Clone(PRINT_CMD_LINE_FUNC=gen_print("Writing", "cyan", main_env)) # Need to have a command and manage tamaas_environement.sh as target because # the build directory does not always exist env_file_env.Command(join(build_dir, 'tamaas_environement.sh'), None, write_env_file) Export('main_env') # Building subdirs def subdir(dir): return SConscript(join(dir, 'SConscript'), variant_dir=join(build_dir, dir), duplicate=True) -subdirs2target = {'src': "tamaas", - 'python': "wrapper", - 'tests': "tests"} - -subdirs2target = {'src': "tamaas", - 'python': "wrapper", - 'tests': "tests"} - -for dir in subdirs2target: - target = subdir(dir) - main_env.Alias(subdirs2target[dir], target) +# Building Tamaas library +subdir('src') -# Building documentation -if main_env['build_doc']: - subdir('doc') +# Building Tamaas extra components +for dir in ['python', 'tests', 'doc']: + if main_env['build_{}'.format(dir)]: + subdir(dir) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 03f6261..c96e1c7 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -1,132 +1,114 @@ from SCons.Script import * from os.path import * import os def fftw(env): """A Tool to search for fftw headers and libraries""" if env.GetOption('clean'): return env.SetDefault(FFTW_VERSION='3') env.SetDefault(FFTW_LIBRARY_WISH=[]) print("Building for fftw version {}".format(env['FFTW_VERSION'])) if 'FFTW_INCLUDE_DIR' in env: env.AppendUnique(CPPPATH=env['FFTW_INCLUDE_DIR']) if 'FFTW_LIBRARY_DIR' in env: env.AppendUnique(LIBPATH=env['FFTW_LIBRARY_DIR']) version = env['FFTW_VERSION'] if version == "2": lib_names = {'main': 'fftw'} inc_names = ['fftw.h'] else: lib_names = {'main': 'fftw3', 'thread': 'fftw3_threads', 'omp': 'fftw3_omp'} inc_names = ['fftw3.h'] try: libs = [lib_names[i] for i in env['FFTW_LIBRARY_WISH']] except: raise SCons.Errors.StopError( 'Incompatible wishlist {0} from version {1}'.format( env['FFTW_LIBRARY_WISH'], env['FFTW_VERSION'])) env.AppendUnique(LIBS=libs) if version == "2": env.Append(LIBS='m') conf = Configure(env) if not conf.CheckLibWithHeader(libs, inc_names, 'c++'): raise SCons.Errors.StopError( 'Failed to find libraries {0} or ' 'headers {1}.'.format(str(lib_names), str(inc_names))) env = conf.Finish() -def criterion(env): - """A tool to configure for Criterion (test suite)""" - if env.GetOption('clean'): - return - - env.AppendUnique(CPPPATH=[Dir("#third-party/Criterion/include")], - LIBPATH=[abspath(str(Dir("#third-party/Criterion/build")))]) - conf = Configure(env) - # import pdb - # pdb.set_trace() - if not conf.CheckLibWithHeader('criterion', - 'criterion/criterion.h', - 'c++'): - raise SCons.Errors.StopError( - 'Failed to find library Criterion or criterion.h header in third-party') - env = conf.Finish() - - def boost(env): """A tool to confugure for boost""" if env.GetOption('clean'): return if 'BOOST_INCLUDE_DIR' in env: env.AppendUnique(CPPPATH=env['BOOST_INCLUDE_DIR']) conf = Configure(env) if not conf.CheckCXXHeader('boost/preprocessor/seq.hpp'): raise SCons.Errors.StopError( 'Failed to find Boost library') env = conf.Finish() def thrust(env): """A tool to confugure for thrust""" if env.GetOption('clean'): return if 'THRUST_INCLUDE_DIR' in env: env.AppendUnique(CPPPATH=env['THRUST_INCLUDE_DIR']) conf = Configure(env) if not conf.CheckCXXHeader('thrust/version.h'): raise SCons.Errors.StopError( 'Failed to find Thrust library') env = conf.Finish() def tamaas(env): """Sets correct environement variables for Tamaas. - 'TAMAAS_PATH' is list of potential tamaas repositories - 'TAMAAS_BUILD_TYPE' is the build type of tamaas (release, debug, profiling)""" if env.GetOption("clean"): return try: build_type = env['TAMAAS_BUILD_TYPE'] except: build_type = "release" if 'TAMAAS_PATH' not in env: print("Please set the 'TAMAAS_PATH' variable in your scons environment") Exit(1) tm_path = env['TAMAAS_PATH'] include_dir = tm_path + '/src' subdirs = "core model solvers gpu bem surface".split(" ") include_dirs = [abspath(join(include_dir, x)) for x in subdirs] lib_dir = join(tm_path, 'build-{}/src'.format(build_type)) env.AppendUnique(CPPPATH = include_dirs) env.AppendUnique(CPPPATH = ['/opt/cuda/include']) # TODO fix this dirty hack env.AppendUnique(LIBPATH = [abspath(lib_dir)]) env.AppendUnique(RPATH = [abspath(lib_dir)]) env.AppendUnique(CXXFLAGS = ["-std=c++11"]) conf = Configure(env) if not conf.CheckLibWithHeader("Tamaas", "tamaas.hh", language="CXX"): raise SCons.Errors.StopError( "Tamaas ({}) not found in {}".format(build_type, abspath(tm_path))) env['TAMAAS_PATH'] = abspath(tm_path) env = conf.Finish() diff --git a/tests/SConscript b/tests/SConscript index 0c47a5a..6c86369 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -1,60 +1,102 @@ from __future__ import print_function -from os.path import join, abspath +import os +from os.path import join, abspath, basename +# ------------------------------------------------------------------------------ def copyComStr(env, main): if 'SHCXXCOMSTR' in main: env['CXXCOMSTR'] = main['SHCXXCOMSTR'] if 'SHLINKCOMSTR' in main: env['LINKCOMSTR'] = main['SHLINKCOMSTR'] +# ------------------------------------------------------------------------------ +def make_python_tests(env): + """Copy python tests to build directory""" + test_env = env.Clone( + PRINT_CMD_LINE_FUNC=main_env['gen_print']("Copying", "red", main_env)) + + test_files = Split(""" + run_tests.sh + test_hertz_pressure.py + test_westergaard.py + test_patch_westergaard.py + test_surface.py + test_autocorrelation.py + test_hertz_disp.py + test_hertz_kato.py + test_saturated_pressure.py + test_bem_grid.py + test_flood_fill.py + """) + + src_dir = "#/tests" + build_dir = 'build-' + main_env['build_type'] + '/tests' + + for file in test_files: + source = join(src_dir, file) + test_env.Command(file, source, Copy("$TARGET", "$SOURCE")) + + +# ------------------------------------------------------------------------------ +def compile_google_test(env, gtest_dir): + gtest_obj = env.Object('gtest.o', [join(gtest_dir, "src/gtest-all.cc")]) + env.StaticLibrary('gtest', gtest_obj) + +# ------------------------------------------------------------------------------ +def make_google_tests(env): + gtest_dir = '#/third-party/googletest/googletest' + gtest_dir = str(Dir(gtest_dir)) + + # Checking for google test repo + if len(os.listdir(gtest_dir)) == 0: + print("Googletest submodule was not initialized\n" + + "Run 'git submodule update --init --recursive third-party/googletest'") + Exit(1) + + gtest_env = env.Clone(CPPPATH=[gtest_dir], + CXXFLAGS=['-pthread', '-isystem', + join(gtest_dir, "include")]) + + compile_google_test(gtest_env, gtest_dir) + + env.AppendUnique(LIBS=['Tamaas'], + CXXFLAGS=gtest_env['CXXFLAGS']) + + # google_test_files = Split(""" + # test_grid.cpp + # test_loop.cpp + # test_fft.cpp + # test_model.cpp + # """) + google_test_files = Split(""" + test_rough_surface.cpp + """) + + google_test_files += ['libgtest.a'] + env.Program('test_gtest_all', google_test_files) + + +# ------------------------------------------------------------------------------ +def make_bare_tests(env): + pass + # env.Program("test_rough_surface.cpp") + +# ------------------------------------------------------------------------------ Import('main_env') -test_env = main_env.Clone( - PRINT_CMD_LINE_FUNC=main_env['gen_print']("Copying", "red", main_env)) - -test_files = Split(""" -run_tests.sh -test_hertz_pressure.py -test_westergaard.py -test_patch_westergaard.py -test_surface.py -test_autocorrelation.py -test_hertz_disp.py -test_hertz_kato.py -test_saturated_pressure.py -test_bem_grid.py -test_flood_fill.py -""") - -src_dir = "#/tests" -build_dir = 'build-' + main_env['build_type'] + '/tests' - -for file in test_files: - source = join(src_dir, file) - test_env.Command(file, source, Copy("$TARGET", "$SOURCE")) - -# Do not use criterion for this one (benchmark for SCITAS) -bare_env = main_env.Clone() -copyComStr(bare_env, main_env) -bare_env.AppendUnique(LIBS="Tamaas") -bare_env.Program("test_rough_surface.cpp") - -if main_env['use_criterion']: - crit_env = main_env.Clone(tools=[criterion]) - copyComStr(crit_env, main_env) - - crit_env.AppendUnique(LIBS=['Tamaas'], - LIBPATH=[abspath('build-' + main_env['build_type'] + '/src')]) - - cpp_test_files = Split(""" - test_grid.cpp - test_loop.cpp - test_fft.cpp - test_model.cpp - """) - - for file in cpp_test_files: - crit_env.Program(file) +# Setup of test environment +test_env = main_env.Clone() +test_env.AppendUnique(LIBS=['Tamaas'], + LIBPATH=[abspath('build-' + main_env['build_type'] + '/src')]) +copyComStr(test_env, main_env) + + +# Building tests that do not require any third party +make_bare_tests(test_env) + +# Building google tests +if test_env['use_googletest']: + make_google_tests(test_env)