Teach check_tool_requirements to check for edalize versions

We need this specific edalize version because recent verilators have
got pickier about string parameter passing, breaking the
"MultiplierImplementation" parameter.

As well as teaching check_tool_requirements.py to get the edalize
version from pip3, this patch also does a bit of tidying up, coping
better if tool_requirements.py is missing or malformed.
This commit is contained in:
Rupert Swarbrick 2020-04-14 13:02:09 +01:00 committed by Rupert Swarbrick
parent 5c51bc7e4d
commit db6f8f050e
2 changed files with 91 additions and 10 deletions

View file

@ -6,4 +6,5 @@
# and inserted into the Sphinx-generated documentation.
__TOOL_REQUIREMENTS__ = {
'verilator': '4.028',
'edalize': '0.2.0'
}

View file

@ -6,15 +6,50 @@
from distutils.version import StrictVersion
import logging as log
import os
import re
import subprocess
import sys
# Display INFO log messages and up.
log.basicConfig(level=log.INFO, format="%(levelname)s: %(message)s")
# Populate __TOOL_REQUIREMENTS__
topsrcdir = os.path.join(os.path.dirname(__file__), '..')
exec(open(os.path.join(topsrcdir, 'tool_requirements.py')).read())
def get_tool_requirements_path():
'''Return the path to tool_requirements.py, at the top of the Ibex repo'''
# top_src_dir is the top of the repository
top_src_dir = os.path.normpath(os.path.join(os.path.dirname(__file__),
'..'))
return os.path.join(top_src_dir, 'tool_requirements.py')
def read_tool_requirements(path=None):
'''Read tool requirements from a Python file'''
if path is None:
path = get_tool_requirements_path()
with open(path, 'r') as pyfile:
globs = {}
exec(pyfile.read(), globs)
# We expect the exec call to have populated globs with a
# __TOOL_REQUIREMENTS__ dictionary.
reqs = globs.get('__TOOL_REQUIREMENTS__')
if reqs is None:
log.error('The Python file at {} did not define '
'__TOOL_REQUIREMENTS__.'
.format(path))
return None
# reqs should be a dictionary (mapping tool name to minimum version)
if not isinstance(reqs, dict):
log.error('The Python file at {} defined '
'__TOOL_REQUIREMENTS__, but it is not a dict.'
.format(path))
return None
return reqs
def get_verilator_version():
try:
@ -32,8 +67,44 @@ def get_verilator_version():
log.error(e.stdout)
return None
def check_version(tool_name, required_version, actual_version):
if required_version is None or actual_version is None:
def pip3_get_version(tool):
'''Run pip3 to find the version of an installed module'''
cmd = ['pip3', 'show', tool]
try:
proc = subprocess.run(cmd,
check=True,
stderr=subprocess.STDOUT,
stdout=subprocess.PIPE,
universal_newlines=True)
except subprocess.CalledProcessError as err:
log.error('pip3 command failed: {}'.format(err))
log.error("Failed to get version of {} with pip3: is it installed?"
.format(tool))
log.error(err.stdout)
return None
version_re = 'Version: (.*)'
for line in proc.stdout.splitlines():
match = re.match(version_re, line)
if match:
return match.group(1)
# If we get here, we never saw a version line.
log.error('No output line from running {} started with "Version: ".'
.format(cmd))
return None
def check_version(requirements, tool_name, getter):
required_version = requirements.get(tool_name)
if required_version is None:
log.error('Requirements file does not specify version for {}.'
.format(tool_name))
return False
actual_version = getter()
if actual_version is None:
return False
if StrictVersion(actual_version) < StrictVersion(required_version):
@ -47,17 +118,26 @@ def check_version(tool_name, required_version, actual_version):
def main():
any_failed = False
# Get tool requirements
tool_requirements = read_tool_requirements()
if tool_requirements is None:
return 1
if not check_version('verilator', __TOOL_REQUIREMENTS__['verilator'],
get_verilator_version()):
any_failed = True
all_good = True
all_good &= check_version(tool_requirements,
'verilator',
get_verilator_version)
all_good &= check_version(tool_requirements,
'edalize',
lambda: pip3_get_version('edalize'))
if any_failed:
if not all_good:
log.error("Tool requirements not fulfilled. "
"Please update the tools and retry.")
return 1
return 0
if __name__ == "__main__":
sys.exit(main())