diff --git a/.clang-tidy b/.clang-tidy new file mode 100644 index 0000000..76c907d --- /dev/null +++ b/.clang-tidy @@ -0,0 +1,4 @@ +Checks: 'modernize-use-*, performance-*, cppcoreguidelines-*' +AnalyzeTemporaryDtors: false +HeaderFilterRegex: 'src/.*' +FormatStyle: file diff --git a/SConstruct b/SConstruct index 434e025..6769e7b 100644 --- a/SConstruct +++ b/SConstruct @@ -1,301 +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['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['blue'], target[0], colors['end'])) 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'] # 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') vars.Add(BoolVariable('verbose', 'Activate verbosity', False)) vars.Add(BoolVariable('build_doc', 'Build documentation', False)) vars.Add(BoolVariable('color', 'Color the non-verbose compilation output', 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['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"], "clang++": [], "icpc": [] } omp_flags = { "g++": ["-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', +main_env.AppendUnique(CXXFLAGS=['-std=c++14', '-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) + 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 documentation if main_env['build_doc']: subdir('doc') diff --git a/python/SConscript b/python/SConscript index 7bd10db..fcafd9a 100644 --- a/python/SConscript +++ b/python/SConscript @@ -1,63 +1,67 @@ from __future__ import print_function from os.path import abspath, basename from distutils.version import StrictVersion from subprocess import check_output, STDOUT +import re Import('main_env') env_swig = main_env.Clone( tools=['swig'], SHLIBPREFIX='', ) if env_swig['SWIGVERSION'] is None: print("Could not find swig version") - print(env_swig.Dictionary()) - print(env_swig.Environment) Exit(1) if StrictVersion(env_swig['SWIGVERSION']) < StrictVersion("3.0"): print("Swig version {} is not supported by tamaas".format(env_swig['SWIGVERSION'])) Exit(1) # Check user's prefered version -version_out = check_output([main_env['py_exec'], "--version"], stderr=STDOUT).split(' ') -version = StrictVersion(version_out[-1]) +version_out = check_output([main_env['py_exec'], "--version"], stderr=STDOUT, universal_newlines=True).split(' ') + +# Matching version +version_re = re.compile(r"^\d(\.\d+)*") +version_string = version_re.match(version_out[-1]).group() +# Verifying version +version = StrictVersion(version_string) python3 = version >= StrictVersion("3.0") print("Building wrapper for python version {}".format(version)) # 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([main_env['py_exec'], "-c", versions_script]).split('\n') +includes = check_output([main_env['py_exec'], "-c", versions_script], universal_newlines=True).split('\n') env_swig.AppendUnique(CPPPATH=includes) env_swig.AppendUnique(SWIGPATH=['$CPPPATH']) env_swig.AppendUnique(SWIGFLAGS=['-python', '-c++']) if python3: env_swig.AppendUnique(SWIGFLAGS=['-py3']) verbose = main_env['verbose'] if not verbose: env_swig.AppendUnique(SWIGFLAGS=['-w325', '-w315']) if basename(env_swig['CXX']) != "clang++": # for some warning in wrapper code env_swig.AppendUnique(CXXFLAGS=['-Wno-maybe-uninitialized']) else: env_swig.AppendUnique(CXXFLAGS=['-Wno-uninitialized', '-Wno-dynamic-class-memaccess']) env_swig.SharedLibrary( target='_tamaas.so', source=['tamaas.i'], LIBS=['Tamaas'], RPATH=[abspath('../src')] ) diff --git a/site_scons/site_tools/swig.py b/site_scons/site_tools/swig.py index 887f94f..826052e 100644 --- a/site_scons/site_tools/swig.py +++ b/site_scons/site_tools/swig.py @@ -1,202 +1,202 @@ """SCons.Tool.swig Tool-specific initialization for swig. There normally shouldn't be any need to import this module directly. It will usually be imported through the generic SCons.Tool.Tool() selection method. """ # # Copyright (c) 2001 - 2015 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # __revision__ = "src/engine/SCons/Tool/swig.py rel_2.4.1:3453:73fefd3ea0b0 2015/11/09 03:25:05 bdbaddog" import os.path import re import subprocess import SCons.Action import SCons.Defaults import SCons.Scanner import SCons.Tool import SCons.Util import SCons.Node swigs = ['swig', 'swig3.0', 'swig2.0'] SwigAction = SCons.Action.Action('$SWIGCOM', '$SWIGCOMSTR') def swigSuffixEmitter(env, source): if '-c++' in SCons.Util.CLVar(env.subst("$SWIGFLAGS", source=source)): return '$SWIGCXXFILESUFFIX' else: return '$SWIGCFILESUFFIX' # Match '%module test', as well as '%module(directors="1") test' # Also allow for test to be quoted (SWIG permits double quotes, but not single) # Also allow for the line to have spaces after test if not quoted _reModule = re.compile(r'%module(\s*\(.*\))?\s+("?)(\S+)\2') def _find_modules(src): """Find all modules referenced by %module lines in `src`, a SWIG .i file. Returns a list of all modules, and a flag set if SWIG directors have been requested (SWIG will generate an additional header file in this case.)""" directors = 0 mnames = [] try: matches = _reModule.findall(open(src).read()) except IOError: # If the file's not yet generated, guess the module name from the file stem matches = [] mnames.append(os.path.splitext(os.path.basename(src))[0]) for m in matches: mnames.append(m[2]) directors = directors or m[0].find('directors') >= 0 return mnames, directors def _add_director_header_targets(target, env): # Directors only work with C++ code, not C suffix = env.subst(env['SWIGCXXFILESUFFIX']) # For each file ending in SWIGCXXFILESUFFIX, add a new target director # header by replacing the ending with SWIGDIRECTORSUFFIX. for x in target[:]: n = x.name d = x.dir if n[-len(suffix):] == suffix: target.append(d.File(n[:-len(suffix)] + env['SWIGDIRECTORSUFFIX'])) def _swigEmitter(target, source, env): swigflags = env.subst("$SWIGFLAGS", target=target, source=source) flags = SCons.Util.CLVar(swigflags) for src in source: src = str(src.rfile()) mnames = None if "-python" in flags and "-noproxy" not in flags: if mnames is None: mnames, directors = _find_modules(src) if directors: _add_director_header_targets(target, env) python_files = [m + ".py" for m in mnames] outdir = env.subst('$SWIGOUTDIR', target=target, source=source) # .py files should be generated in SWIGOUTDIR if specified, # otherwise in the same directory as the target if outdir: python_files = [env.fs.File(os.path.join(outdir, j)) for j in python_files] else: python_files = [target[0].dir.File(m) for m in python_files] target.extend(python_files) if "-java" in flags: if mnames is None: mnames, directors = _find_modules(src) if directors: _add_director_header_targets(target, env) java_files = [[m + ".java", m + "JNI.java"] for m in mnames] java_files = SCons.Util.flatten(java_files) outdir = env.subst('$SWIGOUTDIR', target=target, source=source) if outdir: java_files = [os.path.join(outdir, j) for j in java_files] java_files = list(map(env.fs.File, java_files)) def t_from_s(t, p, s, x): return t.dir tsm = SCons.Node._target_from_source_map tkey = len(tsm) tsm[tkey] = t_from_s for jf in java_files: jf._func_target_from_source = tkey target.extend(java_files) return (target, source) def _get_swig_version(env, swig): """Run the SWIG command line tool to get and return the version number""" swig = env.subst(swig) pipe = SCons.Action._subproc(env, SCons.Util.CLVar(swig) + ['-version'], stdin='devnull', stderr='devnull', stdout=subprocess.PIPE) if pipe.wait() != 0: return - out = pipe.stdout.read() - match = re.search(r'SWIG Version\s+(\S+)$', out, re.MULTILINE) + out = str(pipe.stdout.read()) + match = re.search(r'SWIG Version\s+(\d+\.\d+\.?\d*)', out, re.MULTILINE) if match: return match.group(1) def generate(env): """Add Builders and construction variables for swig to an Environment.""" c_file, cxx_file = SCons.Tool.createCFileBuilders(env) c_file.suffix['.i'] = swigSuffixEmitter cxx_file.suffix['.i'] = swigSuffixEmitter c_file.add_action('.i', SwigAction) c_file.add_emitter('.i', _swigEmitter) cxx_file.add_action('.i', SwigAction) cxx_file.add_emitter('.i', _swigEmitter) java_file = SCons.Tool.CreateJavaFileBuilder(env) java_file.suffix['.i'] = swigSuffixEmitter java_file.add_action('.i', SwigAction) java_file.add_emitter('.i', _swigEmitter) if 'SWIG' not in env: env['SWIG'] = env.Detect(swigs) or swigs[0] env['SWIGVERSION'] = _get_swig_version(env, env['SWIG']) env['SWIGFLAGS'] = SCons.Util.CLVar('') env['SWIGDIRECTORSUFFIX'] = '_wrap.h' env['SWIGCFILESUFFIX'] = '_wrap$CFILESUFFIX' env['SWIGCXXFILESUFFIX'] = '_wrap$CXXFILESUFFIX' env['_SWIGOUTDIR'] = r'${"-outdir \"%s\"" % SWIGOUTDIR}' env['SWIGPATH'] = [] env['SWIGINCPREFIX'] = '-I' env['SWIGINCSUFFIX'] = '' env['_SWIGINCFLAGS'] = '$( ${_concat(SWIGINCPREFIX, SWIGPATH, SWIGINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' env['SWIGCOM'] = '$SWIG -o $TARGET ${_SWIGOUTDIR} ${_SWIGINCFLAGS} $SWIGFLAGS $SOURCES' expr = '^[ \t]*%[ \t]*(?:include|import|extern)[ \t]*(<|"?)([^>\s"]+)(?:>|"?)' scanner = SCons.Scanner.ClassicCPP("SWIGScan", ".i", "SWIGPATH", expr) env.Append(SCANNERS=scanner) def exists(env): swig = env.get('SWIG') or env.Detect(swigs) return swig # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/version.py b/version.py index 5fb5453..e67f642 100644 --- a/version.py +++ b/version.py @@ -1,33 +1,33 @@ import subprocess import base64 def write_info_file(file_name): header = '''#include \nstd::string tamaas_release_info = "''' file_content = '' branch = subprocess.check_output(["git", "rev-parse", "--abbrev-ref", "HEAD"])[:-1] commit = subprocess.check_output(["git", "rev-parse", branch])[:-1] - file_content += branch + ' ' + commit + '\n\n' + file_content += '{} {}\n\n'.format(branch, commit) diff = subprocess.check_output(["git", "diff"]) diff_cached = subprocess.check_output(["git", "diff", "--cached"]) - file_content += "diff\n" + base64.b64encode(diff) + '\n\n' - file_content += "diff --cached\n" + base64.b64encode(diff_cached) + '\n' + file_content += "diff\n{}\n\n".format(base64.b64encode(diff)) + file_content += "diff --cached\n{}\n\n".format(base64.b64encode(diff_cached)) remotes = subprocess.check_output(['git', 'remote', '-v']) - file_content += "remotes\n" + remotes + '\n' + file_content += "remotes\n{}\n".format(remotes) try: - origin_branch_commit = subprocess.check_output(["git", "rev-parse", "origin/" + branch, '--']) - file_content += "origin/" + branch + " commit: " + origin_branch_commit + origin_branch_commit = subprocess.check_output(["git", "rev-parse", "origin/" + branch, '--'], universal_newlines=True) + file_content += "origin/{} commit: {}".format(branch, origin_branch_commit) except: - file_content += "origin/" + branch + " does not exist" + file_content += "origin/ {} does not exist".format(branch) file_content += '"' file_content = file_content.replace('\n', '\\\n') with open(file_name, 'w') as file: file.write(header + file_content + ';\n')