]>
Commit | Line | Data |
---|---|---|
879a9dc5 DL |
1 | # |
2 | # helper class to grab variables from FRR's Makefile | |
3 | # | |
4 | ||
5 | import os | |
6 | import subprocess | |
59994395 | 7 | import re |
879a9dc5 | 8 | |
701a0192 | 9 | |
59994395 | 10 | class MakeVarsBase(object): |
701a0192 | 11 | """ |
59994395 | 12 | common code between MakeVars and MakeReVars |
701a0192 | 13 | """ |
14 | ||
879a9dc5 DL |
15 | def __init__(self): |
16 | self._data = dict() | |
17 | ||
59994395 DL |
18 | def __getitem__(self, k): |
19 | if k not in self._data: | |
20 | self.getvars([k]) | |
21 | return self._data[k] | |
22 | ||
701a0192 | 23 | def get(self, k, defval=None): |
59994395 DL |
24 | if k not in self._data: |
25 | self.getvars([k]) | |
26 | return self._data.get(k) or defval | |
27 | ||
701a0192 | 28 | |
59994395 | 29 | class MakeVars(MakeVarsBase): |
701a0192 | 30 | """ |
59994395 DL |
31 | makevars['FOO_CFLAGS'] gets you "FOO_CFLAGS" from Makefile |
32 | ||
33 | This variant works by invoking make as a subprocess, i.e. Makefile must | |
34 | be valid and working. (This is sometimes a problem if depfiles have not | |
35 | been generated.) | |
701a0192 | 36 | """ |
37 | ||
879a9dc5 | 38 | def getvars(self, varlist): |
701a0192 | 39 | """ |
879a9dc5 | 40 | get a batch list of variables from make. faster than individual calls. |
701a0192 | 41 | """ |
879a9dc5 DL |
42 | rdfd, wrfd = os.pipe() |
43 | ||
701a0192 | 44 | shvars = ["shvar-%s" % s for s in varlist] |
45 | make = subprocess.Popen( | |
46 | ["make", "-s", "VARFD=%d" % wrfd] + shvars, pass_fds=[wrfd] | |
47 | ) | |
879a9dc5 | 48 | os.close(wrfd) |
701a0192 | 49 | data = b"" |
879a9dc5 | 50 | |
701a0192 | 51 | rdf = os.fdopen(rdfd, "rb") |
879a9dc5 DL |
52 | while True: |
53 | rdata = rdf.read() | |
54 | if len(rdata) == 0: | |
55 | break | |
56 | data += rdata | |
57 | ||
58 | del rdf | |
59 | make.wait() | |
60 | ||
701a0192 | 61 | data = data.decode("US-ASCII").strip().split("\n") |
879a9dc5 | 62 | for row in data: |
701a0192 | 63 | k, v = row.split("=", 1) |
879a9dc5 DL |
64 | v = v[1:-1] |
65 | self._data[k] = v | |
66 | ||
701a0192 | 67 | |
59994395 | 68 | class MakeReVars(MakeVarsBase): |
701a0192 | 69 | """ |
59994395 | 70 | makevars['FOO_CFLAGS'] gets you "FOO_CFLAGS" from Makefile |
879a9dc5 | 71 | |
59994395 DL |
72 | This variant works by regexing through Makefile. This means the Makefile |
73 | does not need to be fully working, but on the other hand it doesn't support | |
74 | fancy complicated make expressions. | |
701a0192 | 75 | """ |
76 | ||
77 | var_re = re.compile( | |
78 | r"^([^=#\n\s]+)[ \t]*=[ \t]*([^#\n]*)(?:#.*)?$", flags=re.MULTILINE | |
79 | ) | |
80 | repl_re = re.compile(r"\$(?:([A-Za-z])|\(([^\)]+)\))") | |
59994395 DL |
81 | |
82 | def __init__(self, maketext): | |
c4006e05 | 83 | super(MakeReVars, self).__init__() |
701a0192 | 84 | self._vars = dict(self.var_re.findall(maketext.replace("\\\n", ""))) |
59994395 DL |
85 | |
86 | def replacevar(self, match): | |
87 | varname = match.group(1) or match.group(2) | |
701a0192 | 88 | return self._vars.get(varname, "") |
59994395 DL |
89 | |
90 | def getvars(self, varlist): | |
91 | for varname in varlist: | |
92 | if varname not in self._vars: | |
93 | continue | |
94 | ||
95 | val, prevval = self._vars[varname], None | |
96 | while val != prevval: | |
97 | prevval = val | |
98 | val = self.repl_re.sub(self.replacevar, val) | |
99 | ||
100 | self._data[varname] = val |