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