meta2deps.py revision 249033
1246149Ssjg#!/usr/bin/env python
2246149Ssjg
3246149Ssjg"""
4246149SsjgThis script parses each "meta" file and extracts the
5246149Ssjginformation needed to deduce build and src dependencies.
6246149Ssjg
7246149SsjgIt works much the same as the original shell script, but is
8246149Ssjg*much* more efficient.
9246149Ssjg
10246149SsjgThe parsing work is handled by the class MetaFile.
11246149SsjgWe only pay attention to a subset of the information in the
12246149Ssjg"meta" files.  Specifically:
13246149Ssjg
14246149Ssjg'CWD'	to initialize our notion.
15246149Ssjg
16246149Ssjg'C'	to track chdir(2) on a per process basis
17246149Ssjg
18246149Ssjg'R'	files read are what we really care about.
19246149Ssjg	directories read, provide a clue to resolving
20246149Ssjg	subsequent relative paths.  That is if we cannot find
21246149Ssjg	them relative to 'cwd', we check relative to the last
22246149Ssjg	dir read.
23246149Ssjg
24246149Ssjg'W'	files opened for write or read-write,
25246149Ssjg	for filemon V3 and earlier.
26246149Ssjg
27246149Ssjg'E'	files executed.
28246149Ssjg
29246149Ssjg'L'	files linked
30246149Ssjg
31246149Ssjg'V'	the filemon version, this record is used as a clue
32246149Ssjg	that we have reached the interesting bit.
33246149Ssjg
34246149Ssjg"""
35246149Ssjg
36246149Ssjg"""
37246149SsjgRCSid:
38249033Ssjg	$Id: meta2deps.py,v 1.12 2013/03/31 22:31:59 sjg Exp $
39246149Ssjg
40249033Ssjg	Copyright (c) 2011-2013, Juniper Networks, Inc.
41249033Ssjg	All rights reserved.
42246149Ssjg
43246149Ssjg	Redistribution and use in source and binary forms, with or without
44246149Ssjg	modification, are permitted provided that the following conditions
45246149Ssjg	are met:
46246149Ssjg	1. Redistributions of source code must retain the above copyright
47246149Ssjg	   notice, this list of conditions and the following disclaimer.
48246149Ssjg	2. Redistributions in binary form must reproduce the above copyright
49246149Ssjg	   notice, this list of conditions and the following disclaimer in the
50246149Ssjg	   documentation and/or other materials provided with the distribution.
51246149Ssjg
52246149Ssjg	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
53246149Ssjg	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
54246149Ssjg	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
55246149Ssjg	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
56246149Ssjg	OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
57246149Ssjg	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
58246149Ssjg	LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
59246149Ssjg	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
60246149Ssjg	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
61246149Ssjg	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
62246149Ssjg	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
63246149Ssjg
64246149Ssjg"""
65246149Ssjg
66246149Ssjgimport os, re, sys
67246149Ssjg
68246149Ssjgdef getv(dict, key, d=None):
69246149Ssjg    """Lookup key in dict and return value or the supplied default."""
70246149Ssjg    if key in dict:
71246149Ssjg        return dict[key]
72246149Ssjg    return d
73246149Ssjg
74246149Ssjgdef resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
75246149Ssjg    """
76246149Ssjg    Return an absolute path, resolving via cwd or last_dir if needed.
77246149Ssjg    """
78246149Ssjg    if path.endswith('/.'):
79246149Ssjg        path = path[0:-2]
80246149Ssjg    if path[0] == '/':
81246149Ssjg        return path
82246149Ssjg    if path == '.':
83246149Ssjg        return cwd
84246149Ssjg    if path.startswith('./'):
85246149Ssjg        return cwd + path[1:]
86246149Ssjg    if last_dir == cwd:
87246149Ssjg        last_dir = None
88246149Ssjg    for d in [last_dir, cwd]:
89246149Ssjg        if not d:
90246149Ssjg            continue
91246149Ssjg        p = '/'.join([d,path])
92246149Ssjg        if debug > 2:
93246149Ssjg            print >> debug_out, "looking for:", p,
94246149Ssjg        if not os.path.exists(p):
95246149Ssjg            if debug > 2:
96246149Ssjg                print >> debug_out, "nope"
97246149Ssjg            p = None
98246149Ssjg            continue
99246149Ssjg        if debug > 2:
100246149Ssjg            print >> debug_out, "found:", p
101246149Ssjg        return p
102246149Ssjg    return None
103246149Ssjg
104246149Ssjgdef abspath(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
105246149Ssjg    """
106246149Ssjg    Return an absolute path, resolving via cwd or last_dir if needed.
107246149Ssjg    this gets called a lot, so we try to avoid calling realpath
108246149Ssjg    until we know we have something.
109246149Ssjg    """
110246149Ssjg    path = resolve(path, cwd, last_dir, debug, debug_out)
111246149Ssjg    if path and (path.find('./') > 0 or
112246149Ssjg                 path.endswith('/..') or
113246149Ssjg                 os.path.islink(path)):
114246149Ssjg        return os.path.realpath(path)
115246149Ssjg    return path
116246149Ssjg
117246149Ssjgdef sort_unique(list, cmp=None, key=None, reverse=False):
118246149Ssjg    list.sort(cmp, key, reverse)
119246149Ssjg    nl = []
120246149Ssjg    le = None
121246149Ssjg    for e in list:
122246149Ssjg        if e == le:
123246149Ssjg            continue
124246149Ssjg        nl.append(e)
125246149Ssjg    return nl
126246149Ssjg
127246149Ssjgclass MetaFile:
128246149Ssjg    """class to parse meta files generated by bmake."""
129246149Ssjg
130246149Ssjg    conf = None
131246149Ssjg    dirdep_re = None
132246149Ssjg    host_target = None
133246149Ssjg    srctops = []
134246149Ssjg    objroots = []
135246149Ssjg
136246149Ssjg    seen = {}
137246149Ssjg    obj_deps = []
138246149Ssjg    src_deps = []
139246149Ssjg    file_deps = []
140246149Ssjg
141246149Ssjg    def __init__(self, name, conf={}):
142246149Ssjg        """if name is set we will parse it now.
143246149Ssjg        conf can have the follwing keys:
144246149Ssjg
145246149Ssjg        SRCTOPS	list of tops of the src tree(s).
146246149Ssjg
147246149Ssjg        CURDIR	the src directory 'bmake' was run from.
148246149Ssjg
149246149Ssjg        RELDIR	the relative path from SRCTOP to CURDIR
150246149Ssjg
151246149Ssjg        MACHINE	the machine we built for.
152246149Ssjg        	set to 'none' if we are not cross-building.
153249033Ssjg		More specifically if machine cannot be deduced from objdirs.
154246149Ssjg
155246149Ssjg        HOST_TARGET
156246149Ssjg		when we build for the psuedo machine 'host'
157246149Ssjg		the object tree uses HOST_TARGET rather than MACHINE.
158246149Ssjg
159246149Ssjg        OBJROOTS a list of the common prefix for all obj dirs it might
160246149Ssjg		end in '/' or '-'.
161246149Ssjg
162246149Ssjg        DPDEPS	names an optional file to which per file dependencies
163246149Ssjg		will be appended.
164246149Ssjg		For example if 'some/path/foo.h' is read from SRCTOP
165246149Ssjg		then 'DPDEPS_some/path/foo.h +=' "RELDIR" is output.
166246149Ssjg		This can allow 'bmake' to learn all the dirs within
167246149Ssjg 		the tree that depend on 'foo.h'
168246149Ssjg
169246149Ssjg        debug	desired debug level
170246149Ssjg
171246149Ssjg        debug_out open file to send debug output to (sys.stderr)
172246149Ssjg
173246149Ssjg        """
174246149Ssjg
175246149Ssjg        self.name = name
176246149Ssjg        self.debug = getv(conf, 'debug', 0)
177246149Ssjg        self.debug_out = getv(conf, 'debug_out', sys.stderr)
178246149Ssjg
179249033Ssjg        self.machine = getv(conf, 'MACHINE', '')
180249033Ssjg        self.curdir = getv(conf, 'CURDIR')
181249033Ssjg        self.reldir = getv(conf, 'RELDIR')
182249033Ssjg        self.dpdeps = getv(conf, 'DPDEPS')
183249033Ssjg
184246149Ssjg        if not self.conf:
185246149Ssjg            # some of the steps below we want to do only once
186246149Ssjg            self.conf = conf
187246149Ssjg            self.host_target = getv(conf, 'HOST_TARGET')
188246149Ssjg            for srctop in getv(conf, 'SRCTOPS', []):
189246149Ssjg                if srctop[-1] != '/':
190246149Ssjg                    srctop += '/'
191246149Ssjg                if not srctop in self.srctops:
192246149Ssjg                    self.srctops.append(srctop)
193246149Ssjg                _srctop = os.path.realpath(srctop)
194246149Ssjg                if _srctop[-1] != '/':
195246149Ssjg                    _srctop += '/'
196246149Ssjg                if not _srctop in self.srctops:
197246149Ssjg                    self.srctops.append(_srctop)
198246149Ssjg
199249033Ssjg            trim_list = ['/' + self.machine + '/',
200249033Ssjg                         '/' + self.machine,
201249033Ssjg                         self.machine + '/',
202249033Ssjg                         self.machine]
203249033Ssjg
204249033Ssjg            if self.machine == 'host':
205249033Ssjg                trim_list += ['/' + self.host_target + '/',
206249033Ssjg                              '/' + self.host_target,
207249033Ssjg                              self.host_target + '/',
208249033Ssjg                              self.host_target]
209249033Ssjg
210246149Ssjg            for objroot in getv(conf, 'OBJROOTS', []):
211249033Ssjg                for e in trim_list:
212249033Ssjg                    if objroot.endswith(e):
213249033Ssjg                        # this is not what we want - fix it
214249033Ssjg                        objroot = objroot[0:-len(e)]
215249033Ssjg                        if e.endswith('/'):
216249033Ssjg                            objroot += '/'
217246149Ssjg                if not objroot in self.objroots:
218246149Ssjg                    self.objroots.append(objroot)
219246149Ssjg                    _objroot = os.path.realpath(objroot)
220246149Ssjg                    if objroot[-1] == '/':
221246149Ssjg                        _objroot += '/'
222246149Ssjg                    if not _objroot in self.objroots:
223246149Ssjg                        self.objroots.append(_objroot)
224246149Ssjg
225249033Ssjg            # we want the longest match
226249033Ssjg            self.srctops.sort(reverse=True)
227249033Ssjg            self.objroots.sort(reverse=True)
228249033Ssjg
229246149Ssjg            if self.debug:
230246149Ssjg                print >> self.debug_out, "host_target=", self.host_target
231246149Ssjg                print >> self.debug_out, "srctops=", self.srctops
232246149Ssjg                print >> self.debug_out, "objroots=", self.objroots
233246149Ssjg
234246149Ssjg            self.dirdep_re = re.compile(r'([^/]+)/(.+)')
235246149Ssjg
236246149Ssjg        if self.dpdeps and not self.reldir:
237246149Ssjg            if self.debug:
238246149Ssjg                print >> self.debug_out, "need reldir:",
239246149Ssjg            if self.curdir:
240246149Ssjg                srctop = self.find_top(self.curdir, self.srctops)
241246149Ssjg                if srctop:
242246149Ssjg                    self.reldir = self.curdir.replace(srctop,'')
243246149Ssjg                    if self.debug:
244246149Ssjg                        print >> self.debug_out, self.reldir
245246149Ssjg            if not self.reldir:
246246149Ssjg                self.dpdeps = None      # we cannot do it?
247246149Ssjg
248249033Ssjg        self.cwd = os.getcwd()          # make sure this is initialized
249249033Ssjg
250246149Ssjg        if name:
251246149Ssjg            self.parse()
252246149Ssjg
253246149Ssjg    def reset(self):
254246149Ssjg        """reset state if we are being passed meta files from multiple directories."""
255246149Ssjg        self.seen = {}
256246149Ssjg        self.obj_deps = []
257246149Ssjg        self.src_deps = []
258246149Ssjg        self.file_deps = []
259246149Ssjg
260246149Ssjg    def dirdeps(self, sep='\n'):
261246149Ssjg        """return DIRDEPS"""
262246149Ssjg        return sep.strip() + sep.join(self.obj_deps)
263246149Ssjg
264246149Ssjg    def src_dirdeps(self, sep='\n'):
265246149Ssjg        """return SRC_DIRDEPS"""
266246149Ssjg        return sep.strip() + sep.join(self.src_deps)
267246149Ssjg
268246149Ssjg    def file_depends(self, out=None):
269246149Ssjg        """Append DPDEPS_${file} += ${RELDIR}
270246149Ssjg        for each file we saw, to the output file."""
271246149Ssjg        if not self.reldir:
272246149Ssjg            return None
273246149Ssjg        for f in sort_unique(self.file_deps):
274246149Ssjg            print >> out, 'DPDEPS_%s += %s' % (f, self.reldir)
275246149Ssjg
276246149Ssjg    def seenit(self, dir):
277246149Ssjg        """rememer that we have seen dir."""
278246149Ssjg        self.seen[dir] = 1
279246149Ssjg
280246149Ssjg    def add(self, list, data, clue=''):
281246149Ssjg        """add data to list if it isn't already there."""
282246149Ssjg        if data not in list:
283246149Ssjg            list.append(data)
284246149Ssjg            if self.debug:
285246149Ssjg                print >> self.debug_out, "%s: %sAdd: %s" % (self.name, clue, data)
286246149Ssjg
287246149Ssjg    def find_top(self, path, list):
288246149Ssjg        """the logical tree may be split accross multiple trees"""
289246149Ssjg        for top in list:
290246149Ssjg            if path.startswith(top):
291246149Ssjg                if self.debug > 2:
292246149Ssjg                    print >> self.debug_out, "found in", top
293246149Ssjg                return top
294246149Ssjg        return None
295246149Ssjg
296246149Ssjg    def find_obj(self, objroot, dir, path, input):
297246149Ssjg        """return path within objroot, taking care of .dirdep files"""
298246149Ssjg        ddep = None
299246149Ssjg        for ddepf in [path + '.dirdep', dir + '/.dirdep']:
300246149Ssjg            if not ddep and os.path.exists(ddepf):
301246149Ssjg                ddep = open(ddepf, 'rb').readline().strip('# \n')
302246149Ssjg                if self.debug > 1:
303246149Ssjg                    print >> self.debug_out, "found %s: %s\n" % (ddepf, ddep)
304246149Ssjg                if ddep.endswith(self.machine):
305246149Ssjg                    ddep = ddep[0:-(1+len(self.machine))]
306246149Ssjg
307246149Ssjg        if not ddep:
308246149Ssjg            # no .dirdeps, so remember that we've seen the raw input
309246149Ssjg            self.seenit(input)
310246149Ssjg            self.seenit(dir)
311246149Ssjg            if self.machine == 'none':
312246149Ssjg                if dir.startswith(objroot):
313246149Ssjg                    return dir.replace(objroot,'')
314246149Ssjg                return None
315246149Ssjg            m = self.dirdep_re.match(dir.replace(objroot,''))
316246149Ssjg            if m:
317246149Ssjg                ddep = m.group(2)
318246149Ssjg                dmachine = m.group(1)
319246149Ssjg                if dmachine != self.machine:
320246149Ssjg                    if not (self.machine == 'host' and
321246149Ssjg                            dmachine == self.host_target):
322246149Ssjg                        if self.debug > 2:
323246149Ssjg                            print >> self.debug_out, "adding .%s to %s" % (dmachine, ddep)
324246149Ssjg                        ddep += '.' + dmachine
325246149Ssjg
326246149Ssjg        return ddep
327246149Ssjg
328246149Ssjg    def parse(self, name=None, file=None):
329246149Ssjg        """A meta file looks like:
330246149Ssjg
331246149Ssjg	# Meta data file "path"
332246149Ssjg	CMD "command-line"
333246149Ssjg	CWD "cwd"
334246149Ssjg	TARGET "target"
335246149Ssjg	-- command output --
336246149Ssjg	-- filemon acquired metadata --
337246149Ssjg	# buildmon version 3
338246149Ssjg	V 3
339246149Ssjg	C "pid" "cwd"
340246149Ssjg	E "pid" "path"
341246149Ssjg        F "pid" "child"
342246149Ssjg	R "pid" "path"
343246149Ssjg	W "pid" "path"
344246149Ssjg	X "pid" "status"
345246149Ssjg        D "pid" "path"
346246149Ssjg        L "pid" "src" "target"
347246149Ssjg        M "pid" "old" "new"
348246149Ssjg        S "pid" "path"
349246149Ssjg        # Bye bye
350246149Ssjg
351246149Ssjg        We go to some effort to avoid processing a dependency more than once.
352246149Ssjg        Of the above record types only C,E,F,L,R,V and W are of interest.
353246149Ssjg        """
354246149Ssjg
355246149Ssjg        version = 0                     # unknown
356246149Ssjg        if name:
357246149Ssjg            self.name = name;
358246149Ssjg        if file:
359246149Ssjg            f = file
360246149Ssjg            cwd = last_dir = self.cwd
361246149Ssjg        else:
362246149Ssjg            f = open(self.name, 'rb')
363246149Ssjg        skip = True
364246149Ssjg        pid_cwd = {}
365246149Ssjg        pid_last_dir = {}
366246149Ssjg        last_pid = 0
367246149Ssjg
368246149Ssjg        if self.curdir:
369246149Ssjg            self.seenit(self.curdir)    # we ignore this
370246149Ssjg
371246149Ssjg        interesting = 'CEFLRV'
372246149Ssjg        for line in f:
373246149Ssjg            # ignore anything we don't care about
374246149Ssjg            if not line[0] in interesting:
375246149Ssjg                continue
376246149Ssjg            if self.debug > 2:
377246149Ssjg                print >> self.debug_out, "input:", line,
378246149Ssjg            w = line.split()
379246149Ssjg
380246149Ssjg            if skip:
381246149Ssjg                if w[0] == 'V':
382246149Ssjg                    skip = False
383246149Ssjg                    version = int(w[1])
384246149Ssjg                    """
385246149Ssjg                    if version < 4:
386246149Ssjg                        # we cannot ignore 'W' records
387246149Ssjg                        # as they may be 'rw'
388246149Ssjg                        interesting += 'W'
389246149Ssjg                    """
390246149Ssjg                elif w[0] == 'CWD':
391246149Ssjg                    self.cwd = cwd = last_dir = w[1]
392246149Ssjg                    self.seenit(cwd)    # ignore this
393246149Ssjg                    if self.debug:
394246149Ssjg                        print >> self.debug_out, "%s: CWD=%s" % (self.name, cwd)
395246149Ssjg                continue
396246149Ssjg
397246149Ssjg            pid = int(w[1])
398246149Ssjg            if pid != last_pid:
399246149Ssjg                if last_pid:
400246149Ssjg                    pid_cwd[last_pid] = cwd
401246149Ssjg                    pid_last_dir[last_pid] = last_dir
402246149Ssjg                cwd = getv(pid_cwd, pid, self.cwd)
403246149Ssjg                last_dir = getv(pid_last_dir, pid, self.cwd)
404246149Ssjg                last_pid = pid
405246149Ssjg
406246149Ssjg            # process operations
407246149Ssjg            if w[0] == 'F':
408246149Ssjg                npid = int(w[2])
409246149Ssjg                pid_cwd[npid] = cwd
410246149Ssjg                pid_last_dir[npid] = cwd
411246149Ssjg                last_pid = npid
412246149Ssjg                continue
413246149Ssjg            elif w[0] == 'C':
414246149Ssjg                cwd = abspath(w[2], cwd, None, self.debug, self.debug_out)
415246149Ssjg                if cwd.endswith('/.'):
416246149Ssjg                    cwd = cwd[0:-2]
417246149Ssjg                last_dir = cwd
418246149Ssjg                if self.debug > 1:
419246149Ssjg                    print >> self.debug_out, "cwd=", cwd
420246149Ssjg                continue
421246149Ssjg
422246149Ssjg            if w[2] in self.seen:
423246149Ssjg                if self.debug > 2:
424246149Ssjg                    print >> self.debug_out, "seen:", w[2]
425246149Ssjg                continue
426246149Ssjg            # file operations
427246149Ssjg            if w[0] in 'ML':
428246149Ssjg                path = w[2].strip("'")
429246149Ssjg            else:
430246149Ssjg                path = w[2]
431246149Ssjg            # we are never interested in .dirdep files as dependencies
432246149Ssjg            if path.endswith('.dirdep'):
433246149Ssjg                continue
434246149Ssjg            # we don't want to resolve the last component if it is
435246149Ssjg            # a symlink
436246149Ssjg            path = resolve(path, cwd, last_dir, self.debug, self.debug_out)
437246149Ssjg            if not path:
438246149Ssjg                continue
439246149Ssjg            dir,base = os.path.split(path)
440246149Ssjg            if dir in self.seen:
441246149Ssjg                if self.debug > 2:
442246149Ssjg                    print >> self.debug_out, "seen:", dir
443246149Ssjg                continue
444246149Ssjg            # we can have a path in an objdir which is a link
445246149Ssjg            # to the src dir, we may need to add dependencies for each
446246149Ssjg            rdir = dir
447246149Ssjg            dir = abspath(dir, cwd, last_dir, self.debug, self.debug_out)
448246149Ssjg            if rdir == dir or rdir.find('./') > 0:
449246149Ssjg                rdir = None
450246149Ssjg            # now put path back together
451246149Ssjg            path = '/'.join([dir,base])
452246149Ssjg            if self.debug > 1:
453246149Ssjg                print >> self.debug_out, "raw=%s rdir=%s dir=%s path=%s" % (w[2], rdir, dir, path)
454246149Ssjg            if w[0] in 'SRWL':
455246149Ssjg                if w[0] == 'W' and path.endswith('.dirdep'):
456246149Ssjg                    continue
457246149Ssjg                if path in [last_dir, cwd, self.cwd, self.curdir]:
458246149Ssjg                    if self.debug > 1:
459246149Ssjg                        print >> self.debug_out, "skipping:", path
460246149Ssjg                    continue
461246149Ssjg                if os.path.isdir(path):
462246149Ssjg                    if w[0] in 'RW':
463246149Ssjg                        last_dir = path;
464246149Ssjg                    if self.debug > 1:
465246149Ssjg                        print >> self.debug_out, "ldir=", last_dir
466246149Ssjg                    continue
467246149Ssjg
468246149Ssjg            if w[0] in 'REWML':
469246149Ssjg                # finally, we get down to it
470246149Ssjg                if dir == self.cwd or dir == self.curdir:
471246149Ssjg                    continue
472246149Ssjg                srctop = self.find_top(path, self.srctops)
473246149Ssjg                if srctop:
474246149Ssjg                    if self.dpdeps:
475246149Ssjg                        self.add(self.file_deps, path.replace(srctop,''), 'file')
476246149Ssjg                    self.add(self.src_deps, dir.replace(srctop,''), 'src')
477246149Ssjg                    self.seenit(w[2])
478246149Ssjg                    self.seenit(dir)
479246149Ssjg                    if rdir and not rdir.startswith(srctop):
480246149Ssjg                        dir = rdir      # for below
481246149Ssjg                        rdir = None
482246149Ssjg                    else:
483246149Ssjg                        continue
484246149Ssjg
485246149Ssjg                objroot = None
486246149Ssjg                for dir in [dir,rdir]:
487246149Ssjg                    if not dir:
488246149Ssjg                        continue
489246149Ssjg                    objroot = self.find_top(dir, self.objroots)
490246149Ssjg                    if objroot:
491246149Ssjg                        break
492246149Ssjg                if objroot:
493246149Ssjg                    ddep = self.find_obj(objroot, dir, path, w[2])
494246149Ssjg                    if ddep:
495246149Ssjg                        self.add(self.obj_deps, ddep, 'obj')
496246149Ssjg                else:
497246149Ssjg                    # don't waste time looking again
498246149Ssjg                    self.seenit(w[2])
499246149Ssjg                    self.seenit(dir)
500246149Ssjg        if not file:
501246149Ssjg            f.close()
502246149Ssjg
503246149Ssjg
504246149Ssjgdef main(argv, klass=MetaFile, xopts='', xoptf=None):
505246149Ssjg    """Simple driver for class MetaFile.
506246149Ssjg
507246149Ssjg    Usage:
508246149Ssjg    	script [options] [key=value ...] "meta" ...
509246149Ssjg
510246149Ssjg    Options and key=value pairs contribute to the
511246149Ssjg    dictionary passed to MetaFile.
512246149Ssjg
513246149Ssjg    -S "SRCTOP"
514246149Ssjg		add "SRCTOP" to the "SRCTOPS" list.
515246149Ssjg
516246149Ssjg    -C "CURDIR"
517246149Ssjg
518246149Ssjg    -O "OBJROOT"
519246149Ssjg    		add "OBJROOT" to the "OBJROOTS" list.
520246149Ssjg
521246149Ssjg    -m "MACHINE"
522246149Ssjg
523246149Ssjg    -H "HOST_TARGET"
524246149Ssjg
525246149Ssjg    -D "DPDEPS"
526246149Ssjg
527246149Ssjg    -d	bumps debug level
528246149Ssjg
529246149Ssjg    """
530246149Ssjg    import getopt
531246149Ssjg
532246149Ssjg    # import Psyco if we can
533246149Ssjg    # it can speed things up quite a bit
534246149Ssjg    have_psyco = 0
535246149Ssjg    try:
536246149Ssjg        import psyco
537246149Ssjg        psyco.full()
538246149Ssjg        have_psyco = 1
539246149Ssjg    except:
540246149Ssjg        pass
541246149Ssjg
542246149Ssjg    conf = {
543246149Ssjg        'SRCTOPS': [],
544246149Ssjg        'OBJROOTS': [],
545246149Ssjg        }
546246149Ssjg
547246149Ssjg    try:
548246149Ssjg        machine = os.environ['MACHINE']
549246149Ssjg        if machine:
550246149Ssjg            conf['MACHINE'] = machine
551246149Ssjg        srctop = os.environ['SB_SRC']
552246149Ssjg        if srctop:
553246149Ssjg            conf['SRCTOPS'].append(srctop)
554246149Ssjg        objroot = os.environ['SB_OBJROOT']
555246149Ssjg        if objroot:
556246149Ssjg            conf['OBJROOTS'].append(objroot)
557246149Ssjg    except:
558246149Ssjg        pass
559246149Ssjg
560246149Ssjg    debug = 0
561246149Ssjg    output = True
562246149Ssjg
563246149Ssjg    opts, args = getopt.getopt(argv[1:], 'dS:C:O:R:m:D:H:q' + xopts)
564246149Ssjg    for o, a in opts:
565246149Ssjg        if o == '-d':
566246149Ssjg            debug += 1
567246149Ssjg        elif o == '-q':
568246149Ssjg            output = False
569246149Ssjg        elif o == '-H':
570246149Ssjg            conf['HOST_TARGET'] = a
571246149Ssjg        elif o == '-S':
572246149Ssjg            if a not in conf['SRCTOPS']:
573246149Ssjg                conf['SRCTOPS'].append(a)
574246149Ssjg        elif o == '-C':
575246149Ssjg            conf['CURDIR'] = a
576246149Ssjg        elif o == '-O':
577246149Ssjg            if a not in conf['OBJROOTS']:
578246149Ssjg                conf['OBJROOTS'].append(a)
579246149Ssjg        elif o == '-R':
580246149Ssjg            conf['RELDIR'] = a
581246149Ssjg        elif o == '-D':
582246149Ssjg            conf['DPDEPS'] = a
583246149Ssjg        elif o == '-m':
584246149Ssjg            conf['MACHINE'] = a
585246149Ssjg        elif xoptf:
586246149Ssjg            xoptf(o, a, conf)
587246149Ssjg
588246149Ssjg    conf['debug'] = debug
589246149Ssjg
590246149Ssjg    # get any var=val assignments
591246149Ssjg    eaten = []
592246149Ssjg    for a in args:
593246149Ssjg        if a.find('=') > 0:
594246149Ssjg            k,v = a.split('=')
595246149Ssjg            if k in ['SRCTOP','OBJROOT','SRCTOPS','OBJROOTS']:
596246149Ssjg                if k == 'SRCTOP':
597246149Ssjg                    k = 'SRCTOPS'
598246149Ssjg                elif k == 'OBJROOT':
599246149Ssjg                    k = 'OBJROOTS'
600246149Ssjg                if v not in conf[k]:
601246149Ssjg                    conf[k].append(v)
602246149Ssjg            else:
603246149Ssjg                conf[k] = v
604246149Ssjg            eaten.append(a)
605246149Ssjg            continue
606246149Ssjg        break
607246149Ssjg
608246149Ssjg    for a in eaten:
609246149Ssjg        args.remove(a)
610246149Ssjg
611246149Ssjg    debug_out = getv(conf, 'debug_out', sys.stderr)
612246149Ssjg
613246149Ssjg    if debug:
614246149Ssjg        print >> debug_out, "config:"
615246149Ssjg        print >> debug_out, "psyco=", have_psyco
616246149Ssjg        for k,v in conf.items():
617246149Ssjg            print >> debug_out, "%s=%s" % (k,v)
618246149Ssjg
619246149Ssjg    for a in args:
620246149Ssjg        m = klass(a, conf)
621246149Ssjg
622246149Ssjg    if output:
623246149Ssjg        print m.dirdeps()
624246149Ssjg
625246149Ssjg        print m.src_dirdeps('\nsrc:')
626246149Ssjg
627246149Ssjg        dpdeps = getv(conf, 'DPDEPS')
628246149Ssjg        if dpdeps:
629246149Ssjg            m.file_depends(open(dpdeps, 'wb'))
630246149Ssjg
631246149Ssjg    return m
632246149Ssjg
633246149Ssjgif __name__ == '__main__':
634246149Ssjg    try:
635246149Ssjg        main(sys.argv)
636246149Ssjg    except:
637246149Ssjg        # yes, this goes to stdout
638246149Ssjg        print "ERROR: ", sys.exc_info()[1]
639246149Ssjg        raise
640246149Ssjg
641