meta2deps.py revision 268437
1246149Ssjg#!/usr/bin/env python
2246149Ssjg
3261212Ssjgfrom __future__ import print_function
4261212Ssjg
5246149Ssjg"""
6246149SsjgThis script parses each "meta" file and extracts the
7246149Ssjginformation needed to deduce build and src dependencies.
8246149Ssjg
9246149SsjgIt works much the same as the original shell script, but is
10246149Ssjg*much* more efficient.
11246149Ssjg
12246149SsjgThe parsing work is handled by the class MetaFile.
13246149SsjgWe only pay attention to a subset of the information in the
14246149Ssjg"meta" files.  Specifically:
15246149Ssjg
16246149Ssjg'CWD'	to initialize our notion.
17246149Ssjg
18246149Ssjg'C'	to track chdir(2) on a per process basis
19246149Ssjg
20246149Ssjg'R'	files read are what we really care about.
21246149Ssjg	directories read, provide a clue to resolving
22246149Ssjg	subsequent relative paths.  That is if we cannot find
23246149Ssjg	them relative to 'cwd', we check relative to the last
24246149Ssjg	dir read.
25246149Ssjg
26246149Ssjg'W'	files opened for write or read-write,
27246149Ssjg	for filemon V3 and earlier.
28246149Ssjg
29246149Ssjg'E'	files executed.
30246149Ssjg
31246149Ssjg'L'	files linked
32246149Ssjg
33246149Ssjg'V'	the filemon version, this record is used as a clue
34246149Ssjg	that we have reached the interesting bit.
35246149Ssjg
36246149Ssjg"""
37246149Ssjg
38246149Ssjg"""
39246149SsjgRCSid:
40268437Ssjg	$Id: meta2deps.py,v 1.17 2014/04/05 22:56:54 sjg Exp $
41246149Ssjg
42249033Ssjg	Copyright (c) 2011-2013, Juniper Networks, Inc.
43249033Ssjg	All rights reserved.
44246149Ssjg
45246149Ssjg	Redistribution and use in source and binary forms, with or without
46246149Ssjg	modification, are permitted provided that the following conditions
47246149Ssjg	are met:
48246149Ssjg	1. Redistributions of source code must retain the above copyright
49246149Ssjg	   notice, this list of conditions and the following disclaimer.
50246149Ssjg	2. Redistributions in binary form must reproduce the above copyright
51246149Ssjg	   notice, this list of conditions and the following disclaimer in the
52246149Ssjg	   documentation and/or other materials provided with the distribution.
53246149Ssjg
54246149Ssjg	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
55246149Ssjg	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
56246149Ssjg	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
57246149Ssjg	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
58246149Ssjg	OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
59246149Ssjg	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
60246149Ssjg	LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
61246149Ssjg	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
62246149Ssjg	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
63246149Ssjg	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
64246149Ssjg	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
65246149Ssjg
66246149Ssjg"""
67246149Ssjg
68246149Ssjgimport os, re, sys
69246149Ssjg
70246149Ssjgdef getv(dict, key, d=None):
71246149Ssjg    """Lookup key in dict and return value or the supplied default."""
72246149Ssjg    if key in dict:
73246149Ssjg        return dict[key]
74246149Ssjg    return d
75246149Ssjg
76246149Ssjgdef resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
77246149Ssjg    """
78246149Ssjg    Return an absolute path, resolving via cwd or last_dir if needed.
79246149Ssjg    """
80246149Ssjg    if path.endswith('/.'):
81246149Ssjg        path = path[0:-2]
82253883Ssjg    if len(path) > 0 and path[0] == '/':
83246149Ssjg        return path
84246149Ssjg    if path == '.':
85246149Ssjg        return cwd
86246149Ssjg    if path.startswith('./'):
87246149Ssjg        return cwd + path[1:]
88246149Ssjg    if last_dir == cwd:
89246149Ssjg        last_dir = None
90246149Ssjg    for d in [last_dir, cwd]:
91246149Ssjg        if not d:
92246149Ssjg            continue
93246149Ssjg        p = '/'.join([d,path])
94246149Ssjg        if debug > 2:
95261212Ssjg            print("looking for:", p, end=' ', file=debug_out)
96246149Ssjg        if not os.path.exists(p):
97246149Ssjg            if debug > 2:
98261212Ssjg                print("nope", file=debug_out)
99246149Ssjg            p = None
100246149Ssjg            continue
101246149Ssjg        if debug > 2:
102261212Ssjg            print("found:", p, file=debug_out)
103246149Ssjg        return p
104246149Ssjg    return None
105246149Ssjg
106246149Ssjgdef abspath(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
107246149Ssjg    """
108246149Ssjg    Return an absolute path, resolving via cwd or last_dir if needed.
109246149Ssjg    this gets called a lot, so we try to avoid calling realpath
110246149Ssjg    until we know we have something.
111246149Ssjg    """
112253883Ssjg    rpath = resolve(path, cwd, last_dir, debug, debug_out)
113253883Ssjg    if rpath:
114253883Ssjg        path = rpath
115253883Ssjg    if (path.find('./') > 0 or
116253883Ssjg        path.endswith('/..') or
117253883Ssjg        os.path.islink(path)):
118246149Ssjg        return os.path.realpath(path)
119246149Ssjg    return path
120246149Ssjg
121246149Ssjgdef sort_unique(list, cmp=None, key=None, reverse=False):
122246149Ssjg    list.sort(cmp, key, reverse)
123246149Ssjg    nl = []
124246149Ssjg    le = None
125246149Ssjg    for e in list:
126246149Ssjg        if e == le:
127246149Ssjg            continue
128246149Ssjg        nl.append(e)
129246149Ssjg    return nl
130246149Ssjg
131250837Ssjgdef add_trims(x):
132250837Ssjg    return ['/' + x + '/',
133250837Ssjg            '/' + x,
134250837Ssjg            x + '/',
135250837Ssjg            x]
136250837Ssjg
137246149Ssjgclass MetaFile:
138246149Ssjg    """class to parse meta files generated by bmake."""
139246149Ssjg
140246149Ssjg    conf = None
141246149Ssjg    dirdep_re = None
142246149Ssjg    host_target = None
143246149Ssjg    srctops = []
144246149Ssjg    objroots = []
145246149Ssjg
146246149Ssjg    seen = {}
147246149Ssjg    obj_deps = []
148246149Ssjg    src_deps = []
149246149Ssjg    file_deps = []
150246149Ssjg
151246149Ssjg    def __init__(self, name, conf={}):
152246149Ssjg        """if name is set we will parse it now.
153246149Ssjg        conf can have the follwing keys:
154246149Ssjg
155246149Ssjg        SRCTOPS	list of tops of the src tree(s).
156246149Ssjg
157246149Ssjg        CURDIR	the src directory 'bmake' was run from.
158246149Ssjg
159246149Ssjg        RELDIR	the relative path from SRCTOP to CURDIR
160246149Ssjg
161246149Ssjg        MACHINE	the machine we built for.
162246149Ssjg        	set to 'none' if we are not cross-building.
163249033Ssjg		More specifically if machine cannot be deduced from objdirs.
164246149Ssjg
165250837Ssjg        TARGET_SPEC
166250837Ssjg        	Sometimes MACHINE isn't enough.
167250837Ssjg
168246149Ssjg        HOST_TARGET
169268437Ssjg		when we build for the pseudo machine 'host'
170246149Ssjg		the object tree uses HOST_TARGET rather than MACHINE.
171246149Ssjg
172246149Ssjg        OBJROOTS a list of the common prefix for all obj dirs it might
173246149Ssjg		end in '/' or '-'.
174246149Ssjg
175246149Ssjg        DPDEPS	names an optional file to which per file dependencies
176246149Ssjg		will be appended.
177246149Ssjg		For example if 'some/path/foo.h' is read from SRCTOP
178246149Ssjg		then 'DPDEPS_some/path/foo.h +=' "RELDIR" is output.
179246149Ssjg		This can allow 'bmake' to learn all the dirs within
180246149Ssjg 		the tree that depend on 'foo.h'
181246149Ssjg
182246149Ssjg        debug	desired debug level
183246149Ssjg
184246149Ssjg        debug_out open file to send debug output to (sys.stderr)
185246149Ssjg
186246149Ssjg        """
187246149Ssjg
188246149Ssjg        self.name = name
189246149Ssjg        self.debug = getv(conf, 'debug', 0)
190246149Ssjg        self.debug_out = getv(conf, 'debug_out', sys.stderr)
191246149Ssjg
192249033Ssjg        self.machine = getv(conf, 'MACHINE', '')
193250837Ssjg        self.machine_arch = getv(conf, 'MACHINE_ARCH', '')
194250837Ssjg        self.target_spec = getv(conf, 'TARGET_SPEC', '')
195249033Ssjg        self.curdir = getv(conf, 'CURDIR')
196249033Ssjg        self.reldir = getv(conf, 'RELDIR')
197249033Ssjg        self.dpdeps = getv(conf, 'DPDEPS')
198253883Ssjg        self.line = 0
199249033Ssjg
200246149Ssjg        if not self.conf:
201246149Ssjg            # some of the steps below we want to do only once
202246149Ssjg            self.conf = conf
203246149Ssjg            self.host_target = getv(conf, 'HOST_TARGET')
204246149Ssjg            for srctop in getv(conf, 'SRCTOPS', []):
205246149Ssjg                if srctop[-1] != '/':
206246149Ssjg                    srctop += '/'
207246149Ssjg                if not srctop in self.srctops:
208246149Ssjg                    self.srctops.append(srctop)
209246149Ssjg                _srctop = os.path.realpath(srctop)
210246149Ssjg                if _srctop[-1] != '/':
211246149Ssjg                    _srctop += '/'
212246149Ssjg                if not _srctop in self.srctops:
213246149Ssjg                    self.srctops.append(_srctop)
214246149Ssjg
215250837Ssjg            trim_list = add_trims(self.machine)
216249033Ssjg            if self.machine == 'host':
217250837Ssjg                trim_list += add_trims(self.host_target)
218250837Ssjg            if self.target_spec:
219250837Ssjg                trim_list += add_trims(self.target_spec)
220249033Ssjg
221246149Ssjg            for objroot in getv(conf, 'OBJROOTS', []):
222249033Ssjg                for e in trim_list:
223249033Ssjg                    if objroot.endswith(e):
224249033Ssjg                        # this is not what we want - fix it
225249033Ssjg                        objroot = objroot[0:-len(e)]
226249033Ssjg                        if e.endswith('/'):
227249033Ssjg                            objroot += '/'
228246149Ssjg                if not objroot in self.objroots:
229246149Ssjg                    self.objroots.append(objroot)
230246149Ssjg                    _objroot = os.path.realpath(objroot)
231246149Ssjg                    if objroot[-1] == '/':
232246149Ssjg                        _objroot += '/'
233246149Ssjg                    if not _objroot in self.objroots:
234246149Ssjg                        self.objroots.append(_objroot)
235246149Ssjg
236249033Ssjg            # we want the longest match
237249033Ssjg            self.srctops.sort(reverse=True)
238249033Ssjg            self.objroots.sort(reverse=True)
239249033Ssjg
240246149Ssjg            if self.debug:
241261212Ssjg                print("host_target=", self.host_target, file=self.debug_out)
242261212Ssjg                print("srctops=", self.srctops, file=self.debug_out)
243261212Ssjg                print("objroots=", self.objroots, file=self.debug_out)
244246149Ssjg
245246149Ssjg            self.dirdep_re = re.compile(r'([^/]+)/(.+)')
246246149Ssjg
247246149Ssjg        if self.dpdeps and not self.reldir:
248246149Ssjg            if self.debug:
249261212Ssjg                print("need reldir:", end=' ', file=self.debug_out)
250246149Ssjg            if self.curdir:
251246149Ssjg                srctop = self.find_top(self.curdir, self.srctops)
252246149Ssjg                if srctop:
253246149Ssjg                    self.reldir = self.curdir.replace(srctop,'')
254246149Ssjg                    if self.debug:
255261212Ssjg                        print(self.reldir, file=self.debug_out)
256246149Ssjg            if not self.reldir:
257246149Ssjg                self.dpdeps = None      # we cannot do it?
258246149Ssjg
259249033Ssjg        self.cwd = os.getcwd()          # make sure this is initialized
260249033Ssjg
261246149Ssjg        if name:
262253883Ssjg            self.try_parse()
263246149Ssjg
264246149Ssjg    def reset(self):
265246149Ssjg        """reset state if we are being passed meta files from multiple directories."""
266246149Ssjg        self.seen = {}
267246149Ssjg        self.obj_deps = []
268246149Ssjg        self.src_deps = []
269246149Ssjg        self.file_deps = []
270246149Ssjg
271246149Ssjg    def dirdeps(self, sep='\n'):
272246149Ssjg        """return DIRDEPS"""
273246149Ssjg        return sep.strip() + sep.join(self.obj_deps)
274246149Ssjg
275246149Ssjg    def src_dirdeps(self, sep='\n'):
276246149Ssjg        """return SRC_DIRDEPS"""
277246149Ssjg        return sep.strip() + sep.join(self.src_deps)
278246149Ssjg
279246149Ssjg    def file_depends(self, out=None):
280246149Ssjg        """Append DPDEPS_${file} += ${RELDIR}
281246149Ssjg        for each file we saw, to the output file."""
282246149Ssjg        if not self.reldir:
283246149Ssjg            return None
284246149Ssjg        for f in sort_unique(self.file_deps):
285261212Ssjg            print('DPDEPS_%s += %s' % (f, self.reldir), file=out)
286246149Ssjg
287246149Ssjg    def seenit(self, dir):
288246149Ssjg        """rememer that we have seen dir."""
289246149Ssjg        self.seen[dir] = 1
290246149Ssjg
291246149Ssjg    def add(self, list, data, clue=''):
292246149Ssjg        """add data to list if it isn't already there."""
293246149Ssjg        if data not in list:
294246149Ssjg            list.append(data)
295246149Ssjg            if self.debug:
296261212Ssjg                print("%s: %sAdd: %s" % (self.name, clue, data), file=self.debug_out)
297246149Ssjg
298246149Ssjg    def find_top(self, path, list):
299268437Ssjg        """the logical tree may be split across multiple trees"""
300246149Ssjg        for top in list:
301246149Ssjg            if path.startswith(top):
302246149Ssjg                if self.debug > 2:
303261212Ssjg                    print("found in", top, file=self.debug_out)
304246149Ssjg                return top
305246149Ssjg        return None
306246149Ssjg
307246149Ssjg    def find_obj(self, objroot, dir, path, input):
308246149Ssjg        """return path within objroot, taking care of .dirdep files"""
309246149Ssjg        ddep = None
310246149Ssjg        for ddepf in [path + '.dirdep', dir + '/.dirdep']:
311246149Ssjg            if not ddep and os.path.exists(ddepf):
312261212Ssjg                ddep = open(ddepf, 'r').readline().strip('# \n')
313246149Ssjg                if self.debug > 1:
314261212Ssjg                    print("found %s: %s\n" % (ddepf, ddep), file=self.debug_out)
315246149Ssjg                if ddep.endswith(self.machine):
316246149Ssjg                    ddep = ddep[0:-(1+len(self.machine))]
317250837Ssjg                elif self.target_spec and ddep.endswith(self.target_spec):
318250837Ssjg                    ddep = ddep[0:-(1+len(self.target_spec))]
319246149Ssjg
320246149Ssjg        if not ddep:
321246149Ssjg            # no .dirdeps, so remember that we've seen the raw input
322246149Ssjg            self.seenit(input)
323246149Ssjg            self.seenit(dir)
324246149Ssjg            if self.machine == 'none':
325246149Ssjg                if dir.startswith(objroot):
326246149Ssjg                    return dir.replace(objroot,'')
327246149Ssjg                return None
328246149Ssjg            m = self.dirdep_re.match(dir.replace(objroot,''))
329246149Ssjg            if m:
330246149Ssjg                ddep = m.group(2)
331246149Ssjg                dmachine = m.group(1)
332246149Ssjg                if dmachine != self.machine:
333246149Ssjg                    if not (self.machine == 'host' and
334246149Ssjg                            dmachine == self.host_target):
335246149Ssjg                        if self.debug > 2:
336261212Ssjg                            print("adding .%s to %s" % (dmachine, ddep), file=self.debug_out)
337246149Ssjg                        ddep += '.' + dmachine
338246149Ssjg
339246149Ssjg        return ddep
340246149Ssjg
341253883Ssjg    def try_parse(self, name=None, file=None):
342253883Ssjg        """give file and line number causing exception"""
343253883Ssjg        try:
344253883Ssjg            self.parse(name, file)
345253883Ssjg        except:
346253883Ssjg            # give a useful clue
347261212Ssjg            print('{}:{}: '.format(self.name, self.line), end=' ', file=sys.stderr)
348253883Ssjg            raise
349253883Ssjg
350246149Ssjg    def parse(self, name=None, file=None):
351246149Ssjg        """A meta file looks like:
352246149Ssjg
353246149Ssjg	# Meta data file "path"
354246149Ssjg	CMD "command-line"
355246149Ssjg	CWD "cwd"
356246149Ssjg	TARGET "target"
357246149Ssjg	-- command output --
358246149Ssjg	-- filemon acquired metadata --
359246149Ssjg	# buildmon version 3
360246149Ssjg	V 3
361246149Ssjg	C "pid" "cwd"
362246149Ssjg	E "pid" "path"
363246149Ssjg        F "pid" "child"
364246149Ssjg	R "pid" "path"
365246149Ssjg	W "pid" "path"
366246149Ssjg	X "pid" "status"
367246149Ssjg        D "pid" "path"
368246149Ssjg        L "pid" "src" "target"
369246149Ssjg        M "pid" "old" "new"
370246149Ssjg        S "pid" "path"
371246149Ssjg        # Bye bye
372246149Ssjg
373246149Ssjg        We go to some effort to avoid processing a dependency more than once.
374246149Ssjg        Of the above record types only C,E,F,L,R,V and W are of interest.
375246149Ssjg        """
376246149Ssjg
377246149Ssjg        version = 0                     # unknown
378246149Ssjg        if name:
379246149Ssjg            self.name = name;
380246149Ssjg        if file:
381246149Ssjg            f = file
382246149Ssjg            cwd = last_dir = self.cwd
383246149Ssjg        else:
384261212Ssjg            f = open(self.name, 'r')
385246149Ssjg        skip = True
386246149Ssjg        pid_cwd = {}
387246149Ssjg        pid_last_dir = {}
388246149Ssjg        last_pid = 0
389246149Ssjg
390253883Ssjg        self.line = 0
391246149Ssjg        if self.curdir:
392246149Ssjg            self.seenit(self.curdir)    # we ignore this
393246149Ssjg
394246149Ssjg        interesting = 'CEFLRV'
395246149Ssjg        for line in f:
396253883Ssjg            self.line += 1
397246149Ssjg            # ignore anything we don't care about
398246149Ssjg            if not line[0] in interesting:
399246149Ssjg                continue
400246149Ssjg            if self.debug > 2:
401261212Ssjg                print("input:", line, end=' ', file=self.debug_out)
402246149Ssjg            w = line.split()
403246149Ssjg
404246149Ssjg            if skip:
405246149Ssjg                if w[0] == 'V':
406246149Ssjg                    skip = False
407246149Ssjg                    version = int(w[1])
408246149Ssjg                    """
409246149Ssjg                    if version < 4:
410246149Ssjg                        # we cannot ignore 'W' records
411246149Ssjg                        # as they may be 'rw'
412246149Ssjg                        interesting += 'W'
413246149Ssjg                    """
414246149Ssjg                elif w[0] == 'CWD':
415246149Ssjg                    self.cwd = cwd = last_dir = w[1]
416246149Ssjg                    self.seenit(cwd)    # ignore this
417246149Ssjg                    if self.debug:
418261212Ssjg                        print("%s: CWD=%s" % (self.name, cwd), file=self.debug_out)
419246149Ssjg                continue
420246149Ssjg
421246149Ssjg            pid = int(w[1])
422246149Ssjg            if pid != last_pid:
423246149Ssjg                if last_pid:
424246149Ssjg                    pid_cwd[last_pid] = cwd
425246149Ssjg                    pid_last_dir[last_pid] = last_dir
426246149Ssjg                cwd = getv(pid_cwd, pid, self.cwd)
427246149Ssjg                last_dir = getv(pid_last_dir, pid, self.cwd)
428246149Ssjg                last_pid = pid
429246149Ssjg
430246149Ssjg            # process operations
431246149Ssjg            if w[0] == 'F':
432246149Ssjg                npid = int(w[2])
433246149Ssjg                pid_cwd[npid] = cwd
434246149Ssjg                pid_last_dir[npid] = cwd
435246149Ssjg                last_pid = npid
436246149Ssjg                continue
437246149Ssjg            elif w[0] == 'C':
438246149Ssjg                cwd = abspath(w[2], cwd, None, self.debug, self.debug_out)
439246149Ssjg                if cwd.endswith('/.'):
440246149Ssjg                    cwd = cwd[0:-2]
441246149Ssjg                last_dir = cwd
442246149Ssjg                if self.debug > 1:
443261212Ssjg                    print("cwd=", cwd, file=self.debug_out)
444246149Ssjg                continue
445246149Ssjg
446246149Ssjg            if w[2] in self.seen:
447246149Ssjg                if self.debug > 2:
448261212Ssjg                    print("seen:", w[2], file=self.debug_out)
449246149Ssjg                continue
450246149Ssjg            # file operations
451246149Ssjg            if w[0] in 'ML':
452246149Ssjg                path = w[2].strip("'")
453246149Ssjg            else:
454246149Ssjg                path = w[2]
455246149Ssjg            # we are never interested in .dirdep files as dependencies
456246149Ssjg            if path.endswith('.dirdep'):
457246149Ssjg                continue
458246149Ssjg            # we don't want to resolve the last component if it is
459246149Ssjg            # a symlink
460246149Ssjg            path = resolve(path, cwd, last_dir, self.debug, self.debug_out)
461246149Ssjg            if not path:
462246149Ssjg                continue
463246149Ssjg            dir,base = os.path.split(path)
464246149Ssjg            if dir in self.seen:
465246149Ssjg                if self.debug > 2:
466261212Ssjg                    print("seen:", dir, file=self.debug_out)
467246149Ssjg                continue
468246149Ssjg            # we can have a path in an objdir which is a link
469246149Ssjg            # to the src dir, we may need to add dependencies for each
470246149Ssjg            rdir = dir
471246149Ssjg            dir = abspath(dir, cwd, last_dir, self.debug, self.debug_out)
472246149Ssjg            if rdir == dir or rdir.find('./') > 0:
473246149Ssjg                rdir = None
474246149Ssjg            # now put path back together
475246149Ssjg            path = '/'.join([dir,base])
476246149Ssjg            if self.debug > 1:
477261212Ssjg                print("raw=%s rdir=%s dir=%s path=%s" % (w[2], rdir, dir, path), file=self.debug_out)
478246149Ssjg            if w[0] in 'SRWL':
479246149Ssjg                if w[0] == 'W' and path.endswith('.dirdep'):
480246149Ssjg                    continue
481246149Ssjg                if path in [last_dir, cwd, self.cwd, self.curdir]:
482246149Ssjg                    if self.debug > 1:
483261212Ssjg                        print("skipping:", path, file=self.debug_out)
484246149Ssjg                    continue
485246149Ssjg                if os.path.isdir(path):
486246149Ssjg                    if w[0] in 'RW':
487246149Ssjg                        last_dir = path;
488246149Ssjg                    if self.debug > 1:
489261212Ssjg                        print("ldir=", last_dir, file=self.debug_out)
490246149Ssjg                    continue
491246149Ssjg
492246149Ssjg            if w[0] in 'REWML':
493246149Ssjg                # finally, we get down to it
494246149Ssjg                if dir == self.cwd or dir == self.curdir:
495246149Ssjg                    continue
496246149Ssjg                srctop = self.find_top(path, self.srctops)
497246149Ssjg                if srctop:
498246149Ssjg                    if self.dpdeps:
499246149Ssjg                        self.add(self.file_deps, path.replace(srctop,''), 'file')
500246149Ssjg                    self.add(self.src_deps, dir.replace(srctop,''), 'src')
501246149Ssjg                    self.seenit(w[2])
502246149Ssjg                    self.seenit(dir)
503246149Ssjg                    if rdir and not rdir.startswith(srctop):
504246149Ssjg                        dir = rdir      # for below
505246149Ssjg                        rdir = None
506246149Ssjg                    else:
507246149Ssjg                        continue
508246149Ssjg
509246149Ssjg                objroot = None
510246149Ssjg                for dir in [dir,rdir]:
511246149Ssjg                    if not dir:
512246149Ssjg                        continue
513246149Ssjg                    objroot = self.find_top(dir, self.objroots)
514246149Ssjg                    if objroot:
515246149Ssjg                        break
516246149Ssjg                if objroot:
517246149Ssjg                    ddep = self.find_obj(objroot, dir, path, w[2])
518246149Ssjg                    if ddep:
519246149Ssjg                        self.add(self.obj_deps, ddep, 'obj')
520246149Ssjg                else:
521246149Ssjg                    # don't waste time looking again
522246149Ssjg                    self.seenit(w[2])
523246149Ssjg                    self.seenit(dir)
524246149Ssjg        if not file:
525246149Ssjg            f.close()
526246149Ssjg
527246149Ssjg
528246149Ssjgdef main(argv, klass=MetaFile, xopts='', xoptf=None):
529246149Ssjg    """Simple driver for class MetaFile.
530246149Ssjg
531246149Ssjg    Usage:
532246149Ssjg    	script [options] [key=value ...] "meta" ...
533246149Ssjg
534246149Ssjg    Options and key=value pairs contribute to the
535246149Ssjg    dictionary passed to MetaFile.
536246149Ssjg
537246149Ssjg    -S "SRCTOP"
538246149Ssjg		add "SRCTOP" to the "SRCTOPS" list.
539246149Ssjg
540246149Ssjg    -C "CURDIR"
541246149Ssjg
542246149Ssjg    -O "OBJROOT"
543246149Ssjg    		add "OBJROOT" to the "OBJROOTS" list.
544246149Ssjg
545246149Ssjg    -m "MACHINE"
546246149Ssjg
547250837Ssjg    -a "MACHINE_ARCH"
548250837Ssjg
549246149Ssjg    -H "HOST_TARGET"
550246149Ssjg
551246149Ssjg    -D "DPDEPS"
552246149Ssjg
553246149Ssjg    -d	bumps debug level
554246149Ssjg
555246149Ssjg    """
556246149Ssjg    import getopt
557246149Ssjg
558246149Ssjg    # import Psyco if we can
559246149Ssjg    # it can speed things up quite a bit
560246149Ssjg    have_psyco = 0
561246149Ssjg    try:
562246149Ssjg        import psyco
563246149Ssjg        psyco.full()
564246149Ssjg        have_psyco = 1
565246149Ssjg    except:
566246149Ssjg        pass
567246149Ssjg
568246149Ssjg    conf = {
569246149Ssjg        'SRCTOPS': [],
570246149Ssjg        'OBJROOTS': [],
571246149Ssjg        }
572246149Ssjg
573246149Ssjg    try:
574246149Ssjg        machine = os.environ['MACHINE']
575246149Ssjg        if machine:
576246149Ssjg            conf['MACHINE'] = machine
577250837Ssjg        machine_arch = os.environ['MACHINE_ARCH']
578250837Ssjg        if machine_arch:
579250837Ssjg            conf['MACHINE_ARCH'] = machine_arch
580246149Ssjg        srctop = os.environ['SB_SRC']
581246149Ssjg        if srctop:
582246149Ssjg            conf['SRCTOPS'].append(srctop)
583246149Ssjg        objroot = os.environ['SB_OBJROOT']
584246149Ssjg        if objroot:
585246149Ssjg            conf['OBJROOTS'].append(objroot)
586246149Ssjg    except:
587246149Ssjg        pass
588246149Ssjg
589246149Ssjg    debug = 0
590246149Ssjg    output = True
591246149Ssjg
592250837Ssjg    opts, args = getopt.getopt(argv[1:], 'a:dS:C:O:R:m:D:H:qT:' + xopts)
593246149Ssjg    for o, a in opts:
594250837Ssjg        if o == '-a':
595250837Ssjg            conf['MACHINE_ARCH'] = a
596250837Ssjg        elif o == '-d':
597246149Ssjg            debug += 1
598246149Ssjg        elif o == '-q':
599246149Ssjg            output = False
600246149Ssjg        elif o == '-H':
601246149Ssjg            conf['HOST_TARGET'] = a
602246149Ssjg        elif o == '-S':
603246149Ssjg            if a not in conf['SRCTOPS']:
604246149Ssjg                conf['SRCTOPS'].append(a)
605246149Ssjg        elif o == '-C':
606246149Ssjg            conf['CURDIR'] = a
607246149Ssjg        elif o == '-O':
608246149Ssjg            if a not in conf['OBJROOTS']:
609246149Ssjg                conf['OBJROOTS'].append(a)
610246149Ssjg        elif o == '-R':
611246149Ssjg            conf['RELDIR'] = a
612246149Ssjg        elif o == '-D':
613246149Ssjg            conf['DPDEPS'] = a
614246149Ssjg        elif o == '-m':
615246149Ssjg            conf['MACHINE'] = a
616250837Ssjg        elif o == '-T':
617250837Ssjg            conf['TARGET_SPEC'] = a
618246149Ssjg        elif xoptf:
619246149Ssjg            xoptf(o, a, conf)
620246149Ssjg
621246149Ssjg    conf['debug'] = debug
622246149Ssjg
623246149Ssjg    # get any var=val assignments
624246149Ssjg    eaten = []
625246149Ssjg    for a in args:
626246149Ssjg        if a.find('=') > 0:
627246149Ssjg            k,v = a.split('=')
628246149Ssjg            if k in ['SRCTOP','OBJROOT','SRCTOPS','OBJROOTS']:
629246149Ssjg                if k == 'SRCTOP':
630246149Ssjg                    k = 'SRCTOPS'
631246149Ssjg                elif k == 'OBJROOT':
632246149Ssjg                    k = 'OBJROOTS'
633246149Ssjg                if v not in conf[k]:
634246149Ssjg                    conf[k].append(v)
635246149Ssjg            else:
636246149Ssjg                conf[k] = v
637246149Ssjg            eaten.append(a)
638246149Ssjg            continue
639246149Ssjg        break
640246149Ssjg
641246149Ssjg    for a in eaten:
642246149Ssjg        args.remove(a)
643246149Ssjg
644246149Ssjg    debug_out = getv(conf, 'debug_out', sys.stderr)
645246149Ssjg
646246149Ssjg    if debug:
647261212Ssjg        print("config:", file=debug_out)
648261212Ssjg        print("psyco=", have_psyco, file=debug_out)
649261212Ssjg        for k,v in list(conf.items()):
650261212Ssjg            print("%s=%s" % (k,v), file=debug_out)
651246149Ssjg
652246149Ssjg    for a in args:
653253883Ssjg        if a.endswith('.meta'):
654253883Ssjg            m = klass(a, conf)
655253883Ssjg        elif a.startswith('@'):
656253883Ssjg            # there can actually multiple files per line
657253883Ssjg            for line in open(a[1:]):
658253883Ssjg                for f in line.strip().split():
659253883Ssjg                    m = klass(f, conf)
660246149Ssjg
661246149Ssjg    if output:
662261212Ssjg        print(m.dirdeps())
663246149Ssjg
664261212Ssjg        print(m.src_dirdeps('\nsrc:'))
665246149Ssjg
666246149Ssjg        dpdeps = getv(conf, 'DPDEPS')
667246149Ssjg        if dpdeps:
668246149Ssjg            m.file_depends(open(dpdeps, 'wb'))
669246149Ssjg
670246149Ssjg    return m
671246149Ssjg
672246149Ssjgif __name__ == '__main__':
673246149Ssjg    try:
674246149Ssjg        main(sys.argv)
675246149Ssjg    except:
676246149Ssjg        # yes, this goes to stdout
677261212Ssjg        print("ERROR: ", sys.exc_info()[1])
678246149Ssjg        raise
679246149Ssjg
680