Simplify hook tool implementation

The "hook tools" were capable of hooking into many commands run by the build system.
To my knowlage, the only hook is the "post-build-hook". The post build hook could be
easier to reason about if the implementation is specialized for just post-build
hooking.

This commit make it much easier to point out where post build hooks are called by
making the call explicit.
pull/9738/head
Jimmy Brisson 2019-02-14 14:39:56 -06:00
parent 355f09bbac
commit aeeb43fb3c
6 changed files with 7 additions and 282 deletions

View File

@ -1,214 +0,0 @@
""" Configurable hooks in the build system. Can be used by various platforms
to customize the build process.
"""
################################################################################
# Hooks for the various parts of the build process
# Internal mapping of hooks per tool
_HOOKS = {}
# Internal mapping of running hooks
_RUNNING_HOOKS = {}
# Available hook types
_HOOK_TYPES = ["binary", "compile", "link", "assemble"]
# Available hook steps
_HOOK_STEPS = ["pre", "replace", "post"]
# Hook the given function. Use this function as a decorator
def hook_tool(function):
"""Decorate a function as a tool that may be hooked"""
tool = function.__name__
tool_flag = "_" + tool + "_done"
def wrapper(t_self, *args, **kwargs):
"""The hooked function itself"""
# if a hook for this tool is already running, it's most likely
# coming from a derived class, so don't hook the super class version
if _RUNNING_HOOKS.get(tool, False):
return function(t_self, *args, **kwargs)
_RUNNING_HOOKS[tool] = True
# If this tool isn't hooked, return original function
if tool not in _HOOKS:
res = function(t_self, *args, **kwargs)
_RUNNING_HOOKS[tool] = False
return res
tooldesc = _HOOKS[tool]
setattr(t_self, tool_flag, False)
# If there is a replace hook, execute the replacement instead
if "replace" in tooldesc:
res = tooldesc["replace"](t_self, *args, **kwargs)
# If the replacement has set the "done" flag, exit now
# Otherwise continue as usual
if getattr(t_self, tool_flag, False):
_RUNNING_HOOKS[tool] = False
return res
# Execute pre-function before main function if specified
if "pre" in tooldesc:
tooldesc["pre"](t_self, *args, **kwargs)
# Execute the main function now
res = function(t_self, *args, **kwargs)
# Execute post-function after main function if specified
if "post" in tooldesc:
post_res = tooldesc["post"](t_self, *args, **kwargs)
_RUNNING_HOOKS[tool] = False
return post_res or res
else:
_RUNNING_HOOKS[tool] = False
return res
return wrapper
class Hook(object):
"""A compiler class that may be hooked"""
def __init__(self, target, toolchain):
_HOOKS.clear()
self._cmdline_hooks = {}
self.toolchain = toolchain
target.init_hooks(self, toolchain)
# Hook various functions directly
@staticmethod
def _hook_add(hook_type, hook_step, function):
"""Add a hook to a compile function
Positional arguments:
hook_type - one of the _HOOK_TYPES
hook_step - one of the _HOOK_STEPS
function - the function to add to the list of hooks
"""
if hook_type not in _HOOK_TYPES or hook_step not in _HOOK_STEPS:
return False
if hook_type not in _HOOKS:
_HOOKS[hook_type] = {}
_HOOKS[hook_type][hook_step] = function
return True
def hook_add_compiler(self, hook_step, function):
"""Add a hook to the compiler
Positional Arguments:
hook_step - one of the _HOOK_STEPS
function - the function to add to the list of hooks
"""
return self._hook_add("compile", hook_step, function)
def hook_add_linker(self, hook_step, function):
"""Add a hook to the linker
Positional Arguments:
hook_step - one of the _HOOK_STEPS
function - the function to add to the list of hooks
"""
return self._hook_add("link", hook_step, function)
def hook_add_assembler(self, hook_step, function):
"""Add a hook to the assemble
Positional Arguments:
hook_step - one of the _HOOK_STEPS
function - the function to add to the list of hooks
"""
return self._hook_add("assemble", hook_step, function)
def hook_add_binary(self, hook_step, function):
"""Add a hook to the elf to binary tool
Positional Arguments:
hook_step - one of the _HOOK_STEPS
function - the function to add to the list of hooks
"""
return self._hook_add("binary", hook_step, function)
# Hook command lines
def _hook_cmdline(self, hook_type, function):
"""Add a hook to a command line function
Positional arguments:
hook_type - one of the _HOOK_TYPES
function - the function to add to the list of hooks
"""
if hook_type not in _HOOK_TYPES:
return False
self._cmdline_hooks[hook_type] = function
return True
def hook_cmdline_compiler(self, function):
"""Add a hook to the compiler command line
Positional arguments:
function - the function to call
"""
return self._hook_cmdline("compile", function)
def hook_cmdline_linker(self, function):
"""Add a hook to the linker command line
Positional arguments:
function - the function to call
"""
return self._hook_cmdline("link", function)
def hook_cmdline_assembler(self, function):
"""Add a hook to the assembler command line
Positional arguments:
function - the function to call
"""
return self._hook_cmdline("assemble", function)
def hook_cmdline_binary(self, function):
"""Add a hook to the elf to bin tool command line
Positional arguments:
function - the function to call
"""
return self._hook_cmdline("binary", function)
# Return the command line after applying the hook
def _get_cmdline(self, hook_type, cmdline):
"""Get the command line after running all hooks
Positional arguments:
hook_type - one of the _HOOK_TYPES
cmdline - the initial command line
"""
if hook_type in self._cmdline_hooks:
cmdline = self._cmdline_hooks[hook_type](
self.toolchain.__class__.__name__, cmdline)
return cmdline
def get_cmdline_compiler(self, cmdline):
"""Get the compiler command line after running all hooks
Positional arguments:
cmdline - the initial command line
"""
return self._get_cmdline("compile", cmdline)
def get_cmdline_linker(self, cmdline):
"""Get the linker command line after running all hooks
Positional arguments:
cmdline - the initial command line
"""
return self._get_cmdline("link", cmdline)
def get_cmdline_assembler(self, cmdline):
"""Get the assmebler command line after running all hooks
Positional arguments:
cmdline - the initial command line
"""
return self._get_cmdline("assemble", cmdline)
def get_cmdline_binary(self, cmdline):
"""Get the binary command line after running all hooks
Positional arguments:
cmdline - the initial command line
"""
return self._get_cmdline("binary", cmdline)
################################################################################

View File

@ -355,7 +355,7 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
def is_PSA_non_secure_target(self):
return 'NSPE_Target' in self.labels
def init_hooks(self, hook, toolchain):
def get_post_build_hook(self, toolchain):
"""Initialize the post-build hooks for a toolchain. For now, this
function only allows "post binary" hooks (hooks that are executed
after the binary image is extracted from the executable file)
@ -404,8 +404,7 @@ class Target(namedtuple("Target", "name json_data resolution_order resolution_or
if toolchain_restrictions and \
not toolchain_labels.intersection(toolchain_restrictions):
return
# Finally, hook the requested function
hook.hook_add_binary("post", getattr(cls, function_name))
return getattr(cls, function_name)
################################################################################
# Target specific code goes in this section

View File

@ -36,7 +36,6 @@ from hashlib import md5
from ..utils import (run_cmd, mkdir, rel_path, ToolException,
NotSupportedException, split_path, compile_worker)
from ..settings import MBED_ORG_USER, PRINT_COMPILER_OUTPUT_AS_LINK
from .. import hooks
from ..notifier.term import TerminalNotifier
from ..resources import FileType
from ..memap import MemapParser
@ -96,7 +95,7 @@ class mbedToolchain:
self.name = self.__class__.__name__
# compile/assemble/link/binary hooks
self.hook = hooks.Hook(target, self)
self._post_build_hook = target.get_post_build_hook(self.name)
# Toolchain flags
self.flags = deepcopy(build_profile or self.profile_template)
@ -653,6 +652,9 @@ class mbedToolchain:
self.progress("elf2bin", name)
self.binary(r, elf, bin)
if self._post_build_hook:
self.progress("post-build", name)
self._post_build_hook(self, r, elf, bin)
# Initialize memap and process map file. This doesn't generate output.
self.mem_stats(mapfile)
@ -1002,9 +1004,6 @@ class mbedToolchain:
Side effects:
None
Note:
This method should be decorated with @hook_tool.
"""
raise NotImplemented
@ -1024,9 +1023,6 @@ class mbedToolchain:
Side effects:
None
Note:
This method should be decorated with @hook_tool.
"""
raise NotImplemented
@ -1046,9 +1042,6 @@ class mbedToolchain:
Side effects:
None
Note:
This method should be decorated with @hook_tool.
"""
raise NotImplemented
@ -1068,9 +1061,6 @@ class mbedToolchain:
Side effect:
Runs the linker to produce the executable.
Note:
This method should be decorated with @hook_tool.
"""
raise NotImplemented
@ -1087,9 +1077,6 @@ class mbedToolchain:
Side effect:
Runs the archiving tool to produce the library file.
Note:
This method should be decorated with @hook_tool.
"""
raise NotImplemented
@ -1107,9 +1094,6 @@ class mbedToolchain:
Side effect:
Runs the elf2bin tool to produce the simplified binary file.
Note:
This method should be decorated with @hook_tool.
"""
raise NotImplemented

View File

@ -27,7 +27,6 @@ from distutils.version import LooseVersion
from tools.targets import CORE_ARCH
from tools.toolchains import mbedToolchain, TOOLCHAIN_PATHS
from tools.hooks import hook_tool
from tools.utils import mkdir, NotSupportedException, run_cmd
class ARM(mbedToolchain):
@ -192,7 +191,6 @@ class ARM(mbedToolchain):
return opts
@hook_tool
def assemble(self, source, object, includes):
# Preprocess first, then assemble
dir = join(dirname(object), '.temp')
@ -208,14 +206,9 @@ class ARM(mbedToolchain):
# Build main assemble command
cmd = self.asm + ["-o", object, tempfile]
# Call cmdline hook
cmd_pre = self.hook.get_cmdline_assembler(cmd_pre)
cmd = self.hook.get_cmdline_assembler(cmd)
# Return command array, don't execute
return [cmd_pre, cmd]
@hook_tool
def compile(self, cc, source, object, includes):
# Build compile command
cmd = cc + self.get_compile_options(self.get_symbols(), includes)
@ -224,9 +217,6 @@ class ARM(mbedToolchain):
cmd.extend(["-o", object, source])
# Call cmdline hook
cmd = self.hook.get_cmdline_compiler(cmd)
return [cmd]
def compile_c(self, source, object, includes):
@ -269,7 +259,6 @@ class ARM(mbedToolchain):
return new_scatter
@hook_tool
def link(self, output, objects, libraries, lib_dirs, scatter_file):
base, _ = splitext(output)
map_file = base + ".map"
@ -283,7 +272,6 @@ class ARM(mbedToolchain):
args.extend(["--scatter", new_scatter])
cmd_pre = self.ld + args
cmd = self.hook.get_cmdline_linker(cmd_pre)
if self.RESPONSE_FILES:
cmd_linker = cmd[0]
@ -293,7 +281,6 @@ class ARM(mbedToolchain):
self.notify.cc_verbose("Link: %s" % ' '.join(cmd))
self.default_cmd(cmd)
@hook_tool
def archive(self, objects, lib_path):
if self.RESPONSE_FILES:
param = ['--via', self.get_arch_file(objects)]
@ -301,13 +288,11 @@ class ARM(mbedToolchain):
param = objects
self.default_cmd([self.ar, '-r', lib_path] + param)
@hook_tool
def binary(self, resources, elf, bin):
_, fmt = splitext(bin)
# On .hex format, combine multiple .hex files (for multiple load regions) into one
bin_arg = {".bin": "--bin", ".hex": "--i32combined"}[fmt]
cmd = [self.elf2bin, bin_arg, '-o', bin, elf]
cmd = self.hook.get_cmdline_binary(cmd)
# remove target binary file/path
if exists(bin):
@ -518,18 +503,15 @@ class ARMC6(ARM_STD):
"--cpreproc_opts=%s" % ",".join(self.flags['common'] + opts)]
return opts
@hook_tool
def assemble(self, source, object, includes):
cmd_pre = copy(self.asm)
cmd_pre.extend(self.get_compile_options(
self.get_symbols(True), includes, for_asm=True))
cmd_pre.extend(["-o", object, source])
return [self.hook.get_cmdline_assembler(cmd_pre)]
return [cmd_pre]
@hook_tool
def compile(self, cc, source, object, includes):
cmd = copy(cc)
cmd.extend(self.get_compile_options(self.get_symbols(), includes))
cmd.extend(["-o", object, source])
cmd = self.hook.get_cmdline_compiler(cmd)
return [cmd]

View File

@ -22,7 +22,6 @@ from distutils.version import LooseVersion
from tools.targets import CORE_ARCH
from tools.toolchains import mbedToolchain, TOOLCHAIN_PATHS
from tools.hooks import hook_tool
from tools.utils import run_cmd, NotSupportedException
class GCC(mbedToolchain):
@ -195,18 +194,13 @@ class GCC(mbedToolchain):
opts = opts + self.get_config_option(config_header)
return opts
@hook_tool
def assemble(self, source, object, includes):
# Build assemble command
cmd = self.asm + self.get_compile_options(self.get_symbols(True), includes) + ["-o", object, source]
# Call cmdline hook
cmd = self.hook.get_cmdline_assembler(cmd)
# Return command array, don't execute
return [cmd]
@hook_tool
def compile(self, cc, source, object, includes):
# Build compile command
cmd = cc + self.get_compile_options(self.get_symbols(), includes)
@ -215,8 +209,6 @@ class GCC(mbedToolchain):
cmd.extend(["-o", object, source])
# Call cmdline hook
cmd = self.hook.get_cmdline_compiler(cmd)
if self.use_distcc:
cmd = ["distcc"] + cmd
@ -228,7 +220,6 @@ class GCC(mbedToolchain):
def compile_cpp(self, source, object, includes):
return self.compile(self.cppc, source, object, includes)
@hook_tool
def link(self, output, objects, libraries, lib_dirs, mem_map):
libs = []
for l in libraries:
@ -256,9 +247,6 @@ class GCC(mbedToolchain):
cmd.extend(['-L', L])
cmd.extend(libs)
# Call cmdline hook
cmd = self.hook.get_cmdline_linker(cmd)
if self.RESPONSE_FILES:
# Split link command to linker executable + response file
cmd_linker = cmd[0]
@ -269,7 +257,6 @@ class GCC(mbedToolchain):
self.notify.cc_verbose("Link: %s" % ' '.join(cmd))
self.default_cmd(cmd)
@hook_tool
def archive(self, objects, lib_path):
if self.RESPONSE_FILES:
param = ["@%s" % self.get_arch_file(objects)]
@ -279,16 +266,12 @@ class GCC(mbedToolchain):
# Exec command
self.default_cmd([self.ar, 'rcs', lib_path] + param)
@hook_tool
def binary(self, resources, elf, bin):
# Build binary command
_, fmt = splitext(bin)
bin_arg = {'.bin': 'binary', '.hex': 'ihex'}[fmt]
cmd = [self.elf2bin, "-O", bin_arg, elf, bin]
# Call cmdline hook
cmd = self.hook.get_cmdline_binary(cmd)
# Exec command
self.notify.cc_verbose("FromELF: %s" % ' '.join(cmd))
self.default_cmd(cmd)

View File

@ -21,7 +21,6 @@ from distutils.version import LooseVersion
from tools.targets import CORE_ARCH
from tools.toolchains import mbedToolchain, TOOLCHAIN_PATHS
from tools.hooks import hook_tool
from tools.utils import run_cmd, NotSupportedException
class IAR(mbedToolchain):
@ -185,7 +184,6 @@ class IAR(mbedToolchain):
return opts
@hook_tool
def assemble(self, source, object, includes):
# Build assemble command
cmd = self.asm + self.get_compile_options(self.get_symbols(True), includes, True) + ["-o", object, source]
@ -196,7 +194,6 @@ class IAR(mbedToolchain):
# Return command array, don't execute
return [cmd]
@hook_tool
def compile(self, cc, source, object, includes):
# Build compile command
cmd = cc + self.get_compile_options(self.get_symbols(), includes)
@ -218,7 +215,6 @@ class IAR(mbedToolchain):
def compile_cpp(self, source, object, includes):
return self.compile(self.cppc, source, object, includes)
@hook_tool
def link(self, output, objects, libraries, lib_dirs, mem_map):
# Build linker command
map_file = splitext(output)[0] + ".map"
@ -240,7 +236,6 @@ class IAR(mbedToolchain):
self.notify.cc_verbose("Link: %s" % ' '.join(cmd))
self.default_cmd(cmd)
@hook_tool
def archive(self, objects, lib_path):
if self.RESPONSE_FILES:
param = ['-f', self.get_arch_file(objects)]
@ -252,16 +247,12 @@ class IAR(mbedToolchain):
self.default_cmd([self.ar, lib_path] + param)
@hook_tool
def binary(self, resources, elf, bin):
_, fmt = splitext(bin)
bin_arg = {".bin": "--bin", ".hex": "--ihex"}[fmt]
# Build binary command
cmd = [self.elf2bin, bin_arg, elf, bin]
# Call cmdline hook
cmd = self.hook.get_cmdline_binary(cmd)
# Exec command
self.notify.cc_verbose("FromELF: %s" % ' '.join(cmd))
self.default_cmd(cmd)