xref: /dragonfly/contrib/bmake/mk/meta2deps.py (revision 6eef5f0c)
15f1e34d9SAlexandre Perrin#!/usr/bin/env python
25f1e34d9SAlexandre Perrin
36a91b982SJohn Marinofrom __future__ import print_function
46a91b982SJohn Marino
55f1e34d9SAlexandre Perrin"""
65f1e34d9SAlexandre PerrinThis script parses each "meta" file and extracts the
75f1e34d9SAlexandre Perrininformation needed to deduce build and src dependencies.
85f1e34d9SAlexandre Perrin
95f1e34d9SAlexandre PerrinIt works much the same as the original shell script, but is
105f1e34d9SAlexandre Perrin*much* more efficient.
115f1e34d9SAlexandre Perrin
125f1e34d9SAlexandre PerrinThe parsing work is handled by the class MetaFile.
135f1e34d9SAlexandre PerrinWe only pay attention to a subset of the information in the
145f1e34d9SAlexandre Perrin"meta" files.  Specifically:
155f1e34d9SAlexandre Perrin
165f1e34d9SAlexandre Perrin'CWD'	to initialize our notion.
175f1e34d9SAlexandre Perrin
185f1e34d9SAlexandre Perrin'C'	to track chdir(2) on a per process basis
195f1e34d9SAlexandre Perrin
205f1e34d9SAlexandre Perrin'R'	files read are what we really care about.
215f1e34d9SAlexandre Perrin	directories read, provide a clue to resolving
225f1e34d9SAlexandre Perrin	subsequent relative paths.  That is if we cannot find
235f1e34d9SAlexandre Perrin	them relative to 'cwd', we check relative to the last
245f1e34d9SAlexandre Perrin	dir read.
255f1e34d9SAlexandre Perrin
265f1e34d9SAlexandre Perrin'W'	files opened for write or read-write,
275f1e34d9SAlexandre Perrin	for filemon V3 and earlier.
285f1e34d9SAlexandre Perrin
295f1e34d9SAlexandre Perrin'E'	files executed.
305f1e34d9SAlexandre Perrin
315f1e34d9SAlexandre Perrin'L'	files linked
325f1e34d9SAlexandre Perrin
335f1e34d9SAlexandre Perrin'V'	the filemon version, this record is used as a clue
345f1e34d9SAlexandre Perrin	that we have reached the interesting bit.
355f1e34d9SAlexandre Perrin
365f1e34d9SAlexandre Perrin"""
375f1e34d9SAlexandre Perrin
385f1e34d9SAlexandre Perrin"""
395f1e34d9SAlexandre PerrinRCSid:
40*6eef5f0cSAntonio Huete Jimenez	$Id: meta2deps.py,v 1.44 2022/01/29 02:42:01 sjg Exp $
415f1e34d9SAlexandre Perrin
42a34d5fb1SAntonio Huete Jimenez	Copyright (c) 2011-2020, Simon J. Gerraty
43ca58f742SDaniel Fojt	Copyright (c) 2011-2017, Juniper Networks, Inc.
445f1e34d9SAlexandre Perrin	All rights reserved.
455f1e34d9SAlexandre Perrin
465f1e34d9SAlexandre Perrin	Redistribution and use in source and binary forms, with or without
475f1e34d9SAlexandre Perrin	modification, are permitted provided that the following conditions
485f1e34d9SAlexandre Perrin	are met:
495f1e34d9SAlexandre Perrin	1. Redistributions of source code must retain the above copyright
505f1e34d9SAlexandre Perrin	   notice, this list of conditions and the following disclaimer.
515f1e34d9SAlexandre Perrin	2. Redistributions in binary form must reproduce the above copyright
525f1e34d9SAlexandre Perrin	   notice, this list of conditions and the following disclaimer in the
535f1e34d9SAlexandre Perrin	   documentation and/or other materials provided with the distribution.
545f1e34d9SAlexandre Perrin
555f1e34d9SAlexandre Perrin	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
565f1e34d9SAlexandre Perrin	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
575f1e34d9SAlexandre Perrin	LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
585f1e34d9SAlexandre Perrin	A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
595f1e34d9SAlexandre Perrin	OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
605f1e34d9SAlexandre Perrin	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
615f1e34d9SAlexandre Perrin	LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
625f1e34d9SAlexandre Perrin	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
635f1e34d9SAlexandre Perrin	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
645f1e34d9SAlexandre Perrin	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
655f1e34d9SAlexandre Perrin	OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
665f1e34d9SAlexandre Perrin
675f1e34d9SAlexandre Perrin"""
685f1e34d9SAlexandre Perrin
69*6eef5f0cSAntonio Huete Jimenezimport os
70*6eef5f0cSAntonio Huete Jimenezimport re
71*6eef5f0cSAntonio Huete Jimenezimport sys
72*6eef5f0cSAntonio Huete Jimenezimport stat
735f1e34d9SAlexandre Perrin
745f1e34d9SAlexandre Perrindef resolve(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
755f1e34d9SAlexandre Perrin    """
765f1e34d9SAlexandre Perrin    Return an absolute path, resolving via cwd or last_dir if needed.
775f1e34d9SAlexandre Perrin    """
785f1e34d9SAlexandre Perrin    if path.endswith('/.'):
795f1e34d9SAlexandre Perrin        path = path[0:-2]
805f1e34d9SAlexandre Perrin    if len(path) > 0 and path[0] == '/':
81ca58f742SDaniel Fojt        if os.path.exists(path):
825f1e34d9SAlexandre Perrin            return path
83ca58f742SDaniel Fojt        if debug > 2:
84ca58f742SDaniel Fojt            print("skipping non-existent:", path, file=debug_out)
85ca58f742SDaniel Fojt        return None
865f1e34d9SAlexandre Perrin    if path == '.':
875f1e34d9SAlexandre Perrin        return cwd
885f1e34d9SAlexandre Perrin    if path.startswith('./'):
895f1e34d9SAlexandre Perrin        return cwd + path[1:]
905f1e34d9SAlexandre Perrin    if last_dir == cwd:
915f1e34d9SAlexandre Perrin        last_dir = None
925f1e34d9SAlexandre Perrin    for d in [last_dir, cwd]:
935f1e34d9SAlexandre Perrin        if not d:
945f1e34d9SAlexandre Perrin            continue
95ca58f742SDaniel Fojt        if path == '..':
96ca58f742SDaniel Fojt            dw = d.split('/')
97ca58f742SDaniel Fojt            p = '/'.join(dw[:-1])
98ca58f742SDaniel Fojt            if not p:
99ca58f742SDaniel Fojt                p = '/'
100ca58f742SDaniel Fojt            return p
1015f1e34d9SAlexandre Perrin        p = '/'.join([d,path])
1025f1e34d9SAlexandre Perrin        if debug > 2:
1036a91b982SJohn Marino            print("looking for:", p, end=' ', file=debug_out)
1045f1e34d9SAlexandre Perrin        if not os.path.exists(p):
1055f1e34d9SAlexandre Perrin            if debug > 2:
1066a91b982SJohn Marino                print("nope", file=debug_out)
1075f1e34d9SAlexandre Perrin            p = None
1085f1e34d9SAlexandre Perrin            continue
1095f1e34d9SAlexandre Perrin        if debug > 2:
1106a91b982SJohn Marino            print("found:", p, file=debug_out)
1115f1e34d9SAlexandre Perrin        return p
1125f1e34d9SAlexandre Perrin    return None
1135f1e34d9SAlexandre Perrin
114ca58f742SDaniel Fojtdef cleanpath(path):
115ca58f742SDaniel Fojt    """cleanup path without using realpath(3)"""
116ca58f742SDaniel Fojt    if path.startswith('/'):
117ca58f742SDaniel Fojt        r = '/'
118ca58f742SDaniel Fojt    else:
119ca58f742SDaniel Fojt        r = ''
120ca58f742SDaniel Fojt    p = []
121ca58f742SDaniel Fojt    w = path.split('/')
122ca58f742SDaniel Fojt    for d in w:
123ca58f742SDaniel Fojt        if not d or d == '.':
124ca58f742SDaniel Fojt            continue
125ca58f742SDaniel Fojt        if d == '..':
126ca58f742SDaniel Fojt            try:
127ca58f742SDaniel Fojt                p.pop()
128ca58f742SDaniel Fojt                continue
129ca58f742SDaniel Fojt            except:
130ca58f742SDaniel Fojt                break
131ca58f742SDaniel Fojt        p.append(d)
132ca58f742SDaniel Fojt
133ca58f742SDaniel Fojt    return r + '/'.join(p)
134ca58f742SDaniel Fojt
1355f1e34d9SAlexandre Perrindef abspath(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr):
1365f1e34d9SAlexandre Perrin    """
1375f1e34d9SAlexandre Perrin    Return an absolute path, resolving via cwd or last_dir if needed.
138ca58f742SDaniel Fojt    this gets called a lot, so we try to avoid calling realpath.
1395f1e34d9SAlexandre Perrin    """
1405f1e34d9SAlexandre Perrin    rpath = resolve(path, cwd, last_dir, debug, debug_out)
1415f1e34d9SAlexandre Perrin    if rpath:
1425f1e34d9SAlexandre Perrin        path = rpath
143ca58f742SDaniel Fojt    elif len(path) > 0 and path[0] == '/':
144ca58f742SDaniel Fojt        return None
145f445c897SJohn Marino    if (path.find('/') < 0 or
146f445c897SJohn Marino        path.find('./') > 0 or
147ca58f742SDaniel Fojt        path.endswith('/..')):
148ca58f742SDaniel Fojt        path = cleanpath(path)
1495f1e34d9SAlexandre Perrin    return path
1505f1e34d9SAlexandre Perrin
1515f1e34d9SAlexandre Perrindef sort_unique(list, cmp=None, key=None, reverse=False):
152*6eef5f0cSAntonio Huete Jimenez    if sys.version_info[0] == 2:
1535f1e34d9SAlexandre Perrin        list.sort(cmp, key, reverse)
154*6eef5f0cSAntonio Huete Jimenez    else:
155*6eef5f0cSAntonio Huete Jimenez        list.sort(reverse=reverse)
1565f1e34d9SAlexandre Perrin    nl = []
1575f1e34d9SAlexandre Perrin    le = None
1585f1e34d9SAlexandre Perrin    for e in list:
1595f1e34d9SAlexandre Perrin        if e == le:
1605f1e34d9SAlexandre Perrin            continue
161ca58f742SDaniel Fojt        le = e
1625f1e34d9SAlexandre Perrin        nl.append(e)
1635f1e34d9SAlexandre Perrin    return nl
1645f1e34d9SAlexandre Perrin
1655f1e34d9SAlexandre Perrindef add_trims(x):
1665f1e34d9SAlexandre Perrin    return ['/' + x + '/',
1675f1e34d9SAlexandre Perrin            '/' + x,
1685f1e34d9SAlexandre Perrin            x + '/',
1695f1e34d9SAlexandre Perrin            x]
1705f1e34d9SAlexandre Perrin
171*6eef5f0cSAntonio Huete Jimenezdef target_spec_exts(target_spec):
172*6eef5f0cSAntonio Huete Jimenez    """return a list of dirdep extensions that could match target_spec"""
173*6eef5f0cSAntonio Huete Jimenez
174*6eef5f0cSAntonio Huete Jimenez    if target_spec.find(',') < 0:
175*6eef5f0cSAntonio Huete Jimenez        return ['.'+target_spec]
176*6eef5f0cSAntonio Huete Jimenez    w = target_spec.split(',')
177*6eef5f0cSAntonio Huete Jimenez    n = len(w)
178*6eef5f0cSAntonio Huete Jimenez    e = []
179*6eef5f0cSAntonio Huete Jimenez    while n > 0:
180*6eef5f0cSAntonio Huete Jimenez        e.append('.'+','.join(w[0:n]))
181*6eef5f0cSAntonio Huete Jimenez        n -= 1
182*6eef5f0cSAntonio Huete Jimenez    return e
183*6eef5f0cSAntonio Huete Jimenez
1845f1e34d9SAlexandre Perrinclass MetaFile:
1855f1e34d9SAlexandre Perrin    """class to parse meta files generated by bmake."""
1865f1e34d9SAlexandre Perrin
1875f1e34d9SAlexandre Perrin    conf = None
1885f1e34d9SAlexandre Perrin    dirdep_re = None
1895f1e34d9SAlexandre Perrin    host_target = None
1905f1e34d9SAlexandre Perrin    srctops = []
1915f1e34d9SAlexandre Perrin    objroots = []
192f445c897SJohn Marino    excludes = []
1935f1e34d9SAlexandre Perrin    seen = {}
1945f1e34d9SAlexandre Perrin    obj_deps = []
1955f1e34d9SAlexandre Perrin    src_deps = []
1965f1e34d9SAlexandre Perrin    file_deps = []
1975f1e34d9SAlexandre Perrin
1985f1e34d9SAlexandre Perrin    def __init__(self, name, conf={}):
1995f1e34d9SAlexandre Perrin        """if name is set we will parse it now.
2005f1e34d9SAlexandre Perrin        conf can have the follwing keys:
2015f1e34d9SAlexandre Perrin
2025f1e34d9SAlexandre Perrin        SRCTOPS list of tops of the src tree(s).
2035f1e34d9SAlexandre Perrin
2045f1e34d9SAlexandre Perrin        CURDIR  the src directory 'bmake' was run from.
2055f1e34d9SAlexandre Perrin
2065f1e34d9SAlexandre Perrin        RELDIR  the relative path from SRCTOP to CURDIR
2075f1e34d9SAlexandre Perrin
2085f1e34d9SAlexandre Perrin        MACHINE the machine we built for.
2095f1e34d9SAlexandre Perrin                set to 'none' if we are not cross-building.
2105f1e34d9SAlexandre Perrin                More specifically if machine cannot be deduced from objdirs.
2115f1e34d9SAlexandre Perrin
2125f1e34d9SAlexandre Perrin        TARGET_SPEC
2135f1e34d9SAlexandre Perrin                Sometimes MACHINE isn't enough.
2145f1e34d9SAlexandre Perrin
2155f1e34d9SAlexandre Perrin        HOST_TARGET
2166a91b982SJohn Marino                when we build for the pseudo machine 'host'
2175f1e34d9SAlexandre Perrin                the object tree uses HOST_TARGET rather than MACHINE.
2185f1e34d9SAlexandre Perrin
2195f1e34d9SAlexandre Perrin        OBJROOTS a list of the common prefix for all obj dirs it might
2205f1e34d9SAlexandre Perrin                end in '/' or '-'.
2215f1e34d9SAlexandre Perrin
2225f1e34d9SAlexandre Perrin        DPDEPS  names an optional file to which per file dependencies
2235f1e34d9SAlexandre Perrin                will be appended.
2245f1e34d9SAlexandre Perrin                For example if 'some/path/foo.h' is read from SRCTOP
2255f1e34d9SAlexandre Perrin                then 'DPDEPS_some/path/foo.h +=' "RELDIR" is output.
2265f1e34d9SAlexandre Perrin                This can allow 'bmake' to learn all the dirs within
2275f1e34d9SAlexandre Perrin                the tree that depend on 'foo.h'
2285f1e34d9SAlexandre Perrin
229f445c897SJohn Marino        EXCLUDES
230f445c897SJohn Marino                A list of paths to ignore.
231f445c897SJohn Marino                ccache(1) can otherwise be trouble.
232f445c897SJohn Marino
2335f1e34d9SAlexandre Perrin        debug   desired debug level
2345f1e34d9SAlexandre Perrin
2355f1e34d9SAlexandre Perrin        debug_out open file to send debug output to (sys.stderr)
2365f1e34d9SAlexandre Perrin
2375f1e34d9SAlexandre Perrin        """
2385f1e34d9SAlexandre Perrin
2395f1e34d9SAlexandre Perrin        self.name = name
240*6eef5f0cSAntonio Huete Jimenez        self.debug = conf.get('debug', 0)
241*6eef5f0cSAntonio Huete Jimenez        self.debug_out = conf.get('debug_out', sys.stderr)
2425f1e34d9SAlexandre Perrin
243*6eef5f0cSAntonio Huete Jimenez        self.machine = conf.get('MACHINE', '')
244*6eef5f0cSAntonio Huete Jimenez        self.machine_arch = conf.get('MACHINE_ARCH', '')
245*6eef5f0cSAntonio Huete Jimenez        self.target_spec = conf.get('TARGET_SPEC', self.machine)
246*6eef5f0cSAntonio Huete Jimenez        self.exts = target_spec_exts(self.target_spec)
247*6eef5f0cSAntonio Huete Jimenez        self.curdir = conf.get('CURDIR')
248*6eef5f0cSAntonio Huete Jimenez        self.reldir = conf.get('RELDIR')
249*6eef5f0cSAntonio Huete Jimenez        self.dpdeps = conf.get('DPDEPS')
250*6eef5f0cSAntonio Huete Jimenez        self.pids = {}
2515f1e34d9SAlexandre Perrin        self.line = 0
2525f1e34d9SAlexandre Perrin
2535f1e34d9SAlexandre Perrin        if not self.conf:
2545f1e34d9SAlexandre Perrin            # some of the steps below we want to do only once
2555f1e34d9SAlexandre Perrin            self.conf = conf
256*6eef5f0cSAntonio Huete Jimenez            self.host_target = conf.get('HOST_TARGET')
257*6eef5f0cSAntonio Huete Jimenez            for srctop in conf.get('SRCTOPS', []):
2585f1e34d9SAlexandre Perrin                if srctop[-1] != '/':
2595f1e34d9SAlexandre Perrin                    srctop += '/'
2605f1e34d9SAlexandre Perrin                if not srctop in self.srctops:
2615f1e34d9SAlexandre Perrin                    self.srctops.append(srctop)
2625f1e34d9SAlexandre Perrin                _srctop = os.path.realpath(srctop)
2635f1e34d9SAlexandre Perrin                if _srctop[-1] != '/':
2645f1e34d9SAlexandre Perrin                    _srctop += '/'
2655f1e34d9SAlexandre Perrin                if not _srctop in self.srctops:
2665f1e34d9SAlexandre Perrin                    self.srctops.append(_srctop)
2675f1e34d9SAlexandre Perrin
2685f1e34d9SAlexandre Perrin            trim_list = add_trims(self.machine)
2695f1e34d9SAlexandre Perrin            if self.machine == 'host':
2705f1e34d9SAlexandre Perrin                trim_list += add_trims(self.host_target)
271*6eef5f0cSAntonio Huete Jimenez            if self.target_spec != self.machine:
2725f1e34d9SAlexandre Perrin                trim_list += add_trims(self.target_spec)
2735f1e34d9SAlexandre Perrin
274*6eef5f0cSAntonio Huete Jimenez            for objroot in conf.get('OBJROOTS', []):
2755f1e34d9SAlexandre Perrin                for e in trim_list:
2765f1e34d9SAlexandre Perrin                    if objroot.endswith(e):
2775f1e34d9SAlexandre Perrin                        # this is not what we want - fix it
2785f1e34d9SAlexandre Perrin                        objroot = objroot[0:-len(e)]
279ca58f742SDaniel Fojt
280ca58f742SDaniel Fojt                if objroot[-1] != '/':
2815f1e34d9SAlexandre Perrin                    objroot += '/'
2825f1e34d9SAlexandre Perrin                if not objroot in self.objroots:
2835f1e34d9SAlexandre Perrin                    self.objroots.append(objroot)
2845f1e34d9SAlexandre Perrin                    _objroot = os.path.realpath(objroot)
2855f1e34d9SAlexandre Perrin                    if objroot[-1] == '/':
2865f1e34d9SAlexandre Perrin                        _objroot += '/'
2875f1e34d9SAlexandre Perrin                    if not _objroot in self.objroots:
2885f1e34d9SAlexandre Perrin                        self.objroots.append(_objroot)
2895f1e34d9SAlexandre Perrin
2905f1e34d9SAlexandre Perrin            # we want the longest match
2915f1e34d9SAlexandre Perrin            self.srctops.sort(reverse=True)
2925f1e34d9SAlexandre Perrin            self.objroots.sort(reverse=True)
2935f1e34d9SAlexandre Perrin
294*6eef5f0cSAntonio Huete Jimenez            self.excludes = conf.get('EXCLUDES', [])
295f445c897SJohn Marino
2965f1e34d9SAlexandre Perrin            if self.debug:
2976a91b982SJohn Marino                print("host_target=", self.host_target, file=self.debug_out)
2986a91b982SJohn Marino                print("srctops=", self.srctops, file=self.debug_out)
2996a91b982SJohn Marino                print("objroots=", self.objroots, file=self.debug_out)
300f445c897SJohn Marino                print("excludes=", self.excludes, file=self.debug_out)
301*6eef5f0cSAntonio Huete Jimenez                print("ext_list=", self.exts, file=self.debug_out)
3025f1e34d9SAlexandre Perrin
3035f1e34d9SAlexandre Perrin            self.dirdep_re = re.compile(r'([^/]+)/(.+)')
3045f1e34d9SAlexandre Perrin
3055f1e34d9SAlexandre Perrin        if self.dpdeps and not self.reldir:
3065f1e34d9SAlexandre Perrin            if self.debug:
3076a91b982SJohn Marino                print("need reldir:", end=' ', file=self.debug_out)
3085f1e34d9SAlexandre Perrin            if self.curdir:
3095f1e34d9SAlexandre Perrin                srctop = self.find_top(self.curdir, self.srctops)
3105f1e34d9SAlexandre Perrin                if srctop:
3115f1e34d9SAlexandre Perrin                    self.reldir = self.curdir.replace(srctop,'')
3125f1e34d9SAlexandre Perrin                    if self.debug:
3136a91b982SJohn Marino                        print(self.reldir, file=self.debug_out)
3145f1e34d9SAlexandre Perrin            if not self.reldir:
3155f1e34d9SAlexandre Perrin                self.dpdeps = None      # we cannot do it?
3165f1e34d9SAlexandre Perrin
3175f1e34d9SAlexandre Perrin        self.cwd = os.getcwd()          # make sure this is initialized
318f445c897SJohn Marino        self.last_dir = self.cwd
3195f1e34d9SAlexandre Perrin
3205f1e34d9SAlexandre Perrin        if name:
3215f1e34d9SAlexandre Perrin            self.try_parse()
3225f1e34d9SAlexandre Perrin
3235f1e34d9SAlexandre Perrin    def reset(self):
3245f1e34d9SAlexandre Perrin        """reset state if we are being passed meta files from multiple directories."""
3255f1e34d9SAlexandre Perrin        self.seen = {}
3265f1e34d9SAlexandre Perrin        self.obj_deps = []
3275f1e34d9SAlexandre Perrin        self.src_deps = []
3285f1e34d9SAlexandre Perrin        self.file_deps = []
3295f1e34d9SAlexandre Perrin
3305f1e34d9SAlexandre Perrin    def dirdeps(self, sep='\n'):
3315f1e34d9SAlexandre Perrin        """return DIRDEPS"""
3325f1e34d9SAlexandre Perrin        return sep.strip() + sep.join(self.obj_deps)
3335f1e34d9SAlexandre Perrin
3345f1e34d9SAlexandre Perrin    def src_dirdeps(self, sep='\n'):
3355f1e34d9SAlexandre Perrin        """return SRC_DIRDEPS"""
3365f1e34d9SAlexandre Perrin        return sep.strip() + sep.join(self.src_deps)
3375f1e34d9SAlexandre Perrin
3385f1e34d9SAlexandre Perrin    def file_depends(self, out=None):
3395f1e34d9SAlexandre Perrin        """Append DPDEPS_${file} += ${RELDIR}
3405f1e34d9SAlexandre Perrin        for each file we saw, to the output file."""
3415f1e34d9SAlexandre Perrin        if not self.reldir:
3425f1e34d9SAlexandre Perrin            return None
3435f1e34d9SAlexandre Perrin        for f in sort_unique(self.file_deps):
3446a91b982SJohn Marino            print('DPDEPS_%s += %s' % (f, self.reldir), file=out)
345ca58f742SDaniel Fojt        # these entries provide for reverse DIRDEPS lookup
346ca58f742SDaniel Fojt        for f in self.obj_deps:
347ca58f742SDaniel Fojt            print('DEPDIRS_%s += %s' % (f, self.reldir), file=out)
3485f1e34d9SAlexandre Perrin
3495f1e34d9SAlexandre Perrin    def seenit(self, dir):
3505f1e34d9SAlexandre Perrin        """rememer that we have seen dir."""
3515f1e34d9SAlexandre Perrin        self.seen[dir] = 1
3525f1e34d9SAlexandre Perrin
3535f1e34d9SAlexandre Perrin    def add(self, list, data, clue=''):
3545f1e34d9SAlexandre Perrin        """add data to list if it isn't already there."""
3555f1e34d9SAlexandre Perrin        if data not in list:
3565f1e34d9SAlexandre Perrin            list.append(data)
3575f1e34d9SAlexandre Perrin            if self.debug:
3586a91b982SJohn Marino                print("%s: %sAdd: %s" % (self.name, clue, data), file=self.debug_out)
3595f1e34d9SAlexandre Perrin
3605f1e34d9SAlexandre Perrin    def find_top(self, path, list):
3616a91b982SJohn Marino        """the logical tree may be split across multiple trees"""
3625f1e34d9SAlexandre Perrin        for top in list:
3635f1e34d9SAlexandre Perrin            if path.startswith(top):
3645f1e34d9SAlexandre Perrin                if self.debug > 2:
3656a91b982SJohn Marino                    print("found in", top, file=self.debug_out)
3665f1e34d9SAlexandre Perrin                return top
3675f1e34d9SAlexandre Perrin        return None
3685f1e34d9SAlexandre Perrin
3695f1e34d9SAlexandre Perrin    def find_obj(self, objroot, dir, path, input):
3705f1e34d9SAlexandre Perrin        """return path within objroot, taking care of .dirdep files"""
3715f1e34d9SAlexandre Perrin        ddep = None
3725f1e34d9SAlexandre Perrin        for ddepf in [path + '.dirdep', dir + '/.dirdep']:
3735f1e34d9SAlexandre Perrin            if not ddep and os.path.exists(ddepf):
3746a91b982SJohn Marino                ddep = open(ddepf, 'r').readline().strip('# \n')
3755f1e34d9SAlexandre Perrin                if self.debug > 1:
3766a91b982SJohn Marino                    print("found %s: %s\n" % (ddepf, ddep), file=self.debug_out)
377*6eef5f0cSAntonio Huete Jimenez                for e in self.exts:
378*6eef5f0cSAntonio Huete Jimenez                    if ddep.endswith(e):
379*6eef5f0cSAntonio Huete Jimenez                        ddep = ddep[0:-len(e)]
380*6eef5f0cSAntonio Huete Jimenez                        break
3815f1e34d9SAlexandre Perrin
3825f1e34d9SAlexandre Perrin        if not ddep:
3835f1e34d9SAlexandre Perrin            # no .dirdeps, so remember that we've seen the raw input
3845f1e34d9SAlexandre Perrin            self.seenit(input)
3855f1e34d9SAlexandre Perrin            self.seenit(dir)
3865f1e34d9SAlexandre Perrin            if self.machine == 'none':
3875f1e34d9SAlexandre Perrin                if dir.startswith(objroot):
3885f1e34d9SAlexandre Perrin                    return dir.replace(objroot,'')
3895f1e34d9SAlexandre Perrin                return None
3905f1e34d9SAlexandre Perrin            m = self.dirdep_re.match(dir.replace(objroot,''))
3915f1e34d9SAlexandre Perrin            if m:
3925f1e34d9SAlexandre Perrin                ddep = m.group(2)
3935f1e34d9SAlexandre Perrin                dmachine = m.group(1)
3945f1e34d9SAlexandre Perrin                if dmachine != self.machine:
3955f1e34d9SAlexandre Perrin                    if not (self.machine == 'host' and
3965f1e34d9SAlexandre Perrin                            dmachine == self.host_target):
3975f1e34d9SAlexandre Perrin                        if self.debug > 2:
3986a91b982SJohn Marino                            print("adding .%s to %s" % (dmachine, ddep), file=self.debug_out)
3995f1e34d9SAlexandre Perrin                        ddep += '.' + dmachine
4005f1e34d9SAlexandre Perrin
4015f1e34d9SAlexandre Perrin        return ddep
4025f1e34d9SAlexandre Perrin
4035f1e34d9SAlexandre Perrin    def try_parse(self, name=None, file=None):
4045f1e34d9SAlexandre Perrin        """give file and line number causing exception"""
4055f1e34d9SAlexandre Perrin        try:
4065f1e34d9SAlexandre Perrin            self.parse(name, file)
4075f1e34d9SAlexandre Perrin        except:
4085f1e34d9SAlexandre Perrin            # give a useful clue
4096a91b982SJohn Marino            print('{}:{}: '.format(self.name, self.line), end=' ', file=sys.stderr)
4105f1e34d9SAlexandre Perrin            raise
4115f1e34d9SAlexandre Perrin
4125f1e34d9SAlexandre Perrin    def parse(self, name=None, file=None):
4135f1e34d9SAlexandre Perrin        """A meta file looks like:
4145f1e34d9SAlexandre Perrin
4155f1e34d9SAlexandre Perrin        # Meta data file "path"
4165f1e34d9SAlexandre Perrin        CMD "command-line"
4175f1e34d9SAlexandre Perrin        CWD "cwd"
4185f1e34d9SAlexandre Perrin        TARGET "target"
4195f1e34d9SAlexandre Perrin        -- command output --
4205f1e34d9SAlexandre Perrin        -- filemon acquired metadata --
4215f1e34d9SAlexandre Perrin        # buildmon version 3
4225f1e34d9SAlexandre Perrin        V 3
4235f1e34d9SAlexandre Perrin        C "pid" "cwd"
4245f1e34d9SAlexandre Perrin        E "pid" "path"
4255f1e34d9SAlexandre Perrin        F "pid" "child"
4265f1e34d9SAlexandre Perrin        R "pid" "path"
4275f1e34d9SAlexandre Perrin        W "pid" "path"
4285f1e34d9SAlexandre Perrin        X "pid" "status"
4295f1e34d9SAlexandre Perrin        D "pid" "path"
4305f1e34d9SAlexandre Perrin        L "pid" "src" "target"
4315f1e34d9SAlexandre Perrin        M "pid" "old" "new"
4325f1e34d9SAlexandre Perrin        S "pid" "path"
4335f1e34d9SAlexandre Perrin        # Bye bye
4345f1e34d9SAlexandre Perrin
4355f1e34d9SAlexandre Perrin        We go to some effort to avoid processing a dependency more than once.
4365f1e34d9SAlexandre Perrin        Of the above record types only C,E,F,L,R,V and W are of interest.
4375f1e34d9SAlexandre Perrin        """
4385f1e34d9SAlexandre Perrin
4395f1e34d9SAlexandre Perrin        version = 0                     # unknown
4405f1e34d9SAlexandre Perrin        if name:
4415f1e34d9SAlexandre Perrin            self.name = name;
4425f1e34d9SAlexandre Perrin        if file:
4435f1e34d9SAlexandre Perrin            f = file
444f445c897SJohn Marino            cwd = self.last_dir = self.cwd
4455f1e34d9SAlexandre Perrin        else:
4466a91b982SJohn Marino            f = open(self.name, 'r')
4475f1e34d9SAlexandre Perrin        skip = True
4485f1e34d9SAlexandre Perrin        pid_cwd = {}
4495f1e34d9SAlexandre Perrin        pid_last_dir = {}
4505f1e34d9SAlexandre Perrin        last_pid = 0
4515f1e34d9SAlexandre Perrin
4525f1e34d9SAlexandre Perrin        self.line = 0
4535f1e34d9SAlexandre Perrin        if self.curdir:
4545f1e34d9SAlexandre Perrin            self.seenit(self.curdir)    # we ignore this
4555f1e34d9SAlexandre Perrin
456*6eef5f0cSAntonio Huete Jimenez        interesting = 'CEFLRVX'
4575f1e34d9SAlexandre Perrin        for line in f:
4585f1e34d9SAlexandre Perrin            self.line += 1
4595f1e34d9SAlexandre Perrin            # ignore anything we don't care about
4605f1e34d9SAlexandre Perrin            if not line[0] in interesting:
4615f1e34d9SAlexandre Perrin                continue
4625f1e34d9SAlexandre Perrin            if self.debug > 2:
4636a91b982SJohn Marino                print("input:", line, end=' ', file=self.debug_out)
4645f1e34d9SAlexandre Perrin            w = line.split()
4655f1e34d9SAlexandre Perrin
4665f1e34d9SAlexandre Perrin            if skip:
4675f1e34d9SAlexandre Perrin                if w[0] == 'V':
4685f1e34d9SAlexandre Perrin                    skip = False
4695f1e34d9SAlexandre Perrin                    version = int(w[1])
4705f1e34d9SAlexandre Perrin                    """
4715f1e34d9SAlexandre Perrin                    if version < 4:
4725f1e34d9SAlexandre Perrin                        # we cannot ignore 'W' records
4735f1e34d9SAlexandre Perrin                        # as they may be 'rw'
4745f1e34d9SAlexandre Perrin                        interesting += 'W'
4755f1e34d9SAlexandre Perrin                    """
4765f1e34d9SAlexandre Perrin                elif w[0] == 'CWD':
477f445c897SJohn Marino                    self.cwd = cwd = self.last_dir = w[1]
4785f1e34d9SAlexandre Perrin                    self.seenit(cwd)    # ignore this
4795f1e34d9SAlexandre Perrin                    if self.debug:
4806a91b982SJohn Marino                        print("%s: CWD=%s" % (self.name, cwd), file=self.debug_out)
4815f1e34d9SAlexandre Perrin                continue
4825f1e34d9SAlexandre Perrin
4835f1e34d9SAlexandre Perrin            pid = int(w[1])
4845f1e34d9SAlexandre Perrin            if pid != last_pid:
4855f1e34d9SAlexandre Perrin                if last_pid:
486f445c897SJohn Marino                    pid_last_dir[last_pid] = self.last_dir
487*6eef5f0cSAntonio Huete Jimenez                cwd = pid_cwd.get(pid, self.cwd)
488*6eef5f0cSAntonio Huete Jimenez                self.last_dir = pid_last_dir.get(pid, self.cwd)
4895f1e34d9SAlexandre Perrin                last_pid = pid
4905f1e34d9SAlexandre Perrin
4915f1e34d9SAlexandre Perrin            # process operations
4925f1e34d9SAlexandre Perrin            if w[0] == 'F':
4935f1e34d9SAlexandre Perrin                npid = int(w[2])
4945f1e34d9SAlexandre Perrin                pid_cwd[npid] = cwd
4955f1e34d9SAlexandre Perrin                pid_last_dir[npid] = cwd
4965f1e34d9SAlexandre Perrin                last_pid = npid
4975f1e34d9SAlexandre Perrin                continue
4985f1e34d9SAlexandre Perrin            elif w[0] == 'C':
4995f1e34d9SAlexandre Perrin                cwd = abspath(w[2], cwd, None, self.debug, self.debug_out)
500ca58f742SDaniel Fojt                if not cwd:
501ca58f742SDaniel Fojt                    cwd = w[2]
502ca58f742SDaniel Fojt                    if self.debug > 1:
503ca58f742SDaniel Fojt                        print("missing cwd=", cwd, file=self.debug_out)
5045f1e34d9SAlexandre Perrin                if cwd.endswith('/.'):
5055f1e34d9SAlexandre Perrin                    cwd = cwd[0:-2]
506ca58f742SDaniel Fojt                self.last_dir = pid_last_dir[pid] = cwd
507ca58f742SDaniel Fojt                pid_cwd[pid] = cwd
5085f1e34d9SAlexandre Perrin                if self.debug > 1:
5096a91b982SJohn Marino                    print("cwd=", cwd, file=self.debug_out)
5105f1e34d9SAlexandre Perrin                continue
5115f1e34d9SAlexandre Perrin
512*6eef5f0cSAntonio Huete Jimenez            if w[0] == 'X':
513*6eef5f0cSAntonio Huete Jimenez                try:
514*6eef5f0cSAntonio Huete Jimenez                    del self.pids[pid]
515*6eef5f0cSAntonio Huete Jimenez                except KeyError:
516*6eef5f0cSAntonio Huete Jimenez                    pass
517*6eef5f0cSAntonio Huete Jimenez                continue
518*6eef5f0cSAntonio Huete Jimenez
5195f1e34d9SAlexandre Perrin            if w[2] in self.seen:
5205f1e34d9SAlexandre Perrin                if self.debug > 2:
5216a91b982SJohn Marino                    print("seen:", w[2], file=self.debug_out)
5225f1e34d9SAlexandre Perrin                continue
5235f1e34d9SAlexandre Perrin            # file operations
5245f1e34d9SAlexandre Perrin            if w[0] in 'ML':
525f445c897SJohn Marino                # these are special, tread src as read and
526f445c897SJohn Marino                # target as write
527ca58f742SDaniel Fojt                self.parse_path(w[2].strip("'"), cwd, 'R', w)
528ca58f742SDaniel Fojt                self.parse_path(w[3].strip("'"), cwd, 'W', w)
529f445c897SJohn Marino                continue
530f445c897SJohn Marino            elif w[0] in 'ERWS':
5315f1e34d9SAlexandre Perrin                path = w[2]
532*6eef5f0cSAntonio Huete Jimenez                if w[0] == 'E':
533*6eef5f0cSAntonio Huete Jimenez                    self.pids[pid] = path
534*6eef5f0cSAntonio Huete Jimenez                elif path == '.':
535a34d5fb1SAntonio Huete Jimenez                    continue
536f445c897SJohn Marino                self.parse_path(path, cwd, w[0], w)
537f445c897SJohn Marino
538a34d5fb1SAntonio Huete Jimenez        assert(version > 0)
539*6eef5f0cSAntonio Huete Jimenez        setid_pids = []
540*6eef5f0cSAntonio Huete Jimenez        # self.pids should be empty!
541*6eef5f0cSAntonio Huete Jimenez        for pid,path in self.pids.items():
542*6eef5f0cSAntonio Huete Jimenez            try:
543*6eef5f0cSAntonio Huete Jimenez                # no guarantee that path is still valid
544*6eef5f0cSAntonio Huete Jimenez                if os.stat(path).st_mode & (stat.S_ISUID|stat.S_ISGID):
545*6eef5f0cSAntonio Huete Jimenez                    # we do not expect anything after Exec
546*6eef5f0cSAntonio Huete Jimenez                    setid_pids.append(pid)
547*6eef5f0cSAntonio Huete Jimenez                    continue
548*6eef5f0cSAntonio Huete Jimenez            except:
549*6eef5f0cSAntonio Huete Jimenez                # we do not care why the above fails,
550*6eef5f0cSAntonio Huete Jimenez                # we do not want to miss the ERROR below.
551*6eef5f0cSAntonio Huete Jimenez                pass
552*6eef5f0cSAntonio Huete Jimenez            print("ERROR: missing eXit for {} pid {}".format(path, pid))
553*6eef5f0cSAntonio Huete Jimenez        for pid in setid_pids:
554*6eef5f0cSAntonio Huete Jimenez            del self.pids[pid]
555*6eef5f0cSAntonio Huete Jimenez        assert(len(self.pids) == 0)
556f445c897SJohn Marino        if not file:
557f445c897SJohn Marino            f.close()
558f445c897SJohn Marino
559ca58f742SDaniel Fojt    def is_src(self, base, dir, rdir):
560ca58f742SDaniel Fojt        """is base in srctop"""
561ca58f742SDaniel Fojt        for dir in [dir,rdir]:
562ca58f742SDaniel Fojt            if not dir:
563ca58f742SDaniel Fojt                continue
564ca58f742SDaniel Fojt            path = '/'.join([dir,base])
565ca58f742SDaniel Fojt            srctop = self.find_top(path, self.srctops)
566ca58f742SDaniel Fojt            if srctop:
567ca58f742SDaniel Fojt                if self.dpdeps:
568ca58f742SDaniel Fojt                    self.add(self.file_deps, path.replace(srctop,''), 'file')
569ca58f742SDaniel Fojt                self.add(self.src_deps, dir.replace(srctop,''), 'src')
570ca58f742SDaniel Fojt                self.seenit(dir)
571ca58f742SDaniel Fojt                return True
572ca58f742SDaniel Fojt        return False
573ca58f742SDaniel Fojt
574f445c897SJohn Marino    def parse_path(self, path, cwd, op=None, w=[]):
575f445c897SJohn Marino        """look at a path for the op specified"""
576f445c897SJohn Marino
577f445c897SJohn Marino        if not op:
578f445c897SJohn Marino            op = w[0]
579f445c897SJohn Marino
5805f1e34d9SAlexandre Perrin        # we are never interested in .dirdep files as dependencies
5815f1e34d9SAlexandre Perrin        if path.endswith('.dirdep'):
582f445c897SJohn Marino            return
583f445c897SJohn Marino        for p in self.excludes:
584f445c897SJohn Marino            if p and path.startswith(p):
585f445c897SJohn Marino                if self.debug > 2:
586f445c897SJohn Marino                    print("exclude:", p, path, file=self.debug_out)
587f445c897SJohn Marino                return
5885f1e34d9SAlexandre Perrin        # we don't want to resolve the last component if it is
5895f1e34d9SAlexandre Perrin        # a symlink
590f445c897SJohn Marino        path = resolve(path, cwd, self.last_dir, self.debug, self.debug_out)
5915f1e34d9SAlexandre Perrin        if not path:
592f445c897SJohn Marino            return
5935f1e34d9SAlexandre Perrin        dir,base = os.path.split(path)
5945f1e34d9SAlexandre Perrin        if dir in self.seen:
5955f1e34d9SAlexandre Perrin            if self.debug > 2:
5966a91b982SJohn Marino                print("seen:", dir, file=self.debug_out)
597f445c897SJohn Marino            return
5985f1e34d9SAlexandre Perrin        # we can have a path in an objdir which is a link
5995f1e34d9SAlexandre Perrin        # to the src dir, we may need to add dependencies for each
6005f1e34d9SAlexandre Perrin        rdir = dir
601f445c897SJohn Marino        dir = abspath(dir, cwd, self.last_dir, self.debug, self.debug_out)
602*6eef5f0cSAntonio Huete Jimenez        if dir:
603ca58f742SDaniel Fojt            rdir = os.path.realpath(dir)
604*6eef5f0cSAntonio Huete Jimenez        else:
605*6eef5f0cSAntonio Huete Jimenez            dir = rdir
606ca58f742SDaniel Fojt        if rdir == dir:
6075f1e34d9SAlexandre Perrin            rdir = None
6085f1e34d9SAlexandre Perrin        # now put path back together
6095f1e34d9SAlexandre Perrin        path = '/'.join([dir,base])
6105f1e34d9SAlexandre Perrin        if self.debug > 1:
6116a91b982SJohn Marino            print("raw=%s rdir=%s dir=%s path=%s" % (w[2], rdir, dir, path), file=self.debug_out)
612f445c897SJohn Marino        if op in 'RWS':
613f445c897SJohn Marino            if path in [self.last_dir, cwd, self.cwd, self.curdir]:
6145f1e34d9SAlexandre Perrin                if self.debug > 1:
6156a91b982SJohn Marino                    print("skipping:", path, file=self.debug_out)
616f445c897SJohn Marino                return
6175f1e34d9SAlexandre Perrin            if os.path.isdir(path):
618f445c897SJohn Marino                if op in 'RW':
619f445c897SJohn Marino                    self.last_dir = path;
6205f1e34d9SAlexandre Perrin                if self.debug > 1:
621f445c897SJohn Marino                    print("ldir=", self.last_dir, file=self.debug_out)
622f445c897SJohn Marino                return
6235f1e34d9SAlexandre Perrin
624ca58f742SDaniel Fojt        if op in 'ER':
6255f1e34d9SAlexandre Perrin            # finally, we get down to it
6265f1e34d9SAlexandre Perrin            if dir == self.cwd or dir == self.curdir:
627f445c897SJohn Marino                return
628ca58f742SDaniel Fojt            if self.is_src(base, dir, rdir):
6295f1e34d9SAlexandre Perrin                self.seenit(w[2])
630ca58f742SDaniel Fojt                if not rdir:
631f445c897SJohn Marino                    return
6325f1e34d9SAlexandre Perrin
6335f1e34d9SAlexandre Perrin            objroot = None
6345f1e34d9SAlexandre Perrin            for dir in [dir,rdir]:
6355f1e34d9SAlexandre Perrin                if not dir:
6365f1e34d9SAlexandre Perrin                    continue
6375f1e34d9SAlexandre Perrin                objroot = self.find_top(dir, self.objroots)
6385f1e34d9SAlexandre Perrin                if objroot:
6395f1e34d9SAlexandre Perrin                    break
6405f1e34d9SAlexandre Perrin            if objroot:
6415f1e34d9SAlexandre Perrin                ddep = self.find_obj(objroot, dir, path, w[2])
6425f1e34d9SAlexandre Perrin                if ddep:
6435f1e34d9SAlexandre Perrin                    self.add(self.obj_deps, ddep, 'obj')
644ca58f742SDaniel Fojt                    if self.dpdeps and objroot.endswith('/stage/'):
645ca58f742SDaniel Fojt                        sp = '/'.join(path.replace(objroot,'').split('/')[1:])
646ca58f742SDaniel Fojt                        self.add(self.file_deps, sp, 'file')
6475f1e34d9SAlexandre Perrin            else:
6485f1e34d9SAlexandre Perrin                # don't waste time looking again
6495f1e34d9SAlexandre Perrin                self.seenit(w[2])
6505f1e34d9SAlexandre Perrin                self.seenit(dir)
6515f1e34d9SAlexandre Perrin
6525f1e34d9SAlexandre Perrin
6535f1e34d9SAlexandre Perrindef main(argv, klass=MetaFile, xopts='', xoptf=None):
6545f1e34d9SAlexandre Perrin    """Simple driver for class MetaFile.
6555f1e34d9SAlexandre Perrin
6565f1e34d9SAlexandre Perrin    Usage:
6575f1e34d9SAlexandre Perrin        script [options] [key=value ...] "meta" ...
6585f1e34d9SAlexandre Perrin
6595f1e34d9SAlexandre Perrin    Options and key=value pairs contribute to the
6605f1e34d9SAlexandre Perrin    dictionary passed to MetaFile.
6615f1e34d9SAlexandre Perrin
6625f1e34d9SAlexandre Perrin    -S "SRCTOP"
6635f1e34d9SAlexandre Perrin                add "SRCTOP" to the "SRCTOPS" list.
6645f1e34d9SAlexandre Perrin
6655f1e34d9SAlexandre Perrin    -C "CURDIR"
6665f1e34d9SAlexandre Perrin
6675f1e34d9SAlexandre Perrin    -O "OBJROOT"
6685f1e34d9SAlexandre Perrin                add "OBJROOT" to the "OBJROOTS" list.
6695f1e34d9SAlexandre Perrin
6705f1e34d9SAlexandre Perrin    -m "MACHINE"
6715f1e34d9SAlexandre Perrin
6725f1e34d9SAlexandre Perrin    -a "MACHINE_ARCH"
6735f1e34d9SAlexandre Perrin
6745f1e34d9SAlexandre Perrin    -H "HOST_TARGET"
6755f1e34d9SAlexandre Perrin
6765f1e34d9SAlexandre Perrin    -D "DPDEPS"
6775f1e34d9SAlexandre Perrin
6785f1e34d9SAlexandre Perrin    -d  bumps debug level
6795f1e34d9SAlexandre Perrin
6805f1e34d9SAlexandre Perrin    """
6815f1e34d9SAlexandre Perrin    import getopt
6825f1e34d9SAlexandre Perrin
6835f1e34d9SAlexandre Perrin    # import Psyco if we can
6845f1e34d9SAlexandre Perrin    # it can speed things up quite a bit
6855f1e34d9SAlexandre Perrin    have_psyco = 0
6865f1e34d9SAlexandre Perrin    try:
6875f1e34d9SAlexandre Perrin        import psyco
6885f1e34d9SAlexandre Perrin        psyco.full()
6895f1e34d9SAlexandre Perrin        have_psyco = 1
6905f1e34d9SAlexandre Perrin    except:
6915f1e34d9SAlexandre Perrin        pass
6925f1e34d9SAlexandre Perrin
6935f1e34d9SAlexandre Perrin    conf = {
6945f1e34d9SAlexandre Perrin        'SRCTOPS': [],
6955f1e34d9SAlexandre Perrin        'OBJROOTS': [],
696f445c897SJohn Marino        'EXCLUDES': [],
6975f1e34d9SAlexandre Perrin        }
6985f1e34d9SAlexandre Perrin
6995f1e34d9SAlexandre Perrin    try:
7005f1e34d9SAlexandre Perrin        machine = os.environ['MACHINE']
7015f1e34d9SAlexandre Perrin        if machine:
7025f1e34d9SAlexandre Perrin            conf['MACHINE'] = machine
7035f1e34d9SAlexandre Perrin        machine_arch = os.environ['MACHINE_ARCH']
7045f1e34d9SAlexandre Perrin        if machine_arch:
7055f1e34d9SAlexandre Perrin            conf['MACHINE_ARCH'] = machine_arch
7065f1e34d9SAlexandre Perrin        srctop = os.environ['SB_SRC']
7075f1e34d9SAlexandre Perrin        if srctop:
7085f1e34d9SAlexandre Perrin            conf['SRCTOPS'].append(srctop)
7095f1e34d9SAlexandre Perrin        objroot = os.environ['SB_OBJROOT']
7105f1e34d9SAlexandre Perrin        if objroot:
7115f1e34d9SAlexandre Perrin            conf['OBJROOTS'].append(objroot)
7125f1e34d9SAlexandre Perrin    except:
7135f1e34d9SAlexandre Perrin        pass
7145f1e34d9SAlexandre Perrin
7155f1e34d9SAlexandre Perrin    debug = 0
7165f1e34d9SAlexandre Perrin    output = True
7175f1e34d9SAlexandre Perrin
718f445c897SJohn Marino    opts, args = getopt.getopt(argv[1:], 'a:dS:C:O:R:m:D:H:qT:X:' + xopts)
7195f1e34d9SAlexandre Perrin    for o, a in opts:
7205f1e34d9SAlexandre Perrin        if o == '-a':
7215f1e34d9SAlexandre Perrin            conf['MACHINE_ARCH'] = a
7225f1e34d9SAlexandre Perrin        elif o == '-d':
7235f1e34d9SAlexandre Perrin            debug += 1
7245f1e34d9SAlexandre Perrin        elif o == '-q':
7255f1e34d9SAlexandre Perrin            output = False
7265f1e34d9SAlexandre Perrin        elif o == '-H':
7275f1e34d9SAlexandre Perrin            conf['HOST_TARGET'] = a
7285f1e34d9SAlexandre Perrin        elif o == '-S':
7295f1e34d9SAlexandre Perrin            if a not in conf['SRCTOPS']:
7305f1e34d9SAlexandre Perrin                conf['SRCTOPS'].append(a)
7315f1e34d9SAlexandre Perrin        elif o == '-C':
7325f1e34d9SAlexandre Perrin            conf['CURDIR'] = a
7335f1e34d9SAlexandre Perrin        elif o == '-O':
7345f1e34d9SAlexandre Perrin            if a not in conf['OBJROOTS']:
7355f1e34d9SAlexandre Perrin                conf['OBJROOTS'].append(a)
7365f1e34d9SAlexandre Perrin        elif o == '-R':
7375f1e34d9SAlexandre Perrin            conf['RELDIR'] = a
7385f1e34d9SAlexandre Perrin        elif o == '-D':
7395f1e34d9SAlexandre Perrin            conf['DPDEPS'] = a
7405f1e34d9SAlexandre Perrin        elif o == '-m':
7415f1e34d9SAlexandre Perrin            conf['MACHINE'] = a
7425f1e34d9SAlexandre Perrin        elif o == '-T':
7435f1e34d9SAlexandre Perrin            conf['TARGET_SPEC'] = a
744f445c897SJohn Marino        elif o == '-X':
745f445c897SJohn Marino            if a not in conf['EXCLUDES']:
746f445c897SJohn Marino                conf['EXCLUDES'].append(a)
7475f1e34d9SAlexandre Perrin        elif xoptf:
7485f1e34d9SAlexandre Perrin            xoptf(o, a, conf)
7495f1e34d9SAlexandre Perrin
7505f1e34d9SAlexandre Perrin    conf['debug'] = debug
7515f1e34d9SAlexandre Perrin
7525f1e34d9SAlexandre Perrin    # get any var=val assignments
7535f1e34d9SAlexandre Perrin    eaten = []
7545f1e34d9SAlexandre Perrin    for a in args:
7555f1e34d9SAlexandre Perrin        if a.find('=') > 0:
7565f1e34d9SAlexandre Perrin            k,v = a.split('=')
7575f1e34d9SAlexandre Perrin            if k in ['SRCTOP','OBJROOT','SRCTOPS','OBJROOTS']:
7585f1e34d9SAlexandre Perrin                if k == 'SRCTOP':
7595f1e34d9SAlexandre Perrin                    k = 'SRCTOPS'
7605f1e34d9SAlexandre Perrin                elif k == 'OBJROOT':
7615f1e34d9SAlexandre Perrin                    k = 'OBJROOTS'
7625f1e34d9SAlexandre Perrin                if v not in conf[k]:
7635f1e34d9SAlexandre Perrin                    conf[k].append(v)
7645f1e34d9SAlexandre Perrin            else:
7655f1e34d9SAlexandre Perrin                conf[k] = v
7665f1e34d9SAlexandre Perrin            eaten.append(a)
7675f1e34d9SAlexandre Perrin            continue
7685f1e34d9SAlexandre Perrin        break
7695f1e34d9SAlexandre Perrin
7705f1e34d9SAlexandre Perrin    for a in eaten:
7715f1e34d9SAlexandre Perrin        args.remove(a)
7725f1e34d9SAlexandre Perrin
773*6eef5f0cSAntonio Huete Jimenez    debug_out = conf.get('debug_out', sys.stderr)
7745f1e34d9SAlexandre Perrin
7755f1e34d9SAlexandre Perrin    if debug:
7766a91b982SJohn Marino        print("config:", file=debug_out)
7776a91b982SJohn Marino        print("psyco=", have_psyco, file=debug_out)
7786a91b982SJohn Marino        for k,v in list(conf.items()):
7796a91b982SJohn Marino            print("%s=%s" % (k,v), file=debug_out)
7805f1e34d9SAlexandre Perrin
781f445c897SJohn Marino    m = None
7825f1e34d9SAlexandre Perrin    for a in args:
7835f1e34d9SAlexandre Perrin        if a.endswith('.meta'):
784f445c897SJohn Marino            if not os.path.exists(a):
785f445c897SJohn Marino                continue
7865f1e34d9SAlexandre Perrin            m = klass(a, conf)
7875f1e34d9SAlexandre Perrin        elif a.startswith('@'):
7885f1e34d9SAlexandre Perrin            # there can actually multiple files per line
7895f1e34d9SAlexandre Perrin            for line in open(a[1:]):
7905f1e34d9SAlexandre Perrin                for f in line.strip().split():
791f445c897SJohn Marino                    if not os.path.exists(f):
792f445c897SJohn Marino                        continue
7935f1e34d9SAlexandre Perrin                    m = klass(f, conf)
7945f1e34d9SAlexandre Perrin
795f445c897SJohn Marino    if output and m:
7966a91b982SJohn Marino        print(m.dirdeps())
7975f1e34d9SAlexandre Perrin
7986a91b982SJohn Marino        print(m.src_dirdeps('\nsrc:'))
7995f1e34d9SAlexandre Perrin
800*6eef5f0cSAntonio Huete Jimenez        dpdeps = conf.get('DPDEPS')
8015f1e34d9SAlexandre Perrin        if dpdeps:
802*6eef5f0cSAntonio Huete Jimenez            m.file_depends(open(dpdeps, 'w'))
8035f1e34d9SAlexandre Perrin
8045f1e34d9SAlexandre Perrin    return m
8055f1e34d9SAlexandre Perrin
8065f1e34d9SAlexandre Perrinif __name__ == '__main__':
8075f1e34d9SAlexandre Perrin    try:
8085f1e34d9SAlexandre Perrin        main(sys.argv)
8095f1e34d9SAlexandre Perrin    except:
8105f1e34d9SAlexandre Perrin        # yes, this goes to stdout
8116a91b982SJohn Marino        print("ERROR: ", sys.exc_info()[1])
8125f1e34d9SAlexandre Perrin        raise
8135f1e34d9SAlexandre Perrin
814