Page MenuHomec4science

site_init.py
No OneTemporary

File Metadata

Created
Fri, Apr 19, 06:29

site_init.py

# -*- coding: utf-8 -*-
#
# Copyright (©) 2016-2024 EPFL (École Polytechnique Fédérale de Lausanne),
# Laboratory (LSMS - Laboratoire de Simulation en Mécanique des Solides)
# Copyright (©) 2020-2024 Lucas Frérot
#
# 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 <https://www.gnu.org/licenses/>.
import subprocess
from detect import FindPybind11
from SCons.Script import Configure
from SCons.Errors import StopError
# ------------------------------------------------------------------------------
def pybind11(env):
"""A tool to configure pybind11"""
def execute(command):
return [line for line in subprocess.check_output(
command, universal_newlines=True).split('\n')
if line != ""]
# Create a clone so we do not modify env
clone = env.Clone()
# Set variables for clone
FindPybind11(clone)
includes = clone['CPPPATH']
# Extension of shared library for python
try:
extension = execute(
env.subst('${py_exec}-config --extension-suffix').split())[0]
except subprocess.CalledProcessError:
extension = ".so"
def pybind11_builder(env, target, source, **kwargs):
"""Create a pybind11 module"""
clone = env.Clone()
clone.AppendUnique(CPPPATH=includes)
clone['SHLIBSUFFIX'] = extension
clone.AppendUnique(SHLINKFLAGS=['-fvisibility=hidden'])
return clone.SharedLibrary(target, source, **kwargs)
# Add pseudo-builder to master environment
env.AddMethod(pybind11_builder, 'Pybind11Module')
# ------------------------------------------------------------------------------
def pretty_cmd_print(command, target, source, env):
colors = env['COLOR_DICT']
if 'Copy' in command:
color = colors['gray']
action = 'Copying'
elif 'Creating' in command:
color = colors['yellow']
action = 'Generating'
elif 'database' in command:
color = colors['yellow']
action = 'Generating'
elif 'pytest' in command:
color = colors['green']
action = 'Running tests'
target[0] = target[0].dir
elif 'pip' in command:
color = colors['blue']
if 'user' in command:
action = 'Symlinking'
target[0] = target[0].dir
elif 'prefix' in command:
action = 'Installing'
target[0] = env.subst(target[0].dir.path + ' to ${prefix}')
elif 'tar' in command:
color = colors['blue']
action = 'Archiving'
target[0] = '../' + target[0].path
else:
print(command)
return
print("{color}[{action}] {end}{target}".format(
color=color,
end=colors['end'],
target=target[0],
action=action
))
# ------------------------------------------------------------------------------
def CheckPythonModule(context, module):
"""Checks the existence of a python module"""
context.Message('Checking for Python module {}... '.format(module))
env = context.sconf.env
command = [env.subst('${py_exec}'), '-c', 'import {}'.format(module)]
context.Log('Executing external command: {}\n'.format(command))
try:
subprocess.check_output(command, stderr=subprocess.STDOUT)
result = True
except subprocess.CalledProcessError as e:
result = False
output = bytes(e.output)
context.Log(output.decode() + '\n')
context.Result(result)
return result
# ------------------------------------------------------------------------------
def CheckCompilerFlag(context, flag):
"Check compiler provides flag"
context.Message('Checking compiler flag {}... '.format(
context.sconf.env.subst(flag)))
test_file = """
int main() {
return 0;
}
"""
env = context.sconf.env.Clone(CXXFLAGS=[flag],
LINKFLAGS=[flag])
context.sconf.env = env
result = context.TryLink(test_file, '.cpp')
context.Result(result)
return result
# ------------------------------------------------------------------------------
def dummy_command(env, command, error_msg):
"""Creates a dummy scons command"""
def print_error(*args, **kwargs):
print(error_msg)
def print_cmd(*args, **kwargs):
pass
comm = env.Command('#.phony_{}'.format(command),
'', print_error, PRINT_CMD_LINE_FUNC=print_cmd)
env.Alias(command, comm)
# ------------------------------------------------------------------------------
def get_python_version(env):
versions_script = """
from __future__ import print_function
from sysconfig import get_python_version
print(get_python_version())"""
version = subprocess.check_output([env['py_exec'], "-c", versions_script],
universal_newlines=True).replace('\n', '')
print(version)
return version
# ------------------------------------------------------------------------------
def basic_checks(env):
custom_tests = {
'CheckCompilerFlag': CheckCompilerFlag,
}
conf = Configure(env, custom_tests=custom_tests)
if not conf.CheckCXX():
raise StopError('Could not find working C++ compiler')
for flag in env['CXXFLAGS']:
if not conf.CheckCompilerFlag(flag):
raise StopError(
env.subst('Compiler does not support flag "${CXXFLAGS}"'))
# flags are overwritten by CheckCompilerFlag
conf.env['CXXFLAGS'] = env['CXXFLAGS']
def check_type(cpp_type, stl_header):
if not conf.CheckType(
cpp_type,
'#include <{stl_header}>'.format(stl_header=stl_header),
'C++'
):
raise StopError(
'Standard type "{cpp_type}" is not available'
.format(cpp_type=cpp_type))
check_type(env['real_type'], 'cstdlib')
check_type(env['integer_type'], 'cstdlib')
check_type('std::multiplies<void>', 'functional')
check_type('std::unique_ptr<void>', 'memory')
conf.Finish()
# ------------------------------------------------------------------------------
def linting(env):
"""Define linting targets"""
linting = [] # list of linting commands
conf = Configure(env)
if conf.CheckProg('run-clang-tidy'):
linting.append(env.Command(
target='.phony_clang_tidy',
source='',
action=" ".join([
'run-clang-tidy -extra-arg=-UTAMAAS_USE_MPI',
'-header-filter="${build_dir}/.*" -quiet',
*map(lambda d: str(env.Dir(d)), ['#src', '#python', '#tests']),
])
))
env.Alias('clang-tidy', linting[-1])
if conf.CheckProg('git-clang-format'):
linting.append(env.Command(
target='.phony_clang_format',
source='',
action="git-clang-format --diff HEAD",
))
env.Alias('clang-format', linting[-1])
if conf.CheckProg('flake8'):
linting.append(env.Command(
target='.phony_flake8',
source='',
action=f'flake8 {env.Dir("#python")}; flake8 {env.Dir("#tests")}'
))
env.Alias('flake8', linting[-1])
if conf.CheckProg('mypy'):
linting.append(env.Command(
target='.phony_mypy',
source='',
action='mypy --ignore-missing-import ${build_dir}/python'
))
env.Alias('mypy', linting[-1])
env.Alias('lint', linting)
conf.Finish()

Event Timeline