kmi_defines: Factored out the run() function
It is used in later patches of this series.
Changed some well known indexes into the cc_list for a Target into
constants and added additional checks to Target's ctor.
Bug: 139885116
Change-Id: Ifec145ac6a12e62634b250c69eb27b0db5f637cb
Signed-off-by: Matthias Maennich <maennich@google.com>
diff --git a/abi/kmi_defines.py b/abi/kmi_defines.py
index f3fbd37..077e7ec 100755
--- a/abi/kmi_defines.py
+++ b/abi/kmi_defines.py
@@ -230,6 +230,26 @@
return [entry for entry in line.split() if entry.endswith(".o")]
+def run(args: List[str],
+ raise_on_failure: bool = True) -> subprocess.CompletedProcess:
+ """Run the program specified in args[0] with the arguments in args[1:]."""
+ try:
+ # This argument does not always work for subprocess.run() below:
+ # check=False
+ # neither that nor:
+ # check=True
+ # prevents an exception from being raised if the program that
+ # will be executed is not found
+
+ completion = subprocess.run(args, capture_output=True, text=True)
+ if completion.returncode != 0 and raise_on_failure:
+ raise StopError("execution failed for: " + " ".join(args))
+ return completion
+ except OSError as os_error:
+ raise StopError("failure executing: " + " ".join(args) + "\n"
+ "original OSError: " + str(os_error.args))
+
+
class KernelModule:
"""A kernel module, i.e. a *.ko file."""
def __init__(self, kofile: str) -> None:
@@ -389,19 +409,8 @@
if not file.endswith(".a"):
raise StopError("unknown file type: " + file)
- try:
- # This argument does not always work: check=False
- # neither that nor: check=True prevents an exception from
- # being raised if "ar" can not be found
- completion = subprocess.run(["ar", "t", file],
- capture_output=True,
- text=True)
- if completion.returncode != 0:
- raise StopError("ar failed for: ar t " + file)
- objs = lines_to_list(completion.stdout)
- except OSError as os_error:
- raise StopError("failure executing: ar t", file, "\n"
- "original OSError: " + str(os_error.args))
+ completion = run(["ar", "t", file])
+ objs = lines_to_list(completion.stdout)
for obj in objs:
if not os.path.isabs(obj):
@@ -413,6 +422,27 @@
class Target: # pylint: disable=too-few-public-methods
"""Target of build and the information used to build it."""
+
+ # The compiler invocation has this form:
+ # clang -Wp,-MD,file.o.d ... -c -o file.o file.c
+ # these constants reflect that knowledge in the code, e.g.:
+ # - the "-Wp,_MD,file.o.d" is at WP_MD_FLAG_INDEX
+ # - the "-c" is at index C_FLAG_INDEX
+ # - the "-o" is at index O_FLAG_INDEX
+ # - the "file.o" is at index OBJ_INDEX
+ # - the "file.c" is at index SRC_INDEX
+ #
+ # There must be at least MIN_CC_LIST_LEN options in that command line.
+ # This knowledge is verified at run time in __init__(), see comments
+ # there.
+
+ MIN_CC_LIST_LEN = 6
+ WP_MD_FLAG_INDEX = 1
+ C_FLAG_INDEX = -4
+ O_FLAG_INDEX = -3
+ OBJ_INDEX = -2
+ SRC_INDEX = -1
+
def __init__(self, obj: str, src: str, cc_line: str,
deps: List[str]) -> None:
self._obj = obj
@@ -458,12 +488,47 @@
r"-D\1=\2", cc_line)
cc_list = cc_cmd.split()
- # At least: cc -c -o file.o file.c (last four must be those shown)
- if (len(cc_list) < 5 or cc_list[-4] != "-c" or cc_list[-3] != "-o"
- or not obj.endswith(cc_list[-2])
- or not src.endswith(cc_list[-1])):
- raise StopError("unexpected or missing arguments for " + obj +
+ # TODO(pantin): the handling of -D... arguments above is done better
+ # in a later commit by using shlex.split(). Please ignore for now.
+ # TODO(pantin): possibly use ArgumentParser to make this more robust.
+
+ # The compiler invocation has this form:
+ # clang -Wp,-MD,file.o.d ... -c -o file.o file.c
+ #
+ # The following checks are here to ensure that if this assumption is
+ # broken, failures occur. The indexes *_INDEX are hardcoded, they
+ # could in principle be determined at run time, the -o argument could
+ # be in a future update to the Linux build could changed to be a
+ # single argument with the object file name (as in: -ofile.o) which
+ # could also be detected in code at a later time.
+
+ if (len(cc_list) < Target.MIN_CC_LIST_LEN
+ or not cc_list[Target.WP_MD_FLAG_INDEX].startswith("-Wp,-MD,")
+ or cc_list[Target.C_FLAG_INDEX] != "-c"
+ or cc_list[Target.O_FLAG_INDEX] != "-o"):
+ raise StopError("unexpected or missing arguments for: " + obj +
" cc_line: " + cc_line)
+
+ # Instead of blindly normalizing the source and object arguments,
+ # they are only normalized if that allows the expected invariants
+ # to be verified, otherwise they are left undisturbed. Note that
+ # os.path.normpath() does not turn relative paths into absolute
+ # paths, it just removes up-down walks (e.g. a/b/../c -> a/c).
+
+ def verify_file(file: str, index: int, kind: str, cc_list: List[str],
+ target_file: str) -> None:
+ file_in_cc_list = cc_list[index]
+ if not file.endswith(file_in_cc_list):
+ file_normalized = os.path.normpath(file_in_cc_list)
+ if not file.endswith(file_normalized):
+ raise StopError(f"unexpected {kind} argument for: "
+ f"{target_file} value was: "
+ f"{file_in_cc_list}")
+ cc_list[index] = file_normalized
+
+ verify_file(obj, Target.OBJ_INDEX, "object", cc_list, obj)
+ verify_file(src, Target.SRC_INDEX, "source", cc_list, obj)
+
self._cc_list = cc_list