summaryrefslogtreecommitdiffstats
path: root/python/makefile.py
diff options
context:
space:
mode:
Diffstat (limited to 'python/makefile.py')
-rw-r--r--python/makefile.py217
1 files changed, 217 insertions, 0 deletions
diff --git a/python/makefile.py b/python/makefile.py
new file mode 100644
index 0000000..573871f
--- /dev/null
+++ b/python/makefile.py
@@ -0,0 +1,217 @@
+#!/usr/bin/python3
+#
+# FRR extended automake/Makefile functionality helper
+#
+# This script is executed on/after generating Makefile to add some pieces for
+# clippy.
+
+import sys
+import os
+import subprocess
+import re
+import argparse
+from string import Template
+from makevars import MakeReVars
+
+argp = argparse.ArgumentParser(description="FRR Makefile extensions")
+argp.add_argument(
+ "--dev-build",
+ action="store_const",
+ const=True,
+ help="run additional developer checks",
+)
+args = argp.parse_args()
+
+with open("Makefile", "r") as fd:
+ before = fd.read()
+
+mv = MakeReVars(before)
+
+clippy_scan = mv["clippy_scan"].strip().split()
+for clippy_file in clippy_scan:
+ assert clippy_file.endswith(".c")
+
+xref_targets = []
+for varname in [
+ "bin_PROGRAMS",
+ "sbin_PROGRAMS",
+ "lib_LTLIBRARIES",
+ "module_LTLIBRARIES",
+]:
+ xref_targets.extend(mv[varname].strip().split())
+
+# check for files using clippy but not listed in clippy_scan
+if args.dev_build:
+ basepath = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ if os.path.exists(os.path.join(basepath, ".git")):
+ clippy_ref = subprocess.check_output(
+ [
+ "git",
+ "-C",
+ basepath,
+ "grep",
+ "-l",
+ "-P",
+ "^#\s*include.*_clippy.c",
+ "--",
+ "**.c",
+ ]
+ ).decode("US-ASCII")
+
+ clippy_ref = set(clippy_ref.splitlines())
+ missing = clippy_ref - set(clippy_scan)
+
+ if len(missing) > 0:
+ sys.stderr.write(
+ 'error: files seem to be using clippy, but not listed in "clippy_scan" in subdir.am:\n\t%s\n'
+ % ("\n\t".join(sorted(missing)))
+ )
+ sys.exit(1)
+
+# this additional-dependency rule is stuck onto all compile targets that
+# compile a file which uses clippy-generated input, so it has a dependency to
+# make that first.
+clippydep = Template(
+ """
+${clippybase}.$$(OBJEXT): ${clippybase}_clippy.c
+${clippybase}.lo: ${clippybase}_clippy.c
+${clippybase}_clippy.c: $$(CLIPPY_DEPS)"""
+)
+
+# this one is used when one .c file is built multiple times with different
+# CFLAGS
+clippyauxdep = Template(
+ """# clippy{
+# auxiliary clippy target
+${target}: ${clippybase}_clippy.c
+# }clippy"""
+)
+
+lines = before.splitlines()
+autoderp = "#AUTODERP# "
+out_lines = []
+bcdeps = []
+make_rule_re = re.compile("^([^:\s]+):\s*([^:\s]+)\s*($|\n)")
+
+while lines:
+ line = lines.pop(0)
+ if line.startswith(autoderp):
+ line = line[len(autoderp) :]
+
+ # allow rerunning on already-clippified Makefile
+ if line == "# clippy{":
+ while lines:
+ line = lines.pop(0)
+ if line == "# }clippy":
+ break
+ continue
+
+ if line.startswith("#"):
+ out_lines.append(line)
+ continue
+
+ full_line = line
+ full_lines = lines[:]
+ while full_line.endswith("\\"):
+ full_line = full_line[:-1] + full_lines.pop(0)
+
+ m = make_rule_re.match(full_line)
+ if m is None:
+ out_lines.append(line)
+ continue
+
+ line, lines = full_line, full_lines
+
+ target, dep = m.group(1), m.group(2)
+
+ filename = os.path.basename(target)
+ if "-" in filename:
+ # dashes in output filename = building same .c with different CFLAGS
+ am_name, _ = filename.split("-", 1)
+ am_name = os.path.join(os.path.dirname(target), am_name)
+ am_name = am_name.replace("/", "_")
+ extraflags = " $(%s_CFLAGS)" % (am_name,)
+ else:
+ # this path isn't really triggered because automake is using a generic
+ # .c => .o rule unless CFLAGS are customized for a target
+ extraflags = ""
+
+ if target.endswith(".lo") or target.endswith(".o"):
+ if not dep.endswith(".h"):
+ # LLVM bitcode targets for analysis tools
+ bcdeps.append("%s.bc: %s" % (target, target))
+ bcdeps.append(
+ "\t$(AM_V_LLVM_BC)$(COMPILE)%s -emit-llvm -c -o $@ %s"
+ % (extraflags, dep)
+ )
+ if m.group(2) in clippy_scan:
+ # again - this is only hit for targets with custom CFLAGS, because
+ # automake uses a generic .c -> .o rule for standard CFLAGS
+ out_lines.append(
+ clippyauxdep.substitute(target=m.group(1), clippybase=m.group(2)[:-2])
+ )
+
+ out_lines.append(line)
+
+# now, cover all the .c files that don't have special build rules
+out_lines.append("# clippy{\n# main clippy targets")
+for clippy_file in clippy_scan:
+ out_lines.append(clippydep.substitute(clippybase=clippy_file[:-2]))
+
+# combine daemon .xref files into frr.xref
+out_lines.append("")
+xref_targets = [
+ target
+ for target in xref_targets
+ if target
+ not in [
+ "bgpd/rfp-example/rfptest/rfptest",
+ "pimd/mtracebis",
+ "tools/ssd",
+ "vtysh/vtysh",
+ ]
+]
+out_lines.append(
+ "xrefs = %s" % (" ".join(["%s.xref" % target for target in xref_targets]))
+)
+out_lines.append("frr.xref: $(xrefs)")
+out_lines.append("")
+
+# analog but slower way to get the same frr.xref
+# frr.xref: $(bin_PROGRAMS) $(sbin_PROGRAMS) $(lib_LTLIBRARIES) $(module_LTLIBRARIES)
+# $(AM_V_XRELFO) $(CLIPPY) $(top_srcdir)/python/xrelfo.py -o $@ $^
+
+# LLVM bitcode link targets creating a .bc file for whole daemon or lib
+out_lines.append("")
+out_lines.extend(bcdeps)
+out_lines.append("")
+bc_targets = []
+for varname in [
+ "bin_PROGRAMS",
+ "sbin_PROGRAMS",
+ "lib_LTLIBRARIES",
+ "module_LTLIBRARIES",
+ "noinst_LIBRARIES",
+]:
+ bc_targets.extend(mv[varname].strip().split())
+for target in bc_targets:
+ amtgt = target.replace("/", "_").replace(".", "_").replace("-", "_")
+ objs = mv[amtgt + "_OBJECTS"].strip().split()
+ objs = [obj + ".bc" for obj in objs]
+ deps = mv.get(amtgt + "_DEPENDENCIES", "").strip().split()
+ deps = [d + ".bc" for d in deps if d.endswith(".a")]
+ objs.extend(deps)
+ out_lines.append("%s.bc: %s" % (target, " ".join(objs)))
+ out_lines.append("\t$(AM_V_LLVM_LD)$(LLVM_LINK) -o $@ $^")
+ out_lines.append("")
+
+out_lines.append("# }clippy")
+out_lines.append("")
+
+after = "\n".join(out_lines)
+if after == before:
+ sys.exit(0)
+
+with open("Makefile.pyout", "w") as fd:
+ fd.write(after)
+os.rename("Makefile.pyout", "Makefile")