Attention une mise à jour du service Gitlab va être effectuée le mardi 18 janvier (et non lundi 17 comme annoncé précédemment) entre 18h00 et 18h30. Cette mise à jour va générer une interruption du service dont nous ne maîtrisons pas complètement la durée mais qui ne devrait pas excéder quelques minutes.

Commit 1d6634d6 authored by GILLES Sebastien's avatar GILLES Sebastien
Browse files

#1247 Remove SCons from the project.

parent 638db3ca
This diff is collapsed.
......@@ -132,27 +132,8 @@ git clone https://gitlab.inria.fr/MoReFEM/Models/AcousticWave
One you're there, you may run one of the embedded model left there for play here; to change the input file used you just have to edit a scheme in Product > Scheme.
### SCons
SCons is the command line build tool used up to now to compile on command line the library. However, its maintainance proved to be a bit tricky and the compilation is rather slow, so a switch to CMake is in progress.
To build SCons file:
- Copy one of the build_configuration.sample.***.py file in the Sources directory.
- Edit your local copy to specify the correct settings you want; for a first approach you just have to specify where the programs ans libraries will be installed (BUILD\_DIR) and another used for intermediate steps (INTERMEDIATE\_BUILD\_DIR).
- The run:
````
scons -j *nproc* --config_file=*your configuration file*
````
This command will display all possible targets. If you want to build everything, choose the target 'all':
````
scons -j *nproc* --config_file=*your configuration file* all
````
### CMake
CMake build is intended to replace SCons this year as the command line build tools; the goal is to be a bit more easy to maintain than SCons (and compilation is markedly faster, especially using ninja back-end).
To compile the code:
......
def WarningFlags():
'''
List of warning flags to consider when compiling with clang.
The idea is to use every warning available for clang except a handful ones that are too cumbersome.
Those are:
. -Wno-c++98-compat, -Wno-c++98-compat-pedantic: HMoReFEMHeart is resolutely C++ 14 in style and doesn't bother with C++ 98 compatibility.
. -Wno-padded: This warning tells each time it has to pad a class to make it a multiple of pointer size. The issue is that it tells this even if you can't do better (for instance a class with only one boolean attribute); it would be useful if it guides the user toward more efficiency (for instance suggesting to replace bool, double, bool by bool, bool, double which might take less space in storage).
. -Wno-exit-time-destructors, -Wno-exit-time-destructors: these would triggger warnings due to some design pattern I used (object factories).
. -Wno-documentation-unknown-command: BEcause clang is not up-to-date with current Doxygen syntax.
'''
return ('-Weverything',
'-Wno-c++98-compat',
'-Wno-c++98-compat-pedantic',
'-Wno-padded',
'-Wno-exit-time-destructors',
'-Wno-global-constructors',
'-Wno-documentation',
'-Wno-documentation-unknown-command',
'-Wno-undefined-func-template',
'-Wno-c++1z-extensions')
def DebugMacroFlags():
'''Macro flags that are present only in debug mode.
Only macros specific to compiler are given here; NDEBUG for instance is not and is handled elsewhere.
For instance here we use '_LIBCPP_DEBUG2=0' which is a libc++ (STL used by clang) macro to tells when there
are overflows in array indexes.
'''
return ('_LIBCPP_DEBUG2=0', )
def MiscellaneousFlags(env):
'''Any flag not yet covered in above categories.
\param[in] env Scons environment.
'''
ret = []
if env['MODE'] == 'release':
ret.append('-flto')
return ret
\ No newline at end of file
def WarningFlags():
'''
List of warning flags to consider when compiling with gcc.
This list is adapted from http://stackoverflow.com/questions/5088460/flags-to-enable-thorough-and-verbose-g-warnings.
'''
return ('-Wall',
'-Wextra',
#'-Wpedantic', Can't be activated because of third-party error; pragma doesn't work to disable it only for third party.
'-Wcast-align',
'-Wcast-qual',
'-Wconversion',
'-Wdisabled-optimization',
'-Wfloat-equal',
'-Wformat=2',
'-Wformat-nonliteral',
'-Wformat-security',
'-Wformat-y2k',
'-Wimport',
'-Winit-self',
# '-Winline', Tag destructor defined with = default as inline...
'-Winvalid-pch',
'-Wmissing-field-initializers',
'-Wmissing-format-attribute',
'-Wmissing-include-dirs',
'-Wmissing-noreturn',
'-Wpacked',
'-Wpointer-arith',
'-Wredundant-decls',
#'-Wshadow', g++ shadow is too cumbersome: can't name function argument same as a method for instance...
'-Wstack-protector',
'-Wstrict-aliasing=2',
'-Wswitch-enum',
'-Wunreachable-code',
'-Wunused',
'-Wunused-parameter',
'-Wvariadic-macros',
'-Wwrite-strings'
)
def DebugMacroFlags():
'''Macro flags that are present only in debug mode.
Only macros specific to compiler are given here; NDEBUG for instance is not and is handled elsewhere.
For instance here we use '_LIBCPP_DEBUG2=0' which is a libc++ (STL used by clang) macro to tells when there
are overflows in array indexes.
'''
return ('_LIBCPP_DEBUG2=0', )
def MiscellaneousFlags(env):
'''Any flag not yet covered in above categories.
\param[in] env Scons environment.
'''
return ('-fdiagnostics-color', '-fPIC', )
# This file refers to the clang built manually on top of LLVM, reather than the default one for instance found in Apple XCode.
def WarningFlags():
'''
List of warning flags to consider when compiling with clang.
The idea is to use every warning available for clang except a handful ones that are too cumbersome.
Those are:
. -Wno-c++98-compat, -Wno-c++98-compat-pedantic: MoReFEM is resolutely C++ 14 in style and doesn't bother with C++ 98 compatibility.
. -Wno-padded: This warning tells each time it has to pad a class to make it a multiple of pointer size. The issue is that it tells this even if you can't do better (for instance a class with only one boolean attribute); it would be useful if it guides the user toward more efficiency (for instance suggesting to replace bool, double, bool by bool, bool, double which might take less space in storage).
. -Wno-exit-time-destructors, -Wno-exit-time-destructors: these would triggger warnings due to some design pattern I used (object factories).
. -Wno-documentation-unknown-command: BEcause clang is not up-to-date with current Doxygen syntax.
'''
return ('-Weverything',
'-Wno-c++98-compat',
'-Wno-c++98-compat-pedantic',
'-Wno-padded',
'-Wno-exit-time-destructors',
'-Wno-global-constructors',
'-Wno-documentation',
'-Wno-documentation-unknown-command',
'-Wno-undefined-func-template')
def DebugMacroFlags():
'''Macro flags that are present only in debug mode.
Only macros specific to compiler are given here; NDEBUG for instance is not and is handled elsewhere.
For instance here we use '_LIBCPP_DEBUG2=0' which is a libc++ (STL used by clang) macro to tells when there
are overflows in array indexes.
'''
return ('_LIBCPP_DEBUG2=0', )
def MiscellaneousFlags(env):
'''Any flag not yet covered in above categories.
\param[in] env Scons environment.
'''
ret = ["-DMOREFEM_LLVM_CLANG"]
if env['MODE'] == 'release':
ret.append('-flto')
return ret
\ No newline at end of file
import os
import SCons
def PathOrNone(key, val, env):
"""For BLAS_LIB_DIR: None is also an acceptable value (in OS X as framework Accelerate is used)."""
if val:
return SCons.Variables.PathVariable.PathIsDir(key, val, env)
def ReadConfigurationFile(configuration_file):
"""Read the configuration file that details the settings specific to each user.
"""
assert os.path.isfile(configuration_file)
vars = SCons.Variables.Variables(configuration_file)
vars.Add(SCons.Variables.EnumVariable('MODE', 'Whether you compile in debug or release mode. A hybrid mode for callgrind is also there (it is a release with debug symbols).', 'debug',
allowed_values=('debug', 'release', 'callgrind')))
vars.Add(SCons.Variables.EnumVariable('LIBRARY_TYPE', 'Whether you want to use static or shared libraries.', None,
allowed_values=('static', 'shared', )))
vars.Add(SCons.Variables.BoolVariable('MOREFEM_CHECK_UPDATE_GHOSTS_CALL_RELEVANCE', \
"If true, add a (costly) method that gives an hint whether an UpdateGhost() call was relevant or not.", \
None))
vars.Add(SCons.Variables.BoolVariable('MOREFEM_EXTENDED_TIME_KEEP', \
"If true, TimeKeep gains the ability to track times between each call of PrintTimeElapsed(). If not, PrintTimeElapsed() is flatly ignored.", \
None))
vars.Add(SCons.Variables.BoolVariable('MOREFEM_CHECK_NAN_AND_INF', \
"If true, there are additional checks that no nan and inf appears in the code. Even if False, solver always check for the validity of its solution (if a nan or an inf is present the SolveLinear() or SolveNonLinear() operation throws with a dedicated Petsc error). Advised in debug mode and up to you in release mode.", \
None))
vars.Add(SCons.Variables.PathVariable("BUILD_DIR", "Path to the folder into which build will be installed.", None, SCons.Variables.PathVariable.PathIsDirCreate))
vars.Add(SCons.Variables.PathVariable("INTERMEDIATE_BUILD_DIR", "Path to the folder into which intermediate build will be installed.", None, SCons.Variables.PathVariable.PathIsDirCreate))
vars.Add('COMPILER', "Name of the compiler family used (clang or gcc currently)", None)
vars.Add('COMPILER_DIRECTORY', "Subdirectory(ies) to use within BUID_DIR and INTERMEDIATE_BUILD_DIR for the compiler. It is introduced to allow several directories for a same compiler: for instance if you want to build both gcc5 and gcc 6 you must provide 'gcc' in COMPILER but might name the directories 'gcc-5' and 'gcc-6'. If you do not need such refinement just use the same content as COMPILER field.", None)
vars.Add('CC', "C compiler", None)
vars.Add('CXX', "C++ compiler", None)
vars.Add('CCC_CC', "Option used only for clang-analyzer; points to C compiler.", "clang")
vars.Add('CCC_CXX', "Option used only for clang-analyzer; points to C++ compiler.", "clang++")
vars.Add('CCC_ANALYZER_VERBOSE', "Verbose mode for clang-analyzer", 1)
vars.Add(SCons.Variables.PathVariable('OPEN_MPI_INCL_DIR', "Includes of OpenMPI library.", None))
vars.Add(SCons.Variables.BoolVariable('BLAS_CUSTOM_LINKER', "True if Blas is not handled as a standard library (typically for macOS/framework Accelerate).", 0))
vars.Add(SCons.Variables.PathVariable('BLAS_LIB_DIR', "Directory in which Blas library may be found. Might be 'None'.", None, PathOrNone))
vars.Add('BLAS_LIB', "Call to the linker to the library (e.g. '-framework Accelerate', '-lopenblas', ...).", None)
vars.Add(SCons.Variables.PathVariable('PETSC_GENERAL_INCL_DIR', "Includes of Petsc library that are independant of the build used.", None))
vars.Add(SCons.Variables.PathVariable('PETSC_DEBUG_INCL_DIR', "Includes of Petsc library related to debug build.", None))
vars.Add(SCons.Variables.PathVariable('PETSC_RELEASE_INCL_DIR', "Includes of Petsc library related to release build.", None))
vars.Add(SCons.Variables.PathVariable('PARMETIS_INCL_DIR', "Includes of Parmetis library.", None))
vars.Add(SCons.Variables.PathVariable('LUA_INCL_DIR', "Includes of Lua library.", None))
vars.Add(SCons.Variables.PathVariable('BOOST_INCL_DIR', "Includes of Boost library.", None))
vars.Add(SCons.Variables.PathVariable('OPEN_MPI_LIB_DIR', "Path to OpenMPI library", None))
vars.Add(SCons.Variables.PathVariable('PETSC_DEBUG_LIB_DIR', "Path to Petsc library built in debug mode.", None))
vars.Add(SCons.Variables.PathVariable('PETSC_RELEASE_LIB_DIR', "Path to Petsc library built in release mode.", None))
vars.Add(SCons.Variables.PathVariable('PARMETIS_LIB_DIR', "Path to Parmetis library.", None))
vars.Add(SCons.Variables.PathVariable('LUA_LIB_DIR', "Path to Lua library.", None))
vars.Add(SCons.Variables.PathVariable('BOOST_LIB_DIR', "Path to Boost library.", None))
vars.Add(SCons.Variables.BoolVariable('PHILLIPS_LINKER', "True if you want to link with the Phillips Library. Only works with gcc.", 0))
vars.Add(SCons.Variables.PathVariable('PHILLIPS_DIR', "Path to Phillips library and header. Might be 'None' if irrelevant", None, PathOrNone))
return vars
import os
import sys
import subprocess
import SCons
import imp
def PrepareTargetName(source, suffix_to_remove = ""):
"""
\param[in] source A File object, such as returned by scons Library() or Program().
\param[in] suffix_to_remove For executable, we can build in Intermediate build two different executables:
hyperelasticity-static and hyperelasticity-shared. However in the Build there are two different folders
for static and shared, and the name can simply be hyperelasticity. suffix_to_remove is therefore "-static"
or "-shared"; correct one is yielded by "-{0}".format(env["LIBRARY_TYPE"]).
This function returns the name of the program or the library, without the path.
"""
assert(len(source) == 1)
source_file = str(source[0])
ret = os.path.split(source_file)[1]
if suffix_to_remove:
assert ret.endswith(suffix_to_remove)
pos = len(ret) - len(suffix_to_remove)
ret = ret[:pos]
return ret
def InstallStaticLibrary(target, source, env):
"""
This function is expected to be used through Scons 'Command'; hence its prototype.
\param[in] target List of Scons object targets; for this specific function the number of targets should be one.
\param[in] source List of Scons object sources; for this specific function the number of sources should be one.
\param[in] env Scons object environment.
The static library is copied into a target folder.
"""
# Copy the library file.
assert(len(source) == 1)
assert(len(target) == 1)
src_name = str(source[0])
target_name = str(target[0])
env.Execute(SCons.Defaults.Copy(target_name, src_name))
def InstallDynamicLibrary(target, source, env):
"""
This function is expected to be used through Scons 'Command'; hence its prototype.
\param[in] target List of Scons object targets; for this specific function the number of targets should be one.
\param[in] source List of Scons object sources; for this specific function the number of sources should be one.
\param[in] env Scons object environment.
The dynamic library is copied into a target folder and its content is edited so that the libraries can be used from anywhere (absolute path are put instead of previous relative ones).
"""
InstallStaticLibrary(target, source, env)
target_name = str(target[0])
# Then run install_name_tool if you're on Mac OS X.
if env['PLATFORM'] == 'darwin':
cmd = ("install_name_tool", "-id", os.path.abspath(target_name), target_name)
subprocess.Popen(" ".join(cmd), shell = True).communicate()
def InstallExecutable(target, source, env):
"""
Copy the executable into the install folder.
See InstallDynamicLibrary() for a more thorough explanation of each term.
"""
# Copy the executable file.
assert(len(source) == 1)
assert(len(target) == 1)
src_name = str(source[0])
target_name = str(target[0])
env.Execute(SCons.Defaults.Copy(target_name, src_name))
def CreateObjectList(env, source_list):
"""Go through the list of source files and ask for each of them for the creation of an object.
\param[in] source_list List of source files to consider.
\return List of objects (as SCons objects; names can be derived with str()).
"""
ret = []
for source_file in source_list:
try:
obj_file = env.Object(source_file)
except:
# ThirdParty add some flags to disable warnings; to objects are given instead of sources.
# In this case the call above raise an exception; I assume here it is because \a source_file
# is already an object.
obj_file = source_file
ret.append(obj_file)
return ret
def Prelink(target, source, env):
"""Function that defines the namesake builder.
\param[in] target Single object file that agglomerate all those entailed for the library. The reason of
that step is that in MoReFEM some symbols must be resolved before linking (the ones related to the
factories).
\param[in] source List of objects as created by CreateObjectList() function.
\param[in] env Environment. Not used but signature of this function is constrained by SCons builders.
\return None SCons requested return value when the target could be properly created.
"""
assert len(target) == 1
object_name_list = []
for my_object in source:
name = str(my_object)
object_name_list.append(name)
cmd = "ld -r {0} -o {1}".format(" ".join(object_name_list), str(target[0]))
subprocess.Popen(cmd, shell = True).communicate()
return None
def CustomStaticLibrary(env, name, source_file_list):
"""Create a static library where all the objects have been prelinked in a single one.
The reason for this is that MoReFEM use a FactoryPattern which loads data in the factory before the
program even starts. With a standard StaticLibrary, these informations aren't kept and the program fails in runtime.
The library does the same operation as 'Perform Single-Object Prelink = yes' option in XCode settings.
"""
# First create the list of objects.
object_list = CreateObjectList(env, source_file_list)
# Call the Prelink builder to create a single object file that covers the whole library.
single_obj_file = "prelink_{0}.o".format(name)
env.Prelink(single_obj_file, object_list)
# Create the static library from the result of the Prelink builder.
return env.StaticLibrary(name, single_obj_file,
LIBS=env["LINK_LIBRARIES_LIST"],
LIBPATH=env['LIBPATH'])
def MoReFEMLibrary(env, name, source_file_list, suppl_link_libraries = None):
"""Build a library from a list of sources; its nature depends on env["LIBRARY_TYPE"].
\param[in] env Environment.
\param[in] name Name of the library to build. Don't bother with extensions: SCons handles them itself.
\param[in] source_file_list List of source (or object in fact) files to include in the library.
\param[in] suppl_link_libraries List of additional libraries required by the library (env["LINK_LIBRARIES_LIST"] already covers the libraries shared by all MoReFEM libraries. Only relevant for shared libraries.
\return The Library as a SCons node object.
"""
if env["LIBRARY_TYPE"] == "static":
return CustomStaticLibrary(env, name, source_file_list)
else:
libs = []
if suppl_link_libraries:
libs = suppl_link_libraries
libs.extend(env["LINK_LIBRARIES_LIST"])
return env.SharedLibrary(name, source_file_list, LIBS=libs, LIBPATH=env['LIBPATH'])
def MoReFEMProgram(env, name, source_file, suppl_link_libraries = None):
"""Build a program.
\param[in] env Environment.
\param[in] name Name of the program to build. Don't bother with extensions: SCons handles them itself.
A -static or -shared is added to this name in IntermediateBuild, but does not appear in final build.
\param[in] source_file The source file that include the main (and if you wish other cpp files as well in fact).
\param[in] suppl_link_libraries List of additional libraries required by the program (env["LINK_LIBRARIES_LIST"] already covers the libraries shared by any MoReFEM program).
\return The Program as a SCons node object.
"""
libs = []
if suppl_link_libraries:
libs = suppl_link_libraries
libs.extend(env["LINK_LIBRARIES_LIST"])
program = env.Program('{0}-{1}'.format(name, env["LIBRARY_TYPE"]),
source_file,
LIBS=libs,
LIBPATH=env['LIBPATH'])
env.Alias(name, os.path.join(env["TARGET_VARIANT_DIR"], name))
return program
def DefineEnvironment(vars, morefem_root = None):
"""Define the SCons environment object.
\param[in] morefem_root Path to the MoReFEM root folder (named 'MoReFEM'). If empty, a default value
defined from current location is attempted.
It is there that compilation flags are set; informations are taken from dedicated folders sit in current
MoReFEM/SCons directory.
"""
env = SCons.Environment.Environment(variables = vars,
CFLAGS = ['-std=c99'],
CXXFLAGS = ['-std=c++1z'])
#env['ENV']['TERM'] = os.environ['TERM']
if not morefem_root:
env['MOREFEM_ROOT'] = os.path.join(os.getcwd(), '..')
else:
env['MOREFEM_ROOT'] = morefem_root
env['MOREFEM_SCONS'] = os.path.join(env['MOREFEM_ROOT'], 'SCons')
macro_flags = [
'OPS_WITH_EXCEPTION',
'SELDON_WITH_LAPACK',
'SELDON_WITH_BLAS',
'SELDON_WITH_COMPILED_LIBRARY'
]
# I assume here the compiler displays options close to gcc ones... If not some rewrite should be involved here.
if env['MODE'] == 'debug':
macro_flags.extend(['DEBUG=1', 'SELDON_CHECK_BOUNDS', 'SELDON_CHECK_DIMENSIONS'])
optimization_flags = ['-g', ]
env['PETSC_MODE_INCL_DIR'] = env['PETSC_DEBUG_INCL_DIR']
env['PETSC_MODE_LIB_DIR'] = env['PETSC_DEBUG_LIB_DIR']
if env['MODE'] == 'full_debug':
macro_flags.append('MOREFEM_CHECK_UPDATE_GHOSTS_CALL_RELEVANCE')
else:
macro_flags.append('NDEBUG')
optimization_flags=['-O3', ]
env['PETSC_MODE_INCL_DIR'] = env['PETSC_RELEASE_INCL_DIR']
env['PETSC_MODE_LIB_DIR'] = env['PETSC_RELEASE_LIB_DIR']
# Add specific macros if asked in configuration file.
specific_macro_list = \
( \
"MOREFEM_CHECK_UPDATE_GHOSTS_CALL_RELEVANCE", \
"MOREFEM_EXTENDED_TIME_KEEP", \
"MOREFEM_CHECK_NAN_AND_INF"
)
for entry in specific_macro_list:
if env[entry]:
macro_flags.append(entry)
if env['MODE'] == 'callgrind':
optimization_flags.append('-g')
#---------------------------------------------------------------------------------------
# Read and load informations relevant for the chosen compiler.
#---------------------------------------------------------------------------------------
scons_compiler_folder = os.path.realpath(os.path.join(env['MOREFEM_SCONS'], 'Compilers'))
assert(os.path.exists(scons_compiler_folder)) # If not, SConstruct does not reflect MoReFEM architecture.
try:
compiler_module = imp.load_source(env['COMPILER'], '{0}/{1}.py'.format(scons_compiler_folder, \
env['COMPILER']))
warning_flags = compiler_module.WarningFlags()
miscellaneous_flags = compiler_module.MiscellaneousFlags(env)
if env['MODE'] == 'debug':
macro_flags.extend(compiler_module.DebugMacroFlags())
except Exception as e:
print("Exception found: {0}".format(e))
compiler_list = [file for file in os.listdir(scons_compiler_folder) if os.path.splitext(file)[1] == ".py"]
compiler_list = [compiler[:-3] for compiler in compiler_list]
print("[ERROR] Compiler suite ('{0}') is not yet foreseen in current Scons build; only available compilers are those detailed in {1}: "\
"{2}.".format(env['COMPILER'],
scons_compiler_folder, \
compiler_list))
env.Exit(1)
#---------------------------------------------------------------------------------------
# Set the compiler flags from what has been gathered previously.
#---------------------------------------------------------------------------------------
env.Append(CFLAGS=optimization_flags)
env.Append(CXXFLAGS=optimization_flags)
env.Append(CXXFLAGS=warning_flags)
env.Append(CXXFLAGS=miscellaneous_flags)
env.Append(CPPDEFINES=macro_flags)
# Add to the environment the Prelink builder.
builder = SCons.Builder.Builder(action=Prelink)
env.Append(BUILDERS = {'Prelink': builder})
# List of include directories.
include_dirs = [
os.path.join(env['MOREFEM_ROOT'], 'Sources'),
os.path.join(env['MOREFEM_ROOT'], 'Sources', 'ThirdParty', 'Source'),
os.path.join(env['MOREFEM_ROOT'], 'Sources', 'ThirdParty', 'Source', 'Tclap', 'include'),
env['OPEN_MPI_INCL_DIR'],
env['PETSC_GENERAL_INCL_DIR'],
env['PETSC_MODE_INCL_DIR'],
env['PARMETIS_INCL_DIR'],
env['LUA_INCL_DIR'],
env['BOOST_INCL_DIR'],
os.path.join(env['MOREFEM_ROOT'], 'Sources', 'ThirdParty', 'Source', 'Ops'),
os.path.join(env['MOREFEM_ROOT'], 'Sources', 'ThirdParty', 'Source', 'Seldon')
]
if env['PHILLIPS_DIR']:
include_dirs.append(env['PHILLIPS_DIR'])
for folder in include_dirs:
if not os.path.isdir(folder):
<