meta2deps.py revision 250837
1139804Simp#!/usr/bin/env python
21541Srgrimes
31541Srgrimes"""
41541SrgrimesThis script parses each "meta" file and extracts the
51541Srgrimesinformation needed to deduce build and src dependencies.
61541Srgrimes
71541SrgrimesIt works much the same as the original shell script, but is
81541Srgrimes*much* more efficient.
91541Srgrimes
101541SrgrimesThe parsing work is handled by the class MetaFile.
111541SrgrimesWe only pay attention to a subset of the information in the
121541Srgrimes"meta" files.  Specifically:
131541Srgrimes
141541Srgrimes'CWD'	to initialize our notion.
151541Srgrimes
161541Srgrimes'C'	to track chdir(2) on a per process basis
171541Srgrimes
181541Srgrimes'R'	files read are what we really care about.
191541Srgrimes	directories read, provide a clue to resolving
201541Srgrimes	subsequent relative paths.  That is if we cannot find
211541Srgrimes	them relative to 'cwd', we check relative to the last
221541Srgrimes	dir read.
231541Srgrimes
241541Srgrimes'W'	files opened for write or read-write,
251541Srgrimes	for filemon V3 and earlier.
261541Srgrimes
271541Srgrimes'E'	files executed.
281541Srgrimes
291541Srgrimes'L'	files linked
301541Srgrimes
311541Srgrimes'V'	the filemon version, this record is used as a clue
321541Srgrimes	that we have reached the interesting bit.
331541Srgrimes
341541Srgrimes"""
351541Srgrimes
361541Srgrimes"""
37116182SobrienRCSid:
38116182Sobrien	$Id: meta2deps.py,v 1.13 2013/05/11 05:16:26 sjg Exp $
39116182Sobrien
4013203Swollman	Copyright (c) 2011-2013, Juniper Networks, Inc.
41101127Srwatson	All rights reserved.
42144613Sjeff
4313203Swollman	Redistribution and use in source and binary forms, with or without
441541Srgrimes	modification, are permitted provided that the following conditions
452112Swollman	are met:
4669664Speter	1. Redistributions of source code must retain the above copyright
4776166Smarkm	   notice, this list of conditions and the following disclaimer.
48101127Srwatson	2. Redistributions in binary form must reproduce the above copyright
4989316Salfred	   notice, this list of conditions and the following disclaimer in the
501541Srgrimes	   documentation and/or other materials provided with the distribution.
511541Srgrimes
521541Srgrimes	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
531541Srgrimes	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
541541Srgrimes	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
55141471Sjhb	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
56144613Sjeff	OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
571541Srgrimes	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
581541Srgrimes	LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
591541Srgrimes	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
601541Srgrimes	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
61155334Srwatson	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
62155334Srwatson	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
6392751Sjeff
6432011Sbde"""
65155168Sjeff
66138345Sphkimport os, re, sys
67138345Sphk
681541Srgrimesdef getv(dict, key, d=None):
6969664Speter    """Lookup key in dict and return value or the supplied default."""
7069664Speter    if key in dict:
7192751Sjeff        return dict[key]
7269664Speter    return d
7369664Speter
7469664Speterdef resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
7569664Speter    """
7692654Sjeff    Return an absolute path, resolving via cwd or last_dir if needed.
7792654Sjeff    """
7869664Speter    if path.endswith('/.'):
7969664Speter        path = path[0:-2]
8069664Speter    if path[0] == '/':
8169664Speter        return path
82144613Sjeff    if path == '.':
83144613Sjeff        return cwd
84144613Sjeff    if path.startswith('./'):
85144613Sjeff        return cwd + path[1:]
86144613Sjeff    if last_dir == cwd:
87144613Sjeff        last_dir = None
88144613Sjeff    for d in [last_dir, cwd]:
89144613Sjeff        if not d:
9069664Speter            continue
91161010Srwatson        p = '/'.join([d,path])
921541Srgrimes        if debug > 2:
931541Srgrimes            print >> debug_out, "looking for:", p,
941541Srgrimes        if not os.path.exists(p):
951541Srgrimes            if debug > 2:
961541Srgrimes                print >> debug_out, "nope"
971541Srgrimes            p = None
981541Srgrimes            continue
991541Srgrimes        if debug > 2:
1001541Srgrimes            print >> debug_out, "found:", p
1011541Srgrimes        return p
1021541Srgrimes    return None
1031541Srgrimes
1041541Srgrimesdef abspath(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
1051541Srgrimes    """
1061541Srgrimes    Return an absolute path, resolving via cwd or last_dir if needed.
1071541Srgrimes    this gets called a lot, so we try to avoid calling realpath
1081541Srgrimes    until we know we have something.
1091541Srgrimes    """
1101541Srgrimes    path = resolve(path, cwd, last_dir, debug, debug_out)
111161011Srwatson    if path and (path.find('./') > 0 or
1121541Srgrimes                 path.endswith('/..') or
113161011Srwatson                 os.path.islink(path)):
114161011Srwatson        return os.path.realpath(path)
115161011Srwatson    return path
1161541Srgrimes
1171541Srgrimesdef sort_unique(list, cmp=None, key=None, reverse=False):
1181541Srgrimes    list.sort(cmp, key, reverse)
1191541Srgrimes    nl = []
12083366Sjulian    le = None
12183366Sjulian    for e in list:
122140714Sjeff        if e == le:
1231541Srgrimes            continue
124150164Scsjp        nl.append(e)
125150164Scsjp    return nl
12691419Sjhb
12783366Sjuliandef add_trims(x):
12842408Seivind    return ['/' + x + '/',
12942453Seivind            '/' + x,
13042408Seivind            x + '/',
13142453Seivind            x]
132144613Sjeff
133144613Sjeffclass MetaFile:
13483366Sjulian    """class to parse meta files generated by bmake."""
1351541Srgrimes
1361541Srgrimes    conf = None
1371541Srgrimes    dirdep_re = None
1381541Srgrimes    host_target = None
1391541Srgrimes    srctops = []
1401541Srgrimes    objroots = []
141111119Simp
1421541Srgrimes    seen = {}
1431541Srgrimes    obj_deps = []
14436735Sdfr    src_deps = []
1451541Srgrimes    file_deps = []
1461541Srgrimes
14736735Sdfr    def __init__(self, name, conf={}):
14820069Sbde        """if name is set we will parse it now.
149155334Srwatson        conf can have the follwing keys:
150155334Srwatson
151155334Srwatson        SRCTOPS	list of tops of the src tree(s).
152155334Srwatson
153155334Srwatson        CURDIR	the src directory 'bmake' was run from.
154155334Srwatson
15520069Sbde        RELDIR	the relative path from SRCTOP to CURDIR
15620069Sbde
15720069Sbde        MACHINE	the machine we built for.
15820069Sbde        	set to 'none' if we are not cross-building.
15920069Sbde		More specifically if machine cannot be deduced from objdirs.
16020069Sbde
1611541Srgrimes        TARGET_SPEC
16292751Sjeff        	Sometimes MACHINE isn't enough.
163100613Srwatson
164100613Srwatson        HOST_TARGET
165100613Srwatson		when we build for the psuedo machine 'host'
166100613Srwatson		the object tree uses HOST_TARGET rather than MACHINE.
1671541Srgrimes
1681541Srgrimes        OBJROOTS a list of the common prefix for all obj dirs it might
1691541Srgrimes		end in '/' or '-'.
1701541Srgrimes
1711541Srgrimes        DPDEPS	names an optional file to which per file dependencies
17297994Sjhb		will be appended.
17397994Sjhb		For example if 'some/path/foo.h' is read from SRCTOP
17497994Sjhb		then 'DPDEPS_some/path/foo.h +=' "RELDIR" is output.
17597994Sjhb		This can allow 'bmake' to learn all the dirs within
17697994Sjhb 		the tree that depend on 'foo.h'
1771541Srgrimes
1781541Srgrimes        debug	desired debug level
1791541Srgrimes
1801541Srgrimes        debug_out open file to send debug output to (sys.stderr)
1811541Srgrimes
18289306Salfred        """
18333360Sdyson
18451649Sphk        self.name = name
18533360Sdyson        self.debug = getv(conf, 'debug', 0)
1861541Srgrimes        self.debug_out = getv(conf, 'debug_out', sys.stderr)
187140714Sjeff
1881541Srgrimes        self.machine = getv(conf, 'MACHINE', '')
18989306Salfred        self.machine_arch = getv(conf, 'MACHINE_ARCH', '')
1901541Srgrimes        self.target_spec = getv(conf, 'TARGET_SPEC', '')
1911541Srgrimes        self.curdir = getv(conf, 'CURDIR')
1921541Srgrimes        self.reldir = getv(conf, 'RELDIR')
1931541Srgrimes        self.dpdeps = getv(conf, 'DPDEPS')
1941541Srgrimes
1951541Srgrimes        if not self.conf:
1961541Srgrimes            # some of the steps below we want to do only once
1971541Srgrimes            self.conf = conf
198140714Sjeff            self.host_target = getv(conf, 'HOST_TARGET')
1991541Srgrimes            for srctop in getv(conf, 'SRCTOPS', []):
2001541Srgrimes                if srctop[-1] != '/':
2011541Srgrimes                    srctop += '/'
2021541Srgrimes                if not srctop in self.srctops:
2031541Srgrimes                    self.srctops.append(srctop)
204140714Sjeff                _srctop = os.path.realpath(srctop)
2051541Srgrimes                if _srctop[-1] != '/':
2061541Srgrimes                    _srctop += '/'
207140714Sjeff                if not _srctop in self.srctops:
208140714Sjeff                    self.srctops.append(_srctop)
2091541Srgrimes
2103148Sphk            trim_list = add_trims(self.machine)
2113148Sphk            if self.machine == 'host':
21292751Sjeff                trim_list += add_trims(self.host_target)
213100613Srwatson            if self.target_spec:
214100613Srwatson                trim_list += add_trims(self.target_spec)
215100613Srwatson
216100613Srwatson            for objroot in getv(conf, 'OBJROOTS', []):
2171541Srgrimes                for e in trim_list:
2181541Srgrimes                    if objroot.endswith(e):
219140714Sjeff                        # this is not what we want - fix it
220140714Sjeff                        objroot = objroot[0:-len(e)]
2211541Srgrimes                        if e.endswith('/'):
2221541Srgrimes                            objroot += '/'
2231541Srgrimes                if not objroot in self.objroots:
2241541Srgrimes                    self.objroots.append(objroot)
225100613Srwatson                    _objroot = os.path.realpath(objroot)
22692751Sjeff                    if objroot[-1] == '/':
227100613Srwatson                        _objroot += '/'
228100613Srwatson                    if not _objroot in self.objroots:
229100613Srwatson                        self.objroots.append(_objroot)
230100613Srwatson
231100613Srwatson            # we want the longest match
2321541Srgrimes            self.srctops.sort(reverse=True)
23332286Sdyson            self.objroots.sort(reverse=True)
234140714Sjeff
235140714Sjeff            if self.debug:
236140714Sjeff                print >> self.debug_out, "host_target=", self.host_target
237140714Sjeff                print >> self.debug_out, "srctops=", self.srctops
2381541Srgrimes                print >> self.debug_out, "objroots=", self.objroots
2391541Srgrimes
2401541Srgrimes            self.dirdep_re = re.compile(r'([^/]+)/(.+)')
2411541Srgrimes
2421541Srgrimes        if self.dpdeps and not self.reldir:
2431541Srgrimes            if self.debug:
244101127Srwatson                print >> self.debug_out, "need reldir:",
245105479Srwatson            if self.curdir:
246105479Srwatson                srctop = self.find_top(self.curdir, self.srctops)
247105479Srwatson                if srctop:
248105479Srwatson                    self.reldir = self.curdir.replace(srctop,'')
249105479Srwatson                    if self.debug:
250105479Srwatson                        print >> self.debug_out, self.reldir
251101127Srwatson            if not self.reldir:
2521541Srgrimes                self.dpdeps = None      # we cannot do it?
253111119Simp
2541541Srgrimes        self.cwd = os.getcwd()          # make sure this is initialized
2551541Srgrimes
2561541Srgrimes        if name:
2571541Srgrimes            self.parse()
2581541Srgrimes
2591541Srgrimes    def reset(self):
2601541Srgrimes        """reset state if we are being passed meta files from multiple directories."""
2611541Srgrimes        self.seen = {}
2621541Srgrimes        self.obj_deps = []
26383366Sjulian        self.src_deps = []
2641541Srgrimes        self.file_deps = []
2653148Sphk
2663148Sphk    def dirdeps(self, sep='\n'):
2671541Srgrimes        """return DIRDEPS"""
26892751Sjeff        return sep.strip() + sep.join(self.obj_deps)
2691541Srgrimes
2701541Srgrimes    def src_dirdeps(self, sep='\n'):
2711541Srgrimes        """return SRC_DIRDEPS"""
27278692Sdillon        return sep.strip() + sep.join(self.src_deps)
27378692Sdillon
27492751Sjeff    def file_depends(self, out=None):
27578692Sdillon        """Append DPDEPS_${file} += ${RELDIR}
27678692Sdillon        for each file we saw, to the output file."""
27778692Sdillon        if not self.reldir:
2781541Srgrimes            return None
2791541Srgrimes        for f in sort_unique(self.file_deps):
28092751Sjeff            print >> out, 'DPDEPS_%s += %s' % (f, self.reldir)
2811541Srgrimes
2821541Srgrimes    def seenit(self, dir):
2831541Srgrimes        """rememer that we have seen dir."""
2841541Srgrimes        self.seen[dir] = 1
2851541Srgrimes
28692751Sjeff    def add(self, list, data, clue=''):
2871541Srgrimes        """add data to list if it isn't already there."""
2881541Srgrimes        if data not in list:
2891541Srgrimes            list.append(data)
2901541Srgrimes            if self.debug:
2911541Srgrimes                print >> self.debug_out, "%s: %sAdd: %s" % (self.name, clue, data)
2921541Srgrimes
2931541Srgrimes    def find_top(self, path, list):
29492751Sjeff        """the logical tree may be split accross multiple trees"""
295100613Srwatson        for top in list:
296100613Srwatson            if path.startswith(top):
297100613Srwatson                if self.debug > 2:
298100613Srwatson                    print >> self.debug_out, "found in", top
299144833Sjeff                return top
300144833Sjeff        return None
3011541Srgrimes
302140714Sjeff    def find_obj(self, objroot, dir, path, input):
3031541Srgrimes        """return path within objroot, taking care of .dirdep files"""
3041541Srgrimes        ddep = None
3051541Srgrimes        for ddepf in [path + '.dirdep', dir + '/.dirdep']:
306162288Smohans            if not ddep and os.path.exists(ddepf):
307162288Smohans                ddep = open(ddepf, 'rb').readline().strip('# \n')
308162288Smohans                if self.debug > 1:
309162288Smohans                    print >> self.debug_out, "found %s: %s\n" % (ddepf, ddep)
310162288Smohans                if ddep.endswith(self.machine):
311162288Smohans                    ddep = ddep[0:-(1+len(self.machine))]
312162288Smohans                elif self.target_spec and ddep.endswith(self.target_spec):
313162288Smohans                    ddep = ddep[0:-(1+len(self.target_spec))]
314162288Smohans
315162288Smohans        if not ddep:
3161541Srgrimes            # no .dirdeps, so remember that we've seen the raw input
3171541Srgrimes            self.seenit(input)
3181541Srgrimes            self.seenit(dir)
3191541Srgrimes            if self.machine == 'none':
3201541Srgrimes                if dir.startswith(objroot):
3211541Srgrimes                    return dir.replace(objroot,'')
3221541Srgrimes                return None
3231541Srgrimes            m = self.dirdep_re.match(dir.replace(objroot,''))
3241541Srgrimes            if m:
3251541Srgrimes                ddep = m.group(2)
3261541Srgrimes                dmachine = m.group(1)
3271541Srgrimes                if dmachine != self.machine:
3281541Srgrimes                    if not (self.machine == 'host' and
3291541Srgrimes                            dmachine == self.host_target):
3301541Srgrimes                        if self.debug > 2:
3311541Srgrimes                            print >> self.debug_out, "adding .%s to %s" % (dmachine, ddep)
3321541Srgrimes                        ddep += '.' + dmachine
3331541Srgrimes
3341541Srgrimes        return ddep
3351541Srgrimes
3361541Srgrimes    def parse(self, name=None, file=None):
3378876Srgrimes        """A meta file looks like:
3381541Srgrimes
3391541Srgrimes	# Meta data file "path"
3401541Srgrimes	CMD "command-line"
3411541Srgrimes	CWD "cwd"
3421541Srgrimes	TARGET "target"
3431541Srgrimes	-- command output --
3441541Srgrimes	-- filemon acquired metadata --
3451541Srgrimes	# buildmon version 3
3461541Srgrimes	V 3
3471541Srgrimes	C "pid" "cwd"
3481541Srgrimes	E "pid" "path"
3491541Srgrimes        F "pid" "child"
3501541Srgrimes	R "pid" "path"
3511541Srgrimes	W "pid" "path"
3521541Srgrimes	X "pid" "status"
3531541Srgrimes        D "pid" "path"
3541541Srgrimes        L "pid" "src" "target"
355161011Srwatson        M "pid" "old" "new"
3561541Srgrimes        S "pid" "path"
357161011Srwatson        # Bye bye
358161011Srwatson
3591541Srgrimes        We go to some effort to avoid processing a dependency more than once.
3601541Srgrimes        Of the above record types only C,E,F,L,R,V and W are of interest.
3611541Srgrimes        """
3621541Srgrimes
3631541Srgrimes        version = 0                     # unknown
3649804Sbde        if name:
3651541Srgrimes            self.name = name;
36665805Sbp        if file:
3671541Srgrimes            f = file
36883366Sjulian            cwd = last_dir = self.cwd
369158094Sjeff        else:
370158094Sjeff            f = open(self.name, 'rb')
371140714Sjeff        skip = True
372162288Smohans        pid_cwd = {}
373162288Smohans        pid_last_dir = {}
3741541Srgrimes        last_pid = 0
3751541Srgrimes
3761541Srgrimes        if self.curdir:
377158094Sjeff            self.seenit(self.curdir)    # we ignore this
378158094Sjeff
379140714Sjeff        interesting = 'CEFLRV'
3801541Srgrimes        for line in f:
381144229Sjeff            # ignore anything we don't care about
382144229Sjeff            if not line[0] in interesting:
3831541Srgrimes                continue
3841541Srgrimes            if self.debug > 2:
38522874Sbde                print >> self.debug_out, "input:", line,
38622874Sbde            w = line.split()
3871541Srgrimes
3881541Srgrimes            if skip:
389144286Sjeff                if w[0] == 'V':
3901541Srgrimes                    skip = False
391144286Sjeff                    version = int(w[1])
392144286Sjeff                    """
393144286Sjeff                    if version < 4:
394144286Sjeff                        # we cannot ignore 'W' records
395144613Sjeff                        # as they may be 'rw'
396144613Sjeff                        interesting += 'W'
397144613Sjeff                    """
398144613Sjeff                elif w[0] == 'CWD':
3991541Srgrimes                    self.cwd = cwd = last_dir = w[1]
4001541Srgrimes                    self.seenit(cwd)    # ignore this
401162288Smohans                    if self.debug:
4021541Srgrimes                        print >> self.debug_out, "%s: CWD=%s" % (self.name, cwd)
4031541Srgrimes                continue
4041541Srgrimes
4051541Srgrimes            pid = int(w[1])
4061541Srgrimes            if pid != last_pid:
4071541Srgrimes                if last_pid:
4081541Srgrimes                    pid_cwd[last_pid] = cwd
4091541Srgrimes                    pid_last_dir[last_pid] = last_dir
4101541Srgrimes                cwd = getv(pid_cwd, pid, self.cwd)
4111541Srgrimes                last_dir = getv(pid_last_dir, pid, self.cwd)
4121541Srgrimes                last_pid = pid
4131541Srgrimes
41451906Sphk            # process operations
4151541Srgrimes            if w[0] == 'F':
4161541Srgrimes                npid = int(w[2])
4171541Srgrimes                pid_cwd[npid] = cwd
4181541Srgrimes                pid_last_dir[npid] = cwd
4191541Srgrimes                last_pid = npid
4201541Srgrimes                continue
4211541Srgrimes            elif w[0] == 'C':
4221541Srgrimes                cwd = abspath(w[2], cwd, None, self.debug, self.debug_out)
4231541Srgrimes                if cwd.endswith('/.'):
4241541Srgrimes                    cwd = cwd[0:-2]
4251541Srgrimes                last_dir = cwd
4261541Srgrimes                if self.debug > 1:
4271541Srgrimes                    print >> self.debug_out, "cwd=", cwd
4289804Sbde                continue
4299804Sbde
4309804Sbde            if w[2] in self.seen:
4319804Sbde                if self.debug > 2:
4329804Sbde                    print >> self.debug_out, "seen:", w[2]
4339804Sbde                continue
4349804Sbde            # file operations
4359804Sbde            if w[0] in 'ML':
4369804Sbde                path = w[2].strip("'")
4379804Sbde            else:
4389804Sbde                path = w[2]
4399804Sbde            # we are never interested in .dirdep files as dependencies
4409804Sbde            if path.endswith('.dirdep'):
4419804Sbde                continue
4429804Sbde            # we don't want to resolve the last component if it is
4439804Sbde            # a symlink
4449804Sbde            path = resolve(path, cwd, last_dir, self.debug, self.debug_out)
4459804Sbde            if not path:
4469804Sbde                continue
4471541Srgrimes            dir,base = os.path.split(path)
4481541Srgrimes            if dir in self.seen:
4491541Srgrimes                if self.debug > 2:
4501541Srgrimes                    print >> self.debug_out, "seen:", dir
4511541Srgrimes                continue
4521541Srgrimes            # we can have a path in an objdir which is a link
4531541Srgrimes            # to the src dir, we may need to add dependencies for each
4541541Srgrimes            rdir = dir
4551541Srgrimes            dir = abspath(dir, cwd, last_dir, self.debug, self.debug_out)
4561541Srgrimes            if rdir == dir or rdir.find('./') > 0:
4571541Srgrimes                rdir = None
4581541Srgrimes            # now put path back together
4591541Srgrimes            path = '/'.join([dir,base])
4601541Srgrimes            if self.debug > 1:
4611541Srgrimes                print >> self.debug_out, "raw=%s rdir=%s dir=%s path=%s" % (w[2], rdir, dir, path)
4621541Srgrimes            if w[0] in 'SRWL':
4631541Srgrimes                if w[0] == 'W' and path.endswith('.dirdep'):
4641541Srgrimes                    continue
4651541Srgrimes                if path in [last_dir, cwd, self.cwd, self.curdir]:
4661541Srgrimes                    if self.debug > 1:
46722521Sdyson                        print >> self.debug_out, "skipping:", path
46822521Sdyson                    continue
46922521Sdyson                if os.path.isdir(path):
47022521Sdyson                    if w[0] in 'RW':
4711541Srgrimes                        last_dir = path;
4721541Srgrimes                    if self.debug > 1:
4731541Srgrimes                        print >> self.debug_out, "ldir=", last_dir
4741541Srgrimes                    continue
4751541Srgrimes
4761541Srgrimes            if w[0] in 'REWML':
4771541Srgrimes                # finally, we get down to it
4781541Srgrimes                if dir == self.cwd or dir == self.curdir:
4791541Srgrimes                    continue
480155334Srwatson                srctop = self.find_top(path, self.srctops)
481155334Srwatson                if srctop:
482155334Srwatson                    if self.dpdeps:
483155334Srwatson                        self.add(self.file_deps, path.replace(srctop,''), 'file')
484155334Srwatson                    self.add(self.src_deps, dir.replace(srctop,''), 'src')
485155334Srwatson                    self.seenit(w[2])
4861541Srgrimes                    self.seenit(dir)
48783366Sjulian                    if rdir and not rdir.startswith(srctop):
48854655Seivind                        dir = rdir      # for below
4891541Srgrimes                        rdir = None
4901541Srgrimes                    else:
491140714Sjeff                        continue
4921541Srgrimes
4931541Srgrimes                objroot = None
4941541Srgrimes                for dir in [dir,rdir]:
495154649Struckman                    if not dir:
496154649Struckman                        continue
497154649Struckman                    objroot = self.find_top(dir, self.objroots)
498154649Struckman                    if objroot:
4991541Srgrimes                        break
5001541Srgrimes                if objroot:
501154649Struckman                    ddep = self.find_obj(objroot, dir, path, w[2])
5021541Srgrimes                    if ddep:
5031541Srgrimes                        self.add(self.obj_deps, ddep, 'obj')
50496755Strhodes                else:
505154649Struckman                    # don't waste time looking again
50651649Sphk                    self.seenit(w[2])
5071541Srgrimes                    self.seenit(dir)
5081541Srgrimes        if not file:
509154649Struckman            f.close()
510154649Struckman
511154690Struckman
512154649Struckmandef main(argv, klass=MetaFile, xopts='', xoptf=None):
513154649Struckman    """Simple driver for class MetaFile.
5141541Srgrimes
51551649Sphk    Usage:
51651649Sphk    	script [options] [key=value ...] "meta" ...
51751649Sphk
5181541Srgrimes    Options and key=value pairs contribute to the
5191541Srgrimes    dictionary passed to MetaFile.
520158142Skris
5211541Srgrimes    -S "SRCTOP"
5221541Srgrimes		add "SRCTOP" to the "SRCTOPS" list.
5231541Srgrimes
524101308Sjeff    -C "CURDIR"
5251541Srgrimes
5261541Srgrimes    -O "OBJROOT"
527155385Sjeff    		add "OBJROOT" to the "OBJROOTS" list.
52869405Salfred
52969405Salfred    -m "MACHINE"
53069405Salfred
5311541Srgrimes    -a "MACHINE_ARCH"
532144833Sjeff
533158094Sjeff    -H "HOST_TARGET"
534158094Sjeff
535144833Sjeff    -D "DPDEPS"
5361541Srgrimes
537140714Sjeff    -d	bumps debug level
538162288Smohans
5391541Srgrimes    """
5401541Srgrimes    import getopt
5411541Srgrimes
5421541Srgrimes    # import Psyco if we can
5431541Srgrimes    # it can speed things up quite a bit
5441541Srgrimes    have_psyco = 0
5451541Srgrimes    try:
546101127Srwatson        import psyco
547105479Srwatson        psyco.full()
548105479Srwatson        have_psyco = 1
549105479Srwatson    except:
550105479Srwatson        pass
551105479Srwatson
552101127Srwatson    conf = {
5531541Srgrimes        'SRCTOPS': [],
55422521Sdyson        'OBJROOTS': [],
55524624Sdfr        }
556158094Sjeff
557144286Sjeff    try:
558144286Sjeff        machine = os.environ['MACHINE']
559144286Sjeff        if machine:
560144286Sjeff            conf['MACHINE'] = machine
561144286Sjeff        machine_arch = os.environ['MACHINE_ARCH']
562144286Sjeff        if machine_arch:
563144286Sjeff            conf['MACHINE_ARCH'] = machine_arch
564144286Sjeff        srctop = os.environ['SB_SRC']
565144286Sjeff        if srctop:
566144286Sjeff            conf['SRCTOPS'].append(srctop)
567144286Sjeff        objroot = os.environ['SB_OBJROOT']
568144286Sjeff        if objroot:
569144286Sjeff            conf['OBJROOTS'].append(objroot)
570144286Sjeff    except:
571138345Sphk        pass
572138345Sphk
573138345Sphk    debug = 0
574162288Smohans    output = True
575162288Smohans
57643301Sdillon    opts, args = getopt.getopt(argv[1:], 'a:dS:C:O:R:m:D:H:qT:' + xopts)
577162288Smohans    for o, a in opts:
57842408Seivind        if o == '-a':
5791541Srgrimes            conf['MACHINE_ARCH'] = a
5801541Srgrimes        elif o == '-d':
5811541Srgrimes            debug += 1
5821541Srgrimes        elif o == '-q':
583101308Sjeff            output = False
5841541Srgrimes        elif o == '-H':
5851541Srgrimes            conf['HOST_TARGET'] = a
586144833Sjeff        elif o == '-S':
587158094Sjeff            if a not in conf['SRCTOPS']:
588158094Sjeff                conf['SRCTOPS'].append(a)
589144833Sjeff        elif o == '-C':
590144203Sjeff            conf['CURDIR'] = a
591140714Sjeff        elif o == '-O':
592162288Smohans            if a not in conf['OBJROOTS']:
5931541Srgrimes                conf['OBJROOTS'].append(a)
5941541Srgrimes        elif o == '-R':
5951541Srgrimes            conf['RELDIR'] = a
5961541Srgrimes        elif o == '-D':
5971541Srgrimes            conf['DPDEPS'] = a
5981541Srgrimes        elif o == '-m':
5991541Srgrimes            conf['MACHINE'] = a
6001541Srgrimes        elif o == '-T':
6011541Srgrimes            conf['TARGET_SPEC'] = a
60211644Sdg        elif xoptf:
6031541Srgrimes            xoptf(o, a, conf)
6041541Srgrimes
6051541Srgrimes    conf['debug'] = debug
6069804Sbde
6079804Sbde    # get any var=val assignments
6089804Sbde    eaten = []
6099804Sbde    for a in args:
6109804Sbde        if a.find('=') > 0:
611144203Sjeff            k,v = a.split('=')
612144203Sjeff            if k in ['SRCTOP','OBJROOT','SRCTOPS','OBJROOTS']:
6131541Srgrimes                if k == 'SRCTOP':
614144203Sjeff                    k = 'SRCTOPS'
615144203Sjeff                elif k == 'OBJROOT':
616144203Sjeff                    k = 'OBJROOTS'
617144203Sjeff                if v not in conf[k]:
618144203Sjeff                    conf[k].append(v)
619144203Sjeff            else:
6201541Srgrimes                conf[k] = v
6211541Srgrimes            eaten.append(a)
622161010Srwatson            continue
6231541Srgrimes        break
6241541Srgrimes
6251541Srgrimes    for a in eaten:
6261541Srgrimes        args.remove(a)
6271541Srgrimes
628140714Sjeff    debug_out = getv(conf, 'debug_out', sys.stderr)
629162288Smohans
630162288Smohans    if debug:
6311541Srgrimes        print >> debug_out, "config:"
6321541Srgrimes        print >> debug_out, "psyco=", have_psyco
6331541Srgrimes        for k,v in conf.items():
634144203Sjeff            print >> debug_out, "%s=%s" % (k,v)
6351541Srgrimes
6361541Srgrimes    for a in args:
6371541Srgrimes        m = klass(a, conf)
6381541Srgrimes
6391541Srgrimes    if output:
6401541Srgrimes        print m.dirdeps()
6411541Srgrimes
6421541Srgrimes        print m.src_dirdeps('\nsrc:')
6431541Srgrimes
6441541Srgrimes        dpdeps = getv(conf, 'DPDEPS')
6451541Srgrimes        if dpdeps:
646158094Sjeff            m.file_depends(open(dpdeps, 'wb'))
6471541Srgrimes
6481541Srgrimes    return m
6491541Srgrimes
65096755Strhodesif __name__ == '__main__':
6511541Srgrimes    try:
6521541Srgrimes        main(sys.argv)
6531541Srgrimes    except:
65483366Sjulian        # yes, this goes to stdout
6551541Srgrimes        print "ERROR: ", sys.exc_info()[1]
656144833Sjeff        raise
657158094Sjeff
658155168Sjeff