diff --git a/SConstruct b/SConstruct index b6ff920..4b46b08 100644 --- a/SConstruct +++ b/SConstruct @@ -1,311 +1,313 @@ +# -*- coding: utf-8 -*- + # @file # @section LICENSE # # Copyright (©) 2016-19 EPFL (École Polytechnique Fédérale de Lausanne), # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # ------------------------------------------------------------------------------ # Imports # ------------------------------------------------------------------------------ from __future__ import print_function import os from os.path import join, abspath from version import write_info_file from detect import FindFFTW, FindBoost, FindThrust, FindCuda, FindExpolit # ------------------------------------------------------------------------------ EnsurePythonVersion(2, 7) EnsureSConsVersion(2, 4) # ------------------------------------------------------------------------------ def detect_dependencies(env): """Detect all dependencies""" FindFFTW(env, ['omp']) FindBoost(env, ['boost/preprocessor/seq.hpp']) FindExpolit(env) thrust_var = 'THRUST_ROOT' # Take cuda version of thrust if available if 'CUDA_ROOT' in env['ENV']: thrust_var = 'CUDA_ROOT' FindThrust(env, env['backend'], thrust_var) # Activate cuda if needed if env['backend'] == 'cuda': FindCuda(env) # ------------------------------------------------------------------------------ # 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 = os.getenv('CXX', 'g++') # Build variables vars = Variables('build-setup.conf') vars.AddVariables( EnumVariable('build_type', 'Build type', 'release', allowed_values=('release', 'profiling', 'debug'), ignorecase=2), EnumVariable('backend', 'Thrust backend', 'omp', allowed_values=('omp'), # allowed_values=('omp', 'cuda'), ignorecase=2), EnumVariable('sanitizer', 'Sanitizer type', 'none', allowed_values=('none', 'memory', 'leaks', 'address'), ignorecase=2), PathVariable('prefix', 'Prefix where to install', '/usr/local'), # Dependencies paths PathVariable('FFTW_ROOT', 'FFTW custom path', os.getenv('FFTW_ROOT', ''), PathVariable.PathAccept), PathVariable('THRUST_ROOT', 'Thrust custom path', os.getenv('THRUST_ROOT', ''), PathVariable.PathAccept), PathVariable('BOOST_ROOT', 'Boost custom path', os.getenv('BOOST_ROOT', ''), PathVariable.PathAccept), PathVariable('CUDA_ROOT', 'Cuda custom path', os.getenv('CUDA_ROOT', ''), PathVariable.PathAccept), # Dependencies provided as submodule get different default PathVariable('GTEST_ROOT', 'Googletest custom path', os.getenv('GTEST_ROOT', '#third-party/googletest/googletest'), PathVariable.PathAccept), PathVariable('PYBIND11_ROOT', 'Pybind11 custom path', os.getenv('PYBIND11_ROOT', '#third-party/pybind11/include'), PathVariable.PathAccept), PathVariable('EXPOLIT_ROOT', 'Expolit custom path', os.getenv('EXPOLIT_ROOT', '#third-party/expolit/include'), PathVariable.PathAccept), # Executables ('CXX', 'Compiler', compiler_default), ('py_exec', 'Python executable', 'python3'), # Cosmetic BoolVariable('verbose', 'Activate verbosity', False), BoolVariable('color', 'Color the non-verbose compilation output', False), # Tamaas components BoolVariable('build_doc', 'Build documentation', False), BoolVariable('build_tests', 'Build test suite', False), BoolVariable('build_python', 'Build python wrapper', True), # Dependencies BoolVariable('use_googletest', 'Build tests using GTest', False) ) # Set variables of environment 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])) main_env['should_configure'] = \ not main_env.GetOption('clean') and not main_env.GetOption('help') build_type = main_env['build_type'] build_dir = 'build-' + main_env['build_type'] if main_env['should_configure']: 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] = '' if not verbose: 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']) # Include paths main_env.AppendUnique(CPPPATH=['#/src', '#/src/core', '#/src/bem', '#/src/surface', '#/src/python', '#/src/percolation', '#/src/model', '#/src/model/elasto_plastic', '#/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++" compiler_aliases = { "c++": "g++", "g++-7": "g++", "g++-6": "g++", "clang++-6.0": "clang++", } def cxx_alias(alias): return compiler_aliases.get(alias, alias) # OpenMP flags - compiler dependent omp_libs = { "g++": ["gomp"], "clang++": [], "icpc": [] } omp_flags = { "g++": ["-fopenmp"], "clang++": ["-fopenmp=libomp"], "icpc": ["-qopenmp"] } cxx = cxx_alias(main_env['CXX']) omp_lib = omp_libs[cxx] omp_flag = omp_flags[cxx] main_env.AppendUnique(LIBS=omp_lib) main_env.AppendUnique(LINKFLAGS=omp_flag) # Flags and options main_env.AppendUnique(CXXFLAGS=['-std=c++14', '-Wall', omp_flag]) if main_env['build_python']: main_env.AppendUnique(CPPDEFINES=['USE_PYTHON']) # Adding compile flags defined in evironment main_env.AppendUnique(CXXFLAGS=Split(os.getenv('CXXFLAGS', ""))) if build_type == 'debug': main_env.AppendUnique(CPPDEFINES=['TAMAAS_DEBUG']) # Compilation flags cxxflags_dict = { "debug": Split("-g -O0"), "profiling": Split("-g -O3 -fno-omit-frame-pointer"), "release": Split("-O3") } # Link flags for shared libs shlinkflags_dict = { "debug": Split(""), "profiling": Split("-g -O3 -fno-omit-frame-pointer"), "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" if main_env['should_configure']: detect_dependencies(main_env) # Writing information file write_info_file("src/tamaas_info.cpp", main_env['build_type']) def write_env_file(target, source, env): """Builder to write content to file""" env_content = """ export PYTHONPATH=$PYTHONPATH:{0}/python export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:{0}/src """ with open(str(target[0]), 'w') as env_file: env_file.write(env_content.format(abspath(build_dir))) # Saving the env file 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) # Building subdirs def subdir(dir): return SConscript(join(dir, 'SConscript'), variant_dir=join(build_dir, dir), duplicate=True) # Building Tamaas library Export('main_env') subdir('src') # Building Tamaas extra components for dir in ['python', 'tests', 'doc']: if main_env['build_{}'.format(dir)] and not main_env.GetOption('help'): subdir(dir) diff --git a/doc/SConscript b/doc/SConscript index 84fc09d..48ed23c 100644 --- a/doc/SConscript +++ b/doc/SConscript @@ -1,35 +1,56 @@ +# -*- coding: utf-8 -*- + +# @file +# @section LICENSE +# +# Copyright (©) 2016-19 EPFL (École Polytechnique Fédérale de Lausanne), +# Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + from os.path import join, exists from subprocess import call Import('main_env') doxygen_tool = str(Dir('#site_scons/site_tools/doxygen')) if not exists(doxygen_tool): print("Cloning scons doxygen builder") # Checking if Mercurial is installed env = Environment() conf = Configure(env) if not conf.CheckProg('hg'): print("Could not find Mercurial needed to clone the doxygen tool") Exit(1) try: call(['hg', 'clone', 'https://bitbucket.org/russel/scons_doxygen', doxygen_tool]) except: print("Error while running Mercurial to clone the doxygen tool") Exit(1) env_doxygen = main_env.Clone( tools = ['default', 'doxygen'] ) src_dir = "#/doc/doxygen" file = "Doxyfile" build_dir = 'build-' + main_env['build_type'] + '/doc/doxygen' Command(join("doxygen", file), join(src_dir, file), [ Mkdir(build_dir), Copy("$TARGET", "$SOURCE") ] ) env_doxygen.Doxygen("doxygen/Doxyfile") diff --git a/examples/SConstruct b/examples/SConstruct index 7866343..00a3f4d 100644 --- a/examples/SConstruct +++ b/examples/SConstruct @@ -1,28 +1,30 @@ +# -*- coding: utf-8 -*- + # @file # @section LICENSE # # Copyright (©) 2016-19 EPFL (École Polytechnique Fédérale de Lausanne), # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import os env = Environment(CC = 'g++', CXXFLAGS = ['-std=c++11', '-g', '-fopenmp', '-O3'], TOOLS = ['default', 'tamaas'], TAMAAS_PATH = '../build-release/', ENV = os.environ) env.Program('rough_surface.cc') diff --git a/python/SConscript b/python/SConscript index 06764c5..705549f 100644 --- a/python/SConscript +++ b/python/SConscript @@ -1,67 +1,67 @@ +# -*- coding: utf-8 -*- + # @file # @section LICENSE # # Copyright (©) 2016-19 EPFL (École Polytechnique Fédérale de Lausanne), # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from __future__ import print_function -import SCons - -from os.path import abspath, join -from SCons.Script import Import, Depends, Split, Copy +from os.path import abspath +from SCons.Script import Import, Split, Copy Import('main_env') # Pybind11 wrapper env_pybind = main_env.Clone(SHLIBPREFIX='') env_pybind.Tool(pybind11) pybind_sources = Split(""" tamaas_module.cpp wrap/core.cpp wrap/percolation.cpp wrap/surface.cpp wrap/model.cpp wrap/solvers.cpp wrap/test_features.cpp """) if main_env['CXX'] != 'icpc': pybind_sources += ["wrap/bem.cpp"] env_pybind.AppendUnique(CPPDEFINES="LEGACY_BEM") # Building the pybind library tamaas_wrap = env_pybind.SharedLibrary( target='tamaas/_tamaas', source=pybind_sources, LIBS=['Tamaas'], RPATH=[abspath('../src')] ) # For some reason link happens too early Import('libTamaas') env_pybind.Depends(tamaas_wrap, libTamaas) # Copying the __init__.py file with extra python classes copy_env = env_pybind.Clone( PRINT_CMD_LINE_FUNC=main_env['gen_print']("Copying", "red", main_env)) copy_env.Command(Dir('tamaas'), Dir('#python/tamaas'), Copy("$TARGET", "$SOURCE")) copy_env.Command('setup.py', '#python/setup.py', Copy("$TARGET", "$SOURCE")) # Cleaning the tamaas python package main_env.Execute(Delete('tamaas')) diff --git a/site_scons/detect.py b/site_scons/detect.py index 535712a..1a6d06d 100644 --- a/site_scons/detect.py +++ b/site_scons/detect.py @@ -1,208 +1,210 @@ +# -*- coding: utf-8 -*- + # @file # @section LICENSE # # Copyright (©) 2016-19 EPFL (École Polytechnique Fédérale de Lausanne), # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import SCons from os.path import join, abspath, exists, isdir, basename from SCons.Script import Configure # ------------------------------------------------------------------------------ def _get_path(env, ext, module_var): path = "" if module_var in env and env[module_var] != "": root = abspath(env[module_var]) if not exists(root) or not isdir(root): raise RuntimeError("{} is set to a non-existing path '{}'".format( module_var, root)) path = join(root, ext) if not exists(path) or not isdir(path): raise RuntimeError("{} does not contain '{}' directory".format( module_var, ext)) return [path] # ------------------------------------------------------------------------------ def FindFFTW(env, components=[], module_var='FFTW_ROOT'): """Find FFTW3 and set relevant environment variables""" if not env.get('should_configure', True): return fftw_vars = {} fftw_vars['CPPPATH'] = _get_path(env, 'include', module_var) fftw_vars['LIBPATH'] = _get_path(env, 'lib', module_var) # Setting up FFTW wishes = ['main'] + components # Components lib_names = {'main': 'fftw3', 'thread': 'fftw3_threads', 'omp': 'fftw3_omp'} inc_names = ['fftw3.h'] # Checking list of wishes try: libs = [lib_names[i] for i in wishes] except KeyError: raise SCons.Errors.StopError( 'Incompatible FFTW wishlist {0}'.format(wishes)) fftw_vars['LIBS'] = libs conf_env = env.Clone(**fftw_vars) conf = Configure(conf_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))) conf_env = conf.Finish() # Update modified variables env.AppendUnique(**fftw_vars) # ------------------------------------------------------------------------------ def FindBoost(env, headers=['boost/version.hpp'], module_var='BOOST_ROOT'): """Find Boost and set relevant environment variables""" if not env.get('should_configure', True): return boost_vars = {} boost_vars['CPPPATH'] = _get_path(env, 'include', module_var) conf_env = env.Clone(**boost_vars) conf = Configure(conf_env) if not conf.CheckCXXHeader(headers): raise SCons.Errors.StopError( 'Failed to find Boost headers {}'.format(headers)) conf_env = conf.Finish() # Update modified variables env.AppendUnique(**boost_vars) # ------------------------------------------------------------------------------ def FindThrust(env, backend='omp', module_var='THRUST_ROOT'): """Find Thrust and set relevant environment variables""" if not env.get('should_configure', True): return thrust_vars = {} thrust_vars['CPPPATH'] = _get_path(env, 'include', module_var) if basename(env['CXX']) == "clang++": thrust_vars['CXXFLAGS'] = ["-Wno-unused-local-typedef"] if backend == 'omp': thrust_vars['CPPDEFINES'] = [ "THRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_OMP" ] elif backend == 'cuda': thrust_vars['CPPDEFINES'] = [ "THRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_CUDA", "USE_CUDA" ] else: raise SCons.Errors.StopError( 'Unknown thrust backend "{}"'.format(backend)) conf_env = env.Clone(**thrust_vars) conf = Configure(conf_env) if not conf.CheckCXXHeader('thrust/version.h'): raise SCons.Errors.StopError( 'Failed to find Thrust') conf_env = conf.Finish() # Update modified variables env.AppendUnique(**thrust_vars) # ------------------------------------------------------------------------------ def FindCuda(env, module_var="CUDA_ROOT"): """Detect cuda on clusters""" if not env.get('should_configure', True): return if 'CUDA_ROOT' in env: env['CUDA_TOOLKIT_PATH'] = _get_path(env, '', module_var) 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'] = 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 "-expt-relaxed-constexpr", # experimental constexpr ]) if env['build_type'] == 'debug': env.AppendUnique(CXXFLAGS="-G") env.Tool('nvcc') # ------------------------------------------------------------------------------ def FindGTest(env): """A tool to configure GoogleTest""" if not env.get('should_configure', True): return conf = Configure(env) if not conf.CheckCXXHeader('gtest/gtest.h'): raise SCons.Errors.StopError( 'Failed to find GoogleTest header\n' + "Run 'git submodule update --init --recursive " + "third-party/googletest'") env = conf.Finish() # ------------------------------------------------------------------------------ def FindExpolit(env): """A tool to configure Expolit""" if not env.get('should_configure', True): return expolit_vars = {"CPPPATH": "#/third-party/expolit/include"} conf_env = env.Clone(**expolit_vars) conf = Configure(conf_env) if not conf.CheckCXXHeader('expolit/expolit'): raise SCons.Errors.StopError( 'Failed to find Expolit header\n' + "Run 'git submodule update --init " + "third-party/expolit'") conf_env = conf.Finish() env.AppendUnique(**expolit_vars) diff --git a/site_scons/site_init.py b/site_scons/site_init.py index 7dfc465..7f82851 100644 --- a/site_scons/site_init.py +++ b/site_scons/site_init.py @@ -1,74 +1,76 @@ +# -*- coding: utf-8 -*- + # @file # @section LICENSE # # Copyright (©) 2016-19 EPFL (École Polytechnique Fédérale de Lausanne), # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import SCons from SCons.Script import Dir, Configure from subprocess import check_output # ------------------------------------------------------------------------------ def pybind11(env): """A tool to configure pybind11""" if env.GetOption('clean'): return # Run code below to get versions from user preference and not scons versions_script = """ from __future__ import print_function import distutils.sysconfig as dsys from numpy.distutils.misc_util import get_numpy_include_dirs as numpy_dirs print(dsys.get_python_inc()) for d in numpy_dirs(): print(d)""" includes = check_output([env['py_exec'], "-c", versions_script], universal_newlines=True).split('\n') includes += [Dir(env['PYBIND11_ROOT'])] # Extension of shared library for python extension = check_output([env['py_exec'] + '-config', "--extension-suffix"], universal_newlines=True).split('\n')[0] env['SHLIBSUFFIX'] = extension env.AppendUnique(CPPPATH=includes) conf = Configure(env) if not conf.CheckCXXHeader('pybind11/pybind11.h'): raise SCons.Errors.StopError( 'Failed to find pybind11 header\n' + "Run 'git submodule update --init --recursive " + "third-party/pybind11'") env = conf.Finish() # ------------------------------------------------------------------------------ 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 diff --git a/site_scons/site_tools/nvcc.py b/site_scons/site_tools/nvcc.py index 06dce5d..a385268 100644 --- a/site_scons/site_tools/nvcc.py +++ b/site_scons/site_tools/nvcc.py @@ -1,203 +1,205 @@ +# -*- coding: utf-8 -*- + # @file # @section LICENSE # # Copyright (©) 2016-19 EPFL (École Polytechnique Fédérale de Lausanne), # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . """ SCons tool to setup compilation environment for NVidia's NVCC Based on version https://github.com/thrust/thrust/blob/master/site_scons/site_tools/nvcc.py """ from __future__ import print_function import SCons from SCons.Errors import StopError import platform from os.path import join known_components = { 'cufft': 'cufft.h', 'cufftw': 'cufftw.h' } def _find_cuda_paths(env): """Finding cuda paths""" if 'CUDA_TOOLKIT_PATH' not in env: raise StopError("CUDA_TOOLKIT_PATH variable not found in environment") cuda_path = env['CUDA_TOOLKIT_PATH'] inc_path = join(cuda_path, 'include') lib_path = join(cuda_path, 'lib') bin_path = join(cuda_path, 'bin') if platform.machine()[-2:] == '64': lib_path += '64' # add nvcc's location to PATH env.PrependENVPath('PATH', bin_path) # add the location of the cudart shared library to LD_LIBRARY_PATH as well # this allows us to execute CUDA programs during the build env.PrependENVPath('LD_LIBRARY_PATH', lib_path) # add to include paths env.AppendUnique(CPPPATH=[inc_path]) # add to library paths env.AppendUnique(LIBPATH=[lib_path]) def _configure_cuda_components(env): """Finding required components in cuda""" if env.GetOption('clean'): return if 'CUDA_COMPONENTS' in env: for component in env['CUDA_COMPONENTS']: if component not in known_components: raise StopError("Unknown cuda component '{}'".format(component)) conf = SCons.Script.Configure(env) if not conf.CheckLibWithHeader(component, known_components[component], 'c++'): raise StopError("Failed to find library {} or header {}".format( component, known_components[component])) env = conf.Finish() def _define_compile_cuda(env): """Define the compile string for files that need nvcc""" env['NVCC'] = env.Detect('nvcc') # Setting compilation command env['NVCCCOM'] = \ '$NVCC $CUDA_ARCH_FLAG -o $TARGET -c $NVCC_CXXFLAGS $NVCC_CCFLAGS $_CCCOMCOM -x cu $SOURCES' env['SHNVCCCOM'] = \ '$NVCC $CUDA_ARCH_FLAG -o $TARGET -dc -Xcompiler -fPIC $NVCC_CXXFLAGS $NVCC_CCFLAGS $_CCCOMCOM -x cu $SOURCES' # Constructing proper cc flags env['_NVCC_BARE_CCFLAGS'] = \ '${_concat("", CCFLAGS, "", __env__, _NVCC_BARE_FILTER)}' env['_NVCC_PASSED_CCFLAGS'] = \ '${_concat("-Xcompiler ", CCFLAGS, "", __env__, _NVCC_COMPILER_PASSED_FILTER)}' # Constructing proper cxx flags env['_NVCC_BARE_CXXFLAGS'] = \ '${_concat("", CXXFLAGS, "", __env__, _NVCC_BARE_FILTER)}' env['_NVCC_PASSED_CXXFLAGS'] = \ '${_concat("-Xcompiler ", CXXFLAGS, "", __env__, _NVCC_COMPILER_PASSED_FILTER)}' # Putting all together env['NVCC_CCFLAGS'] = '$_NVCC_BARE_CCFLAGS $_NVCC_PASSED_CCFLAGS' env['NVCC_CXXFLAGS'] = '$_NVCC_BARE_CXXFLAGS $_NVCC_PASSED_CXXFLAGS' def _define_link_cuda(env): """Define the link string""" # Fixing rpaths env['RPATHPREFIX'] = '-rpath=' env['__RPATH'] = '${_concat("-Xlinker ", _RPATH, "", __env__)}' env['LINK'] = env['NVCC'] env['SHLINK'] = env['NVCC'] # Replacing old link command strings env['LINKCOM'] = \ '$LINK $CUDA_ARCH_FLAG -o $TARGET $NVCC_LINKFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' env['SHLINKCOM'] = \ '$SHLINK -shared $CUDA_ARCH_FLAG -o $TARGET $NVCC_SHLINKFLAGS $__SHLIBVERSIONFLAGS $__RPATH $SOURCES $_LIBDIRFLAGS $_LIBFLAGS' # Constructing proper static linker flags env['_NVCC_BARE_LINKFLAGS'] = \ '${_concat("", LINKFLAGS, "", __env__, _NVCC_BARE_FILTER)}' env['_NVCC_COMPILER_LINKFLAGS'] = \ '${_concat("-Xcompiler ", LINKFLAGS, "", __env__, _NVCC_COMPILER_FILTER)}' env['_NVCC_PASSED_LINKFLAGS'] = \ '${_concat("-Xlinker ", LINKFLAGS, "", __env__, _NVCC_LINK_PASSED_FILTER)}' # Constructing proper shared linker flags env['_NVCC_BARE_SHLINKFLAGS'] = \ '${_concat("", SHLINKFLAGS, "", __env__, _NVCC_BARE_FILTER)}' env['_NVCC_COMPILER_SHLINKFLAGS'] = \ '${_concat("-Xcompiler ", LINKFLAGS, "", __env__, _NVCC_COMPILER_FILTER)}' env['_NVCC_PASSED_SHLINKFLAGS'] = \ '${_concat("-Xlinker ", SHLINKFLAGS, "", __env__, _NVCC_LINK_PASSED_FILTER)}' # Putting all together env['NVCC_LINKFLAGS'] = \ '$_NVCC_BARE_LINKFLAGS $_NVCC_COMPILER_LINKFLAGS $_NVCC_PASSED_LINKFLAGS' env['NVCC_SHLINKFLAGS'] = env['NVCC_LINKFLAGS'] env['NVCC_SHLINKFLAGS'] += \ ' $_NVCC_BARE_SHLINKFLAGS $_NVCC_COMPILER_SHLINKFLAGS $_NVCC_PASSED_SHLINKFLAGS' def _define_commands(env): """Defining the command strings""" # Flags allowed by nvcc bare_flags = """-std=c++11 -O0 -O1 -O2 -O3 -g -pg -G -w""".split() # Experimental flags bare_flags += """-expt-extended-lambda -expt-relaxed-constexpr""".split() # Flags that must be passed to compiler at link time compiler_flags = """-fopenmp -qopenmp""".split() # Pass flags bare to nvcc env['_NVCC_BARE_FILTER'] = lambda flags: list(filter(lambda flag: flag in bare_flags, flags)) # Must prepend -Xcompiler, even at link time env['_NVCC_COMPILER_FILTER'] = lambda flags: list(filter( lambda flag: flag in compiler_flags, flags)) # Prepend -Xcompiler env['_NVCC_COMPILER_PASSED_FILTER'] = lambda flags: list(filter( lambda flag: flag not in set(bare_flags), flags)) # Prepend -Xlinker env['_NVCC_LINKED_PASSED_FILTER'] = lambda flags: list(filter( lambda flag: flag not in set(bare_flags) | set(compiler_flags), flags)) _define_compile_cuda(env) _define_link_cuda(env) def _add_actions_cuda(env): """Adding actions to .cu files to compile with nvcc. Other files are not affected.""" nvcc_action = SCons.Action.Action('$NVCCCOM', '$NVCCCOMSTR') shnvcc_action = SCons.Action.Action('$SHNVCCCOM', '$NVCCCOMSTR') static, shared = SCons.Tool.createObjBuilders(env) # Compiling with nvcc action added to detected .cu files static.add_action('.cu', nvcc_action) shared.add_action('.cu', shnvcc_action) # Emitter to qualify correctly object code type static.add_emitter('.cu', SCons.Defaults.StaticObjectEmitter) shared.add_emitter('.cu', SCons.Defaults.SharedObjectEmitter) env['CXXCOM'] = '$NVCCCOM' env['SHCXXCOM'] = '$SHNVCCCOM' # Scanner for dependency calculations SCons.Tool.SourceFileScanner.add_scanner('.cu', SCons.Scanner.C.CScanner()) def generate(env): """Setup environment for compiling .cu files with nvcc""" _find_cuda_paths(env) _add_actions_cuda(env) _define_commands(env) _configure_cuda_components(env) def exists(env): return env.Detect('nvcc') diff --git a/site_scons/site_tools/tamaas.py b/site_scons/site_tools/tamaas.py index 3cf3930..6c6065e 100644 --- a/site_scons/site_tools/tamaas.py +++ b/site_scons/site_tools/tamaas.py @@ -1,59 +1,61 @@ +# -*- coding: utf-8 -*- + # @file # @section LICENSE # # Copyright (©) 2016-19 EPFL (École Polytechnique Fédérale de Lausanne), # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import os from SCons.Errors import StopError from SCons.Script import Configure def generate(env): """Sets correct environement variables for Tamaas. - 'TAMAAS_PATH' is the path to the build directory of Tamaas""" if env.GetOption("clean"): return if 'TAMAAS_PATH' not in env: raise StopError("Please set the 'TAMAAS_PATH' variable in your " + "scons environment") tm_path = env['TAMAAS_PATH'] include_dir = tm_path + '/src' subdirs = "core model solvers bem surface percolation".split(" ") include_dirs = [os.path.abspath(os.path.join(include_dir, x)) for x in subdirs] lib_dir = include_dir env.AppendUnique(CPPPATH=include_dirs) env.AppendUnique(LIBPATH=[os.path.abspath(lib_dir)]) env.AppendUnique(RPATH=[os.path.abspath(lib_dir)]) env.AppendUnique(CXXFLAGS=["-std=c++14"]) env.AppendUnique(CPPDEFINES=["THRUST_DEVICE_SYSTEM=THRUST_DEVICE_SYSTEM_OMP"]) env.AppendUnique(LIBS=['fftw3', 'fftw3_omp']) conf = Configure(env) if not conf.CheckLibWithHeader("Tamaas", "tamaas.hh", language="CXX"): raise StopError( "Tamaas not found in {}".format(os.path.abspath(tm_path))) env['TAMAAS_PATH'] = os.path.abspath(tm_path) env = conf.Finish() def exists(env): if 'TAMAAS_PATH' in env: return True return False diff --git a/site_scons/version.py b/site_scons/version.py index d96e110..a5704db 100644 --- a/site_scons/version.py +++ b/site_scons/version.py @@ -1,59 +1,61 @@ +# -*- coding: utf-8 -*- + # @file # @section LICENSE # # Copyright (©) 2016-19 EPFL (École Polytechnique Fédérale de Lausanne), # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import subprocess import base64 from functools import reduce def git(*args): return subprocess.check_output(["git"] + list(args)) def write_info_file(file_name, build_type="release"): header = '#include "tamaas_info.hh"\n\n' file_content = '' branch = git("rev-parse", "--abbrev-ref", "HEAD")[:-1] commit = git("rev-parse", branch)[:-1] file_content += \ 'std::string tamaas::TamaasInfo::build_type = "{}";\n'.format(build_type) file_content += \ 'std::string tamaas::TamaasInfo::branch = "{}";\n'.format(branch) file_content += \ 'std::string tamaas::TamaasInfo::commit = "{}";\n'.format(commit) diff = git("diff") diff += "|".encode('ascii') + git("diff", "--cached") file_content += \ 'std::string tamaas::TamaasInfo::diff = "{}";\n'.format( base64.b64encode(diff)) remotes = git('remote', '-v') remotes_strings = filter(lambda x: x != '', remotes.splitlines()) remotes_string = reduce(lambda x, y: x + '"{}\\n"\n'.format(y), remotes_strings, "") file_content += \ 'std::string tamaas::TamaasInfo::remotes = {};\n'.format(remotes_string) with open(file_name, 'w') as file: file.write(header + file_content) diff --git a/src/SConscript b/src/SConscript index 0f0e96e..29be809 100644 --- a/src/SConscript +++ b/src/SConscript @@ -1,154 +1,156 @@ +# -*- coding: utf-8 -*- + # @file # @section LICENSE # # Copyright (©) 2016-19 EPFL (École Polytechnique Fédérale de Lausanne), # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import os Import('main_env') def prepend(path, list): return [os.path.join(path, x) for x in list] env = main_env.Clone() # Core core_list = """ fft_plan_manager.cpp fftransform.cpp fftransform_fftw.cpp grid.cpp grid_hermitian.cpp statistics.cpp surface.cpp tamaas.cpp legacy_types.cpp loop.cpp """.split() core_list = prepend('core', core_list) core_list.append('tamaas_info.cpp') # Lib roughcontact generator_list = """ surface_generator.cpp surface_generator_filter.cpp surface_generator_filter_fft.cpp surface_generator_random_phase.cpp isopowerlaw.cpp regularized_powerlaw.cpp """.split() generator_list = prepend('surface', generator_list) # Lib PERCOLATION percolation_list = """ flood_fill.cpp """.split() percolation_list = prepend('percolation', percolation_list) # BEM PERCOLATION bem_list = """ bem_kato.cpp bem_polonski.cpp bem_gigi.cpp bem_gigipol.cpp bem_penalty.cpp bem_uzawa.cpp bem_fft_base.cpp bem_functional.cpp bem_meta_functional.cpp elastic_energy_functional.cpp exponential_adhesion_functional.cpp squared_exponential_adhesion_functional.cpp maugis_adhesion_functional.cpp complimentary_term_functional.cpp bem_grid.cpp bem_grid_polonski.cpp bem_grid_kato.cpp bem_grid_teboulle.cpp bem_grid_condat.cpp """.split() bem_list = prepend('bem', bem_list) # Model model_list = """ model.cpp model_factory.cpp model_type.cpp model_template.cpp be_engine.cpp westergaard.cpp elastic_functional.cpp meta_functional.cpp adhesion_functional.cpp volume_potential.cpp kelvin.cpp mindlin.cpp boussinesq.cpp elasto_plastic/isotropic_hardening.cpp elasto_plastic/elasto_plastic_model.cpp elasto_plastic/residual.cpp integration/element.cpp """.split() model_list = prepend('model', model_list) # Solvers solvers_list = """ contact_solver.cpp polonsky_keer_rey.cpp kato.cpp beck_teboulle.cpp condat.cpp polonsky_keer_tan.cpp ep_solver.cpp epic.cpp """.split() solvers_list = prepend('solvers', solvers_list) # GPU API gpu_list = """ fftransform_cufft.cpp """.split() gpu_list = prepend('gpu', gpu_list) # Assembling total list rough_contact_list = \ core_list + generator_list + percolation_list + model_list + solvers_list # For some reason collapse OMP loops don't compile on intel if env['CXX'] != 'icpc': rough_contact_list += bem_list # Adding GPU if needed if env['backend'] == 'cuda': rough_contact_list += gpu_list # Generating dependency files # env.AppendUnique(CXXFLAGS=['-MMD']) # for file_name in rough_contact_list: # obj = file_name.replace('.cpp', '.os') # dep_file_name = file_name.replace('.cpp', '.d') # env.SideEffect(dep_file_name, obj) # env.ParseDepends(dep_file_name) # Adding extra warnings for Tamaas base lib env.AppendUnique(CXXFLAGS=['-Wextra']) libTamaas = env.SharedLibrary('Tamaas', rough_contact_list) Export('libTamaas') diff --git a/tests/SConscript b/tests/SConscript index f306aae..70fcc0f 100644 --- a/tests/SConscript +++ b/tests/SConscript @@ -1,159 +1,161 @@ +# -*- coding: utf-8 -*- + # @file # @section LICENSE # # Copyright (©) 2016-19 EPFL (École Polytechnique Fédérale de Lausanne), # Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from __future__ import print_function from os.path import join, abspath from SCons.Script import Split, Copy, Dir, Import from detect import FindGTest # ------------------------------------------------------------------------------ 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.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 test_integral_operators.py test_dumper.py test_gtest.py test_tangential.py test_boussinesq_surface.py test_voigt.py fftfreq.py conftest.py """) src_dir = "#/tests" for file in test_files: source = join(src_dir, file) test_env.Command(file, source, Copy("$TARGET", "$SOURCE")) test_env = env.Clone() # Helper module for integral operators test_env['SHLIBPREFIX'] = '' register = test_env.SharedLibrary(target="register_integral_operators", source=["register_integral_operators.cpp"], LIBS=['Tamaas'], RPATH=[abspath('../src')]) Import('libTamaas') test_env.Depends(register, libTamaas) # ------------------------------------------------------------------------------ def compile_google_test(env, gtest_path): gtest_obj = env.Object('gtest.o', [join(gtest_path, "src/gtest-all.cc")]) return env.StaticLibrary('gtest', gtest_obj) # ------------------------------------------------------------------------------ def make_google_tests(env): gtest_dir = Dir(env['GTEST_ROOT']) gtest_env = env.Clone(CPPPATH=[gtest_dir], CXXFLAGS=['-pthread', '-isystem', join(str(gtest_dir), "include")]) colors = env['COLOR_DICT'] if not env['verbose']: gtest_env['ARCOMSTR'] = u'{}[Ar]{} $TARGET'.format(colors['purple'], colors['end']) gtest_env['RANLIBCOMSTR'] = \ u'{}[Randlib]{} $TARGET'.format(colors['purple'], colors['end']) FindGTest(gtest_env) libgtest = None # Hugly hack to detect if we need to compile gtest submodule if env['GTEST_ROOT'] == '#third-party/googletest/googletest': gtest_path = str(gtest_dir) libgtest = compile_google_test(gtest_env, gtest_path) env.AppendUnique(LIBS=['Tamaas'], CXXFLAGS=gtest_env['CXXFLAGS']) google_test_files = Split(""" test_fft.cpp test_grid.cpp test_loop.cpp test_model.cpp test_static_types.cpp test_integration.cpp """) gtest_main = env.Object("tamaas_gtest_main.o", 'tamaas_gtest_main.cc') gtest_all = env.Program('test_gtest_all', google_test_files + [gtest_main], LIBS=(env['LIBS'] + ['gtest'])) Import('libTamaas') env.Depends(gtest_all, libTamaas) env.Depends(gtest_all, libgtest) # ------------------------------------------------------------------------------ def make_bare_tests(env): rough = env.Program("test_rough_surface.cpp") Import('libTamaas') env.Depends(rough, libTamaas) # ------------------------------------------------------------------------------ Import('main_env') # Setup of test environment test_env = main_env.Clone() test_env.AppendUnique(LIBS=['Tamaas'], LIBPATH=['.']) copyComStr(test_env, main_env) # Building tests that do not require any third party make_bare_tests(test_env) if test_env['build_python']: test_env.Tool(pybind11) test_env.ParseConfig("{}-config --ldflags".format(test_env['py_exec'])) test_env['CCFLAGS'] = [] make_python_tests(test_env) # Building google tests if test_env['use_googletest']: make_google_tests(test_env)