1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
#!/usr/bin/python3
# SPDX-License-Identifier: ISC
#
# 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",
r"^#\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(r"^([^:\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")
|