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:
38253883Ssjg	$Id: meta2deps.py,v 1.15 2013/07/29 20:41:23 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]
80253883Ssjg    if len(path) > 0 and 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    """
110253883Ssjg    rpath = resolve(path, cwd, last_dir, debug, debug_out)
111253883Ssjg    if rpath:
112253883Ssjg        path = rpath
113253883Ssjg    if (path.find('./') > 0 or
114253883Ssjg        path.endswith('/..') or
115253883Ssjg        os.path.islink(path)):
116246149Ssjg        return os.path.realpath(path)
117246149Ssjg    return path
118246149Ssjg
119246149Ssjgdef sort_unique(list, cmp=None, key=None, reverse=False):
120246149Ssjg    list.sort(cmp, key, reverse)
121246149Ssjg    nl = []
122246149Ssjg    le = None
123246149Ssjg    for e in list:
124246149Ssjg        if e == le:
125246149Ssjg            continue
126246149Ssjg        nl.append(e)
127246149Ssjg    return nl
128246149Ssjg
129250837Ssjgdef add_trims(x):
130250837Ssjg    return ['/' + x + '/',
131250837Ssjg            '/' + x,
132250837Ssjg            x + '/',
133250837Ssjg            x]
134250837Ssjg
135246149Ssjgclass MetaFile:
136246149Ssjg    """class to parse meta files generated by bmake."""
137246149Ssjg
138246149Ssjg    conf = None
139246149Ssjg    dirdep_re = None
140246149Ssjg    host_target = None
141246149Ssjg    srctops = []
142246149Ssjg    objroots = []
143246149Ssjg
144246149Ssjg    seen = {}
145246149Ssjg    obj_deps = []
146246149Ssjg    src_deps = []
147246149Ssjg    file_deps = []
148246149Ssjg
149246149Ssjg    def __init__(self, name, conf={}):
150246149Ssjg        """if name is set we will parse it now.
151246149Ssjg        conf can have the follwing keys:
152246149Ssjg
153246149Ssjg        SRCTOPS	list of tops of the src tree(s).
154246149Ssjg
155246149Ssjg        CURDIR	the src directory 'bmake' was run from.
156246149Ssjg
157246149Ssjg        RELDIR	the relative path from SRCTOP to CURDIR
158246149Ssjg
159246149Ssjg        MACHINE	the machine we built for.
160246149Ssjg        	set to 'none' if we are not cross-building.
161249033Ssjg		More specifically if machine cannot be deduced from objdirs.
162246149Ssjg
163250837Ssjg        TARGET_SPEC
164250837Ssjg        	Sometimes MACHINE isn't enough.
165250837Ssjg
166246149Ssjg        HOST_TARGET
167246149Ssjg		when we build for the psuedo machine 'host'
168246149Ssjg		the object tree uses HOST_TARGET rather than MACHINE.
169246149Ssjg
170246149Ssjg        OBJROOTS a list of the common prefix for all obj dirs it might
171246149Ssjg		end in '/' or '-'.
172246149Ssjg
173246149Ssjg        DPDEPS	names an optional file to which per file dependencies
174246149Ssjg		will be appended.
175246149Ssjg		For example if 'some/path/foo.h' is read from SRCTOP
176246149Ssjg		then 'DPDEPS_some/path/foo.h +=' "RELDIR" is output.
177246149Ssjg		This can allow 'bmake' to learn all the dirs within
178246149Ssjg 		the tree that depend on 'foo.h'
179246149Ssjg
180246149Ssjg        debug	desired debug level
181246149Ssjg
182246149Ssjg        debug_out open file to send debug output to (sys.stderr)
183246149Ssjg
184246149Ssjg        """
185246149Ssjg
186246149Ssjg        self.name = name
187246149Ssjg        self.debug = getv(conf, 'debug', 0)
188246149Ssjg        self.debug_out = getv(conf, 'debug_out', sys.stderr)
189246149Ssjg
190249033Ssjg        self.machine = getv(conf, 'MACHINE', '')
191250837Ssjg        self.machine_arch = getv(conf, 'MACHINE_ARCH', '')
192250837Ssjg        self.target_spec = getv(conf, 'TARGET_SPEC', '')
193249033Ssjg        self.curdir = getv(conf, 'CURDIR')
194249033Ssjg        self.reldir = getv(conf, 'RELDIR')
195249033Ssjg        self.dpdeps = getv(conf, 'DPDEPS')
196253883Ssjg        self.line = 0
197249033Ssjg
198246149Ssjg        if not self.conf:
199246149Ssjg            # some of the steps below we want to do only once
200246149Ssjg            self.conf = conf
201246149Ssjg            self.host_target = getv(conf, 'HOST_TARGET')
202246149Ssjg            for srctop in getv(conf, 'SRCTOPS', []):
203246149Ssjg                if srctop[-1] != '/':
204246149Ssjg                    srctop += '/'
205246149Ssjg                if not srctop in self.srctops:
206246149Ssjg                    self.srctops.append(srctop)
207246149Ssjg                _srctop = os.path.realpath(srctop)
208246149Ssjg                if _srctop[-1] != '/':
209246149Ssjg                    _srctop += '/'
210246149Ssjg                if not _srctop in self.srctops:
211246149Ssjg                    self.srctops.append(_srctop)
212246149Ssjg
213250837Ssjg            trim_list = add_trims(self.machine)
214249033Ssjg            if self.machine == 'host':
215250837Ssjg                trim_list += add_trims(self.host_target)
216250837Ssjg            if self.target_spec:
217250837Ssjg                trim_list += add_trims(self.target_spec)
218249033Ssjg
219246149Ssjg            for objroot in getv(conf, 'OBJROOTS', []):
220249033Ssjg                for e in trim_list:
221249033Ssjg                    if objroot.endswith(e):
222249033Ssjg                        # this is not what we want - fix it
223249033Ssjg                        objroot = objroot[0:-len(e)]
224249033Ssjg                        if e.endswith('/'):
225249033Ssjg                            objroot += '/'
226246149Ssjg                if not objroot in self.objroots:
227246149Ssjg                    self.objroots.append(objroot)
228246149Ssjg                    _objroot = os.path.realpath(objroot)
229246149Ssjg                    if objroot[-1] == '/':
230246149Ssjg                        _objroot += '/'
231246149Ssjg                    if not _objroot in self.objroots:
232246149Ssjg                        self.objroots.append(_objroot)
233246149Ssjg
234249033Ssjg            # we want the longest match
235249033Ssjg            self.srctops.sort(reverse=True)
236249033Ssjg            self.objroots.sort(reverse=True)
237249033Ssjg
238246149Ssjg            if self.debug:
239246149Ssjg                print >> self.debug_out, "host_target=", self.host_target
240246149Ssjg                print >> self.debug_out, "srctops=", self.srctops
241246149Ssjg                print >> self.debug_out, "objroots=", self.objroots
242246149Ssjg
243246149Ssjg            self.dirdep_re = re.compile(r'([^/]+)/(.+)')
244246149Ssjg
245246149Ssjg        if self.dpdeps and not self.reldir:
246246149Ssjg            if self.debug:
247246149Ssjg                print >> self.debug_out, "need reldir:",
248246149Ssjg            if self.curdir:
249246149Ssjg                srctop = self.find_top(self.curdir, self.srctops)
250246149Ssjg                if srctop:
251246149Ssjg                    self.reldir = self.curdir.replace(srctop,'')
252246149Ssjg                    if self.debug:
253246149Ssjg                        print >> self.debug_out, self.reldir
254246149Ssjg            if not self.reldir:
255246149Ssjg                self.dpdeps = None      # we cannot do it?
256246149Ssjg
257249033Ssjg        self.cwd = os.getcwd()          # make sure this is initialized
258249033Ssjg
259246149Ssjg        if name:
260253883Ssjg            self.try_parse()
261246149Ssjg
262246149Ssjg    def reset(self):
263246149Ssjg        """reset state if we are being passed meta files from multiple directories."""
264246149Ssjg        self.seen = {}
265246149Ssjg        self.obj_deps = []
266246149Ssjg        self.src_deps = []
267246149Ssjg        self.file_deps = []
268246149Ssjg
269246149Ssjg    def dirdeps(self, sep='\n'):
270246149Ssjg        """return DIRDEPS"""
271246149Ssjg        return sep.strip() + sep.join(self.obj_deps)
272246149Ssjg
273246149Ssjg    def src_dirdeps(self, sep='\n'):
274246149Ssjg        """return SRC_DIRDEPS"""
275246149Ssjg        return sep.strip() + sep.join(self.src_deps)
276246149Ssjg
277246149Ssjg    def file_depends(self, out=None):
278246149Ssjg        """Append DPDEPS_${file} += ${RELDIR}
279246149Ssjg        for each file we saw, to the output file."""
280246149Ssjg        if not self.reldir:
281246149Ssjg            return None
282246149Ssjg        for f in sort_unique(self.file_deps):
283246149Ssjg            print >> out, 'DPDEPS_%s += %s' % (f, self.reldir)
284246149Ssjg
285246149Ssjg    def seenit(self, dir):
286246149Ssjg        """rememer that we have seen dir."""
287246149Ssjg        self.seen[dir] = 1
288246149Ssjg
289246149Ssjg    def add(self, list, data, clue=''):
290246149Ssjg        """add data to list if it isn't already there."""
291246149Ssjg        if data not in list:
292246149Ssjg            list.append(data)
293246149Ssjg            if self.debug:
294246149Ssjg                print >> self.debug_out, "%s: %sAdd: %s" % (self.name, clue, data)
295246149Ssjg
296246149Ssjg    def find_top(self, path, list):
297246149Ssjg        """the logical tree may be split accross multiple trees"""
298246149Ssjg        for top in list:
299246149Ssjg            if path.startswith(top):
300246149Ssjg                if self.debug > 2:
301246149Ssjg                    print >> self.debug_out, "found in", top
302246149Ssjg                return top
303246149Ssjg        return None
304246149Ssjg
305246149Ssjg    def find_obj(self, objroot, dir, path, input):
306246149Ssjg        """return path within objroot, taking care of .dirdep files"""
307246149Ssjg        ddep = None
308246149Ssjg        for ddepf in [path + '.dirdep', dir + '/.dirdep']:
309246149Ssjg            if not ddep and os.path.exists(ddepf):
310246149Ssjg                ddep = open(ddepf, 'rb').readline().strip('# \n')
311246149Ssjg                if self.debug > 1:
312246149Ssjg                    print >> self.debug_out, "found %s: %s\n" % (ddepf, ddep)
313246149Ssjg                if ddep.endswith(self.machine):
314246149Ssjg                    ddep = ddep[0:-(1+len(self.machine))]
315250837Ssjg                elif self.target_spec and ddep.endswith(self.target_spec):
316250837Ssjg                    ddep = ddep[0:-(1+len(self.target_spec))]
317246149Ssjg
318246149Ssjg        if not ddep:
319246149Ssjg            # no .dirdeps, so remember that we've seen the raw input
320246149Ssjg            self.seenit(input)
321246149Ssjg            self.seenit(dir)
322246149Ssjg            if self.machine == 'none':
323246149Ssjg                if dir.startswith(objroot):
324246149Ssjg                    return dir.replace(objroot,'')
325246149Ssjg                return None
326246149Ssjg            m = self.dirdep_re.match(dir.replace(objroot,''))
327246149Ssjg            if m:
328246149Ssjg                ddep = m.group(2)
329246149Ssjg                dmachine = m.group(1)
330246149Ssjg                if dmachine != self.machine:
331246149Ssjg                    if not (self.machine == 'host' and
332246149Ssjg                            dmachine == self.host_target):
333246149Ssjg                        if self.debug > 2:
334246149Ssjg                            print >> self.debug_out, "adding .%s to %s" % (dmachine, ddep)
335246149Ssjg                        ddep += '.' + dmachine
336246149Ssjg
337246149Ssjg        return ddep
338246149Ssjg
339253883Ssjg    def try_parse(self, name=None, file=None):
340253883Ssjg        """give file and line number causing exception"""
341253883Ssjg        try:
342253883Ssjg            self.parse(name, file)
343253883Ssjg        except:
344253883Ssjg            # give a useful clue
345253883Ssjg            print >> sys.stderr, '{}:{}: '.format(self.name, self.line),
346253883Ssjg            raise
347253883Ssjg
348246149Ssjg    def parse(self, name=None, file=None):
349246149Ssjg        """A meta file looks like:
350246149Ssjg
351246149Ssjg	# Meta data file "path"
352246149Ssjg	CMD "command-line"
353246149Ssjg	CWD "cwd"
354246149Ssjg	TARGET "target"
355246149Ssjg	-- command output --
356246149Ssjg	-- filemon acquired metadata --
357246149Ssjg	# buildmon version 3
358246149Ssjg	V 3
359246149Ssjg	C "pid" "cwd"
360246149Ssjg	E "pid" "path"
361246149Ssjg        F "pid" "child"
362246149Ssjg	R "pid" "path"
363246149Ssjg	W "pid" "path"
364246149Ssjg	X "pid" "status"
365246149Ssjg        D "pid" "path"
366246149Ssjg        L "pid" "src" "target"
367246149Ssjg        M "pid" "old" "new"
368246149Ssjg        S "pid" "path"
369246149Ssjg        # Bye bye
370246149Ssjg
371246149Ssjg        We go to some effort to avoid processing a dependency more than once.
372246149Ssjg        Of the above record types only C,E,F,L,R,V and W are of interest.
373246149Ssjg        """
374246149Ssjg
375246149Ssjg        version = 0                     # unknown
376246149Ssjg        if name:
377246149Ssjg            self.name = name;
378246149Ssjg        if file:
379246149Ssjg            f = file
380246149Ssjg            cwd = last_dir = self.cwd
381246149Ssjg        else:
382246149Ssjg            f = open(self.name, 'rb')
383246149Ssjg        skip = True
384246149Ssjg        pid_cwd = {}
385246149Ssjg        pid_last_dir = {}
386246149Ssjg        last_pid = 0
387246149Ssjg
388253883Ssjg        self.line = 0
389246149Ssjg        if self.curdir:
390246149Ssjg            self.seenit(self.curdir)    # we ignore this
391246149Ssjg
392246149Ssjg        interesting = 'CEFLRV'
393246149Ssjg        for line in f:
394253883Ssjg            self.line += 1
395246149Ssjg            # ignore anything we don't care about
396246149Ssjg            if not line[0] in interesting:
397246149Ssjg                continue
398246149Ssjg            if self.debug > 2:
399246149Ssjg                print >> self.debug_out, "input:", line,
400246149Ssjg            w = line.split()
401246149Ssjg
402246149Ssjg            if skip:
403246149Ssjg                if w[0] == 'V':
404246149Ssjg                    skip = False
405246149Ssjg                    version = int(w[1])
406246149Ssjg                    """
407246149Ssjg                    if version < 4:
408246149Ssjg                        # we cannot ignore 'W' records
409246149Ssjg                        # as they may be 'rw'
410246149Ssjg                        interesting += 'W'
411246149Ssjg                    """
412246149Ssjg                elif w[0] == 'CWD':
413246149Ssjg                    self.cwd = cwd = last_dir = w[1]
414246149Ssjg                    self.seenit(cwd)    # ignore this
415246149Ssjg                    if self.debug:
416246149Ssjg                        print >> self.debug_out, "%s: CWD=%s" % (self.name, cwd)
417246149Ssjg                continue
418246149Ssjg
419246149Ssjg            pid = int(w[1])
420246149Ssjg            if pid != last_pid:
421246149Ssjg                if last_pid:
422246149Ssjg                    pid_cwd[last_pid] = cwd
423246149Ssjg                    pid_last_dir[last_pid] = last_dir
424246149Ssjg                cwd = getv(pid_cwd, pid, self.cwd)
425246149Ssjg                last_dir = getv(pid_last_dir, pid, self.cwd)
426246149Ssjg                last_pid = pid
427246149Ssjg
428246149Ssjg            # process operations
429246149Ssjg            if w[0] == 'F':
430246149Ssjg                npid = int(w[2])
431246149Ssjg                pid_cwd[npid] = cwd
432246149Ssjg                pid_last_dir[npid] = cwd
433246149Ssjg                last_pid = npid
434246149Ssjg                continue
435246149Ssjg            elif w[0] == 'C':
436246149Ssjg                cwd = abspath(w[2], cwd, None, self.debug, self.debug_out)
437246149Ssjg                if cwd.endswith('/.'):
438246149Ssjg                    cwd = cwd[0:-2]
439246149Ssjg                last_dir = cwd
440246149Ssjg                if self.debug > 1:
441246149Ssjg                    print >> self.debug_out, "cwd=", cwd
442246149Ssjg                continue
443246149Ssjg
444246149Ssjg            if w[2] in self.seen:
445246149Ssjg                if self.debug > 2:
446246149Ssjg                    print >> self.debug_out, "seen:", w[2]
447246149Ssjg                continue
448246149Ssjg            # file operations
449246149Ssjg            if w[0] in 'ML':
450246149Ssjg                path = w[2].strip("'")
451246149Ssjg            else:
452246149Ssjg                path = w[2]
453246149Ssjg            # we are never interested in .dirdep files as dependencies
454246149Ssjg            if path.endswith('.dirdep'):
455246149Ssjg                continue
456246149Ssjg            # we don't want to resolve the last component if it is
457246149Ssjg            # a symlink
458246149Ssjg            path = resolve(path, cwd, last_dir, self.debug, self.debug_out)
459246149Ssjg            if not path:
460246149Ssjg                continue
461246149Ssjg            dir,base = os.path.split(path)
462246149Ssjg            if dir in self.seen:
463246149Ssjg                if self.debug > 2:
464246149Ssjg                    print >> self.debug_out, "seen:", dir
465246149Ssjg                continue
466246149Ssjg            # we can have a path in an objdir which is a link
467246149Ssjg            # to the src dir, we may need to add dependencies for each
468246149Ssjg            rdir = dir
469246149Ssjg            dir = abspath(dir, cwd, last_dir, self.debug, self.debug_out)
470246149Ssjg            if rdir == dir or rdir.find('./') > 0:
471246149Ssjg                rdir = None
472246149Ssjg            # now put path back together
473246149Ssjg            path = '/'.join([dir,base])
474246149Ssjg            if self.debug > 1:
475246149Ssjg                print >> self.debug_out, "raw=%s rdir=%s dir=%s path=%s" % (w[2], rdir, dir, path)
476246149Ssjg            if w[0] in 'SRWL':
477246149Ssjg                if w[0] == 'W' and path.endswith('.dirdep'):
478246149Ssjg                    continue
479246149Ssjg                if path in [last_dir, cwd, self.cwd, self.curdir]:
480246149Ssjg                    if self.debug > 1:
481246149Ssjg                        print >> self.debug_out, "skipping:", path
482246149Ssjg                    continue
483246149Ssjg                if os.path.isdir(path):
484246149Ssjg                    if w[0] in 'RW':
485246149Ssjg                        last_dir = path;
486246149Ssjg                    if self.debug > 1:
487246149Ssjg                        print >> self.debug_out, "ldir=", last_dir
488246149Ssjg                    continue
489246149Ssjg
490246149Ssjg            if w[0] in 'REWML':
491246149Ssjg                # finally, we get down to it
492246149Ssjg                if dir == self.cwd or dir == self.curdir:
493246149Ssjg                    continue
494246149Ssjg                srctop = self.find_top(path, self.srctops)
495246149Ssjg                if srctop:
496246149Ssjg                    if self.dpdeps:
497246149Ssjg                        self.add(self.file_deps, path.replace(srctop,''), 'file')
498246149Ssjg                    self.add(self.src_deps, dir.replace(srctop,''), 'src')
499246149Ssjg                    self.seenit(w[2])
500246149Ssjg                    self.seenit(dir)
501246149Ssjg                    if rdir and not rdir.startswith(srctop):
502246149Ssjg                        dir = rdir      # for below
503246149Ssjg                        rdir = None
504246149Ssjg                    else:
505246149Ssjg                        continue
506246149Ssjg
507246149Ssjg                objroot = None
508246149Ssjg                for dir in [dir,rdir]:
509246149Ssjg                    if not dir:
510246149Ssjg                        continue
511246149Ssjg                    objroot = self.find_top(dir, self.objroots)
512246149Ssjg                    if objroot:
513246149Ssjg                        break
514246149Ssjg                if objroot:
515246149Ssjg                    ddep = self.find_obj(objroot, dir, path, w[2])
516246149Ssjg                    if ddep:
517246149Ssjg                        self.add(self.obj_deps, ddep, 'obj')
518246149Ssjg                else:
519246149Ssjg                    # don't waste time looking again
520246149Ssjg                    self.seenit(w[2])
521246149Ssjg                    self.seenit(dir)
522246149Ssjg        if not file:
523246149Ssjg            f.close()
524246149Ssjg
525246149Ssjg
526246149Ssjgdef main(argv, klass=MetaFile, xopts='', xoptf=None):
527246149Ssjg    """Simple driver for class MetaFile.
528246149Ssjg
529246149Ssjg    Usage:
530246149Ssjg    	script [options] [key=value ...] "meta" ...
531246149Ssjg
532246149Ssjg    Options and key=value pairs contribute to the
533246149Ssjg    dictionary passed to MetaFile.
534246149Ssjg
535246149Ssjg    -S "SRCTOP"
536246149Ssjg		add "SRCTOP" to the "SRCTOPS" list.
537246149Ssjg
538246149Ssjg    -C "CURDIR"
539246149Ssjg
540246149Ssjg    -O "OBJROOT"
541246149Ssjg    		add "OBJROOT" to the "OBJROOTS" list.
542246149Ssjg
543246149Ssjg    -m "MACHINE"
544246149Ssjg
545250837Ssjg    -a "MACHINE_ARCH"
546250837Ssjg
547246149Ssjg    -H "HOST_TARGET"
548246149Ssjg
549246149Ssjg    -D "DPDEPS"
550246149Ssjg
551246149Ssjg    -d	bumps debug level
552246149Ssjg
553246149Ssjg    """
554246149Ssjg    import getopt
555246149Ssjg
556246149Ssjg    # import Psyco if we can
557246149Ssjg    # it can speed things up quite a bit
558246149Ssjg    have_psyco = 0
559246149Ssjg    try:
560246149Ssjg        import psyco
561246149Ssjg        psyco.full()
562246149Ssjg        have_psyco = 1
563246149Ssjg    except:
564246149Ssjg        pass
565246149Ssjg
566246149Ssjg    conf = {
567246149Ssjg        'SRCTOPS': [],
568246149Ssjg        'OBJROOTS': [],
569246149Ssjg        }
570246149Ssjg
571246149Ssjg    try:
572246149Ssjg        machine = os.environ['MACHINE']
573246149Ssjg        if machine:
574246149Ssjg            conf['MACHINE'] = machine
575250837Ssjg        machine_arch = os.environ['MACHINE_ARCH']
576250837Ssjg        if machine_arch:
577250837Ssjg            conf['MACHINE_ARCH'] = machine_arch
578246149Ssjg        srctop = os.environ['SB_SRC']
579246149Ssjg        if srctop:
580246149Ssjg            conf['SRCTOPS'].append(srctop)
581246149Ssjg        objroot = os.environ['SB_OBJROOT']
582246149Ssjg        if objroot:
583246149Ssjg            conf['OBJROOTS'].append(objroot)
584246149Ssjg    except:
585246149Ssjg        pass
586246149Ssjg
587246149Ssjg    debug = 0
588246149Ssjg    output = True
589246149Ssjg
590250837Ssjg    opts, args = getopt.getopt(argv[1:], 'a:dS:C:O:R:m:D:H:qT:' + xopts)
591246149Ssjg    for o, a in opts:
592250837Ssjg        if o == '-a':
593250837Ssjg            conf['MACHINE_ARCH'] = a
594250837Ssjg        elif o == '-d':
595246149Ssjg            debug += 1
596246149Ssjg        elif o == '-q':
597246149Ssjg            output = False
598246149Ssjg        elif o == '-H':
599246149Ssjg            conf['HOST_TARGET'] = a
600246149Ssjg        elif o == '-S':
601246149Ssjg            if a not in conf['SRCTOPS']:
602246149Ssjg                conf['SRCTOPS'].append(a)
603246149Ssjg        elif o == '-C':
604246149Ssjg            conf['CURDIR'] = a
605246149Ssjg        elif o == '-O':
606246149Ssjg            if a not in conf['OBJROOTS']:
607246149Ssjg                conf['OBJROOTS'].append(a)
608246149Ssjg        elif o == '-R':
609246149Ssjg            conf['RELDIR'] = a
610246149Ssjg        elif o == '-D':
611246149Ssjg            conf['DPDEPS'] = a
612246149Ssjg        elif o == '-m':
613246149Ssjg            conf['MACHINE'] = a
614250837Ssjg        elif o == '-T':
615250837Ssjg            conf['TARGET_SPEC'] = a
616246149Ssjg        elif xoptf:
617246149Ssjg            xoptf(o, a, conf)
618246149Ssjg
619246149Ssjg    conf['debug'] = debug
620246149Ssjg
621246149Ssjg    # get any var=val assignments
622246149Ssjg    eaten = []
623246149Ssjg    for a in args:
624246149Ssjg        if a.find('=') > 0:
625246149Ssjg            k,v = a.split('=')
626246149Ssjg            if k in ['SRCTOP','OBJROOT','SRCTOPS','OBJROOTS']:
627246149Ssjg                if k == 'SRCTOP':
628246149Ssjg                    k = 'SRCTOPS'
629246149Ssjg                elif k == 'OBJROOT':
630246149Ssjg                    k = 'OBJROOTS'
631246149Ssjg                if v not in conf[k]:
632246149Ssjg                    conf[k].append(v)
633246149Ssjg            else:
634246149Ssjg                conf[k] = v
635246149Ssjg            eaten.append(a)
636246149Ssjg            continue
637246149Ssjg        break
638246149Ssjg
639246149Ssjg    for a in eaten:
640246149Ssjg        args.remove(a)
641246149Ssjg
642246149Ssjg    debug_out = getv(conf, 'debug_out', sys.stderr)
643246149Ssjg
644246149Ssjg    if debug:
645246149Ssjg        print >> debug_out, "config:"
646246149Ssjg        print >> debug_out, "psyco=", have_psyco
647246149Ssjg        for k,v in conf.items():
648246149Ssjg            print >> debug_out, "%s=%s" % (k,v)
649246149Ssjg
650246149Ssjg    for a in args:
651253883Ssjg        if a.endswith('.meta'):
652253883Ssjg            m = klass(a, conf)
653253883Ssjg        elif a.startswith('@'):
654253883Ssjg            # there can actually multiple files per line
655253883Ssjg            for line in open(a[1:]):
656253883Ssjg                for f in line.strip().split():
657253883Ssjg                    m = klass(f, conf)
658246149Ssjg
659246149Ssjg    if output:
660246149Ssjg        print m.dirdeps()
661246149Ssjg
662246149Ssjg        print m.src_dirdeps('\nsrc:')
663246149Ssjg
664246149Ssjg        dpdeps = getv(conf, 'DPDEPS')
665246149Ssjg        if dpdeps:
666246149Ssjg            m.file_depends(open(dpdeps, 'wb'))
667246149Ssjg
668246149Ssjg    return m
669246149Ssjg
670246149Ssjgif __name__ == '__main__':
671246149Ssjg    try:
672246149Ssjg        main(sys.argv)
673246149Ssjg    except:
674246149Ssjg        # yes, this goes to stdout
675246149Ssjg        print "ERROR: ", sys.exc_info()[1]
676246149Ssjg        raise
677246149Ssjg
678