simh-testsetgenerator/cmake/generate.py
B. Scott Michel 8b14bb69be
CMake build infrastructure II (#53)
* CMake build infrastructure

The squashed commit that builds and packages releases for the SIMH
simulator suite with CMake, version 3.14 or newer.

See README-CMake.md for documentation.
2023-05-17 20:18:42 -04:00

192 lines
No EOL
7.7 KiB
Python

## generate.py
##
## Generate the simulator CMakeLists.txt from the top-level makefile.
##
## This is the top-level driver: process options, search for the
## makefile, parse the makefile and walk its dependencies, and,
## finally, output the CMakeLists.txt(s) and simh-simulators.cmake.
##
## Author: B. Scott Michel
## ("scooter me fecit")
import sys
import os.path
import argparse
import re
GEN_SCRIPT_DIR = os.path.dirname(__file__)
GEN_SCRIPT_NAME = os.path.basename(__file__)
import pprint
import simgen.cmake_container as SCC
import simgen.parse_makefile as SPM
import simgen.packaging as SPKG
## from simgen.text_file import TextFile
def process_makefile(makefile_dir, debug=0):
the_makefile = os.path.join(makefile_dir, "makefile")
print('{0}: Processing {1}'.format(GEN_SCRIPT_NAME, the_makefile))
(defs, rules, actions) = SPM.parse_makefile(the_makefile)
if debug >= 4:
pprint.pp(defs)
all_rule = rules.get('all')
if all_rule is None:
print('{0}: "all" rule not found. Cannot process.'.format(GEN_SCRIPT_NAME))
simulators = SCC.CMakeBuildSystem()
for all_targ in SPM.shallow_expand_vars(all_rule, defs).split():
print("{0}: all target {1}".format(GEN_SCRIPT_NAME, all_targ))
walk_target_deps(all_targ, defs, rules, actions, simulators, debug=debug)
experimental_rule = rules.get('experimental')
for experimental_targ in SPM.shallow_expand_vars(experimental_rule, defs).split():
print("{0}: exp target {1}".format(GEN_SCRIPT_NAME, experimental_targ))
walk_target_deps(experimental_targ, defs, rules, actions, simulators, debug=debug)
simulators.collect_vars(defs, debug=debug)
return simulators
## Makefile target dependencies to filter out.
_ignored_deps = [
'${SIM}',
'${BUILD_ROMS}'
]
## Simulator compile/link action pattern
_compile_act_rx = re.compile(r"\$[({]CC[)}]\s*(.*)")
_test_name_rx = re.compile(r"\$@\s*\$\(call\s+find_test,\s*(.*),(.*)\)\s+\$")
def walk_target_deps(target, defs, rules, actions, simulators, depth='', debug=0):
""" Recursively walk a target's dependencies, i.e., the right hand side of a make rule.
Descend into each dependency to find something that looks like a simulator's
source code list. Once source code list is found, extract simulator defines, includes,
source files and set flags.
"""
if debug >= 1:
print('{0}-- target: {1}'.format(depth, target))
target_deps = SPM.target_dep_list(target, rules, defs)
has_buildrom = any(filter(lambda dep: dep == '${BUILD_ROMS}', target_deps))
if debug >= 1:
print('{0} has_buildrom {1}', has_buildrom)
deps = [dep for dep in target_deps if dep not in _ignored_deps]
targ_actions = actions.get(target)
if targ_actions:
depth3 = depth + ' '
if debug >= 2:
print('{0}deps {1}'.format(depth3, deps))
# Are the dependencies a source code list?
expanded_deps = [l for slist in [ SPM.shallow_expand_vars(dep, defs).split() for dep in deps ] for l in slist]
if debug >= 3:
print('{0}expanded_deps {1}'.format(depth3, expanded_deps))
if any(filter(lambda f: f.endswith('.c'), expanded_deps)):
if debug >= 1:
print('{0}sim sources {1}'.format(depth3, deps))
if debug >= 2:
print('{0}targ_actions {1}'.format(depth3, targ_actions))
# The simulators' compile and test actions are very regular and easy to find:
compile_act = None
test_name = None
sim_dir = None
for act in targ_actions:
m_cact = _compile_act_rx.match(act)
m_test = _test_name_rx.match(act)
if m_cact:
compile_act = m_cact.group(1)
elif m_test:
(sim_dir, test_name) = m_test.group(1, 2)
if debug >= 2:
print('{0}sim_dir {1}'.format(depth3, sim_dir))
print('{0}compile_act {1}'.format(depth3, compile_act))
print('{0}test_name {1}'.format(depth3, test_name))
if compile_act and test_name and sim_dir:
sim_name = target.replace("${BIN}", "").replace("${EXE}", "")
# Just in case there are vestiges of old-style make variables
sim_name = sim_name.replace("$(BIN)", "").replace("$(EXE)", "")
if debug >= 2:
print('{0}sim_name {1}'.format(depth3, sim_name))
simulators.extract(compile_act, test_name, sim_dir, sim_name, defs, has_buildrom, debug, depth+' ')
else:
# No actions associated with the dependency(ies), which means that the dependency(ies)
# are meta-targets. Continue to walk.
for dep in deps:
walk_target_deps(dep, defs, rules, actions, simulators, depth=depth+' ', debug=debug)
if __name__ == '__main__':
args = argparse.ArgumentParser(description="SIMH simulator CMakeLists.txt generator.")
args.add_argument('--debug', nargs='?', const=1, default=0, type=int,
help='Debug level (0-3, 0 == off)')
args.add_argument('--srcdir', default=None,
help='makefile source directory.')
## args.add_argument('--file', '-f', default=os.path.join(GEN_SCRIPT_DIR, 'simh_makefile.cmake'),
## help='Output file for "all-in-one" CMakeLists.txt, default is simh_makefile.cmake')
flags = vars(args.parse_args())
debug_level = flags.get('debug')
makefile_dir = flags.get('srcdir')
print('{0}: Expecting to emit {1} simulators.'.format(GEN_SCRIPT_NAME, len(SPKG.package_info.keys())))
found_makefile = True
if makefile_dir is None:
## Find the makefile, which should be one directory up from this Python
## module
makefile_dir = GEN_SCRIPT_DIR
print('{0}: Looking for makefile, starting in {1}'.format(GEN_SCRIPT_NAME, makefile_dir))
the_makefile = ''
while makefile_dir:
the_makefile = os.path.join(makefile_dir, "makefile")
if os.path.exists(the_makefile):
break
else:
makefile_dir = os.path.dirname(makefile_dir)
print('{0}: Looking for makefile, trying {1}'.format(GEN_SCRIPT_NAME, makefile_dir))
if not the_makefile:
found_makefile = False
else:
the_makefile = os.path.join(makefile_dir, "makefile")
if not os.path.exists(the_makefile):
found_makefile = False
if not found_makefile:
print('{0}: SIMH top-level makefile not found, relative to {1}'.format(GEN_SCRIPT_NAME, GEN_SCRIPT_DIR))
sys.exit(1)
sims = process_makefile(makefile_dir, debug=debug_level)
## Sanity check: Make sure that all of the simulators in SPKG.package_info have
## been encountered
for simdir in sims.dirs.keys():
for sim in sims.dirs[simdir].simulators.keys():
SPKG.package_info[sim].encountered()
orphans = [ sim for sim, pkg_info in SPKG.package_info.items() if not pkg_info.was_processed() ]
if len(orphans) > 0:
print('{0}: Simulators not extracted from makefile:'.format(GEN_SCRIPT_NAME))
for orphan in orphans:
print('{0}{1}'.format(' ' * 4, orphan))
sys.exit(1)
if debug_level >= 1:
pp = pprint.PrettyPrinter()
pp.pprint(sims)
## Emit all of the individual CMakeLists.txt
sims.write_simulators(makefile_dir, debug=debug_level)
## Emit the packaging data
SPKG.write_packaging(makefile_dir)