meta2deps.py revision 281812
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: 40281812Ssjg $Id: meta2deps.py,v 1.18 2015/04/03 18:23:25 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 115281812Ssjg if (path.find('/') < 0 or 116281812Ssjg path.find('./') > 0 or 117253883Ssjg path.endswith('/..') or 118253883Ssjg os.path.islink(path)): 119246149Ssjg return os.path.realpath(path) 120246149Ssjg return path 121246149Ssjg 122246149Ssjgdef sort_unique(list, cmp=None, key=None, reverse=False): 123246149Ssjg list.sort(cmp, key, reverse) 124246149Ssjg nl = [] 125246149Ssjg le = None 126246149Ssjg for e in list: 127246149Ssjg if e == le: 128246149Ssjg continue 129246149Ssjg nl.append(e) 130246149Ssjg return nl 131246149Ssjg 132250837Ssjgdef add_trims(x): 133250837Ssjg return ['/' + x + '/', 134250837Ssjg '/' + x, 135250837Ssjg x + '/', 136250837Ssjg x] 137250837Ssjg 138246149Ssjgclass MetaFile: 139246149Ssjg """class to parse meta files generated by bmake.""" 140246149Ssjg 141246149Ssjg conf = None 142246149Ssjg dirdep_re = None 143246149Ssjg host_target = None 144246149Ssjg srctops = [] 145246149Ssjg objroots = [] 146281812Ssjg excludes = [] 147246149Ssjg seen = {} 148246149Ssjg obj_deps = [] 149246149Ssjg src_deps = [] 150246149Ssjg file_deps = [] 151246149Ssjg 152246149Ssjg def __init__(self, name, conf={}): 153246149Ssjg """if name is set we will parse it now. 154246149Ssjg conf can have the follwing keys: 155246149Ssjg 156246149Ssjg SRCTOPS list of tops of the src tree(s). 157246149Ssjg 158246149Ssjg CURDIR the src directory 'bmake' was run from. 159246149Ssjg 160246149Ssjg RELDIR the relative path from SRCTOP to CURDIR 161246149Ssjg 162246149Ssjg MACHINE the machine we built for. 163246149Ssjg set to 'none' if we are not cross-building. 164249033Ssjg More specifically if machine cannot be deduced from objdirs. 165246149Ssjg 166250837Ssjg TARGET_SPEC 167250837Ssjg Sometimes MACHINE isn't enough. 168250837Ssjg 169246149Ssjg HOST_TARGET 170268437Ssjg when we build for the pseudo machine 'host' 171246149Ssjg the object tree uses HOST_TARGET rather than MACHINE. 172246149Ssjg 173246149Ssjg OBJROOTS a list of the common prefix for all obj dirs it might 174246149Ssjg end in '/' or '-'. 175246149Ssjg 176246149Ssjg DPDEPS names an optional file to which per file dependencies 177246149Ssjg will be appended. 178246149Ssjg For example if 'some/path/foo.h' is read from SRCTOP 179246149Ssjg then 'DPDEPS_some/path/foo.h +=' "RELDIR" is output. 180246149Ssjg This can allow 'bmake' to learn all the dirs within 181246149Ssjg the tree that depend on 'foo.h' 182246149Ssjg 183281812Ssjg EXCLUDES 184281812Ssjg A list of paths to ignore. 185281812Ssjg ccache(1) can otherwise be trouble. 186281812Ssjg 187246149Ssjg debug desired debug level 188246149Ssjg 189246149Ssjg debug_out open file to send debug output to (sys.stderr) 190246149Ssjg 191246149Ssjg """ 192246149Ssjg 193246149Ssjg self.name = name 194246149Ssjg self.debug = getv(conf, 'debug', 0) 195246149Ssjg self.debug_out = getv(conf, 'debug_out', sys.stderr) 196246149Ssjg 197249033Ssjg self.machine = getv(conf, 'MACHINE', '') 198250837Ssjg self.machine_arch = getv(conf, 'MACHINE_ARCH', '') 199250837Ssjg self.target_spec = getv(conf, 'TARGET_SPEC', '') 200249033Ssjg self.curdir = getv(conf, 'CURDIR') 201249033Ssjg self.reldir = getv(conf, 'RELDIR') 202249033Ssjg self.dpdeps = getv(conf, 'DPDEPS') 203253883Ssjg self.line = 0 204249033Ssjg 205246149Ssjg if not self.conf: 206246149Ssjg # some of the steps below we want to do only once 207246149Ssjg self.conf = conf 208246149Ssjg self.host_target = getv(conf, 'HOST_TARGET') 209246149Ssjg for srctop in getv(conf, 'SRCTOPS', []): 210246149Ssjg if srctop[-1] != '/': 211246149Ssjg srctop += '/' 212246149Ssjg if not srctop in self.srctops: 213246149Ssjg self.srctops.append(srctop) 214246149Ssjg _srctop = os.path.realpath(srctop) 215246149Ssjg if _srctop[-1] != '/': 216246149Ssjg _srctop += '/' 217246149Ssjg if not _srctop in self.srctops: 218246149Ssjg self.srctops.append(_srctop) 219246149Ssjg 220250837Ssjg trim_list = add_trims(self.machine) 221249033Ssjg if self.machine == 'host': 222250837Ssjg trim_list += add_trims(self.host_target) 223250837Ssjg if self.target_spec: 224250837Ssjg trim_list += add_trims(self.target_spec) 225249033Ssjg 226246149Ssjg for objroot in getv(conf, 'OBJROOTS', []): 227249033Ssjg for e in trim_list: 228249033Ssjg if objroot.endswith(e): 229249033Ssjg # this is not what we want - fix it 230249033Ssjg objroot = objroot[0:-len(e)] 231249033Ssjg if e.endswith('/'): 232249033Ssjg objroot += '/' 233246149Ssjg if not objroot in self.objroots: 234246149Ssjg self.objroots.append(objroot) 235246149Ssjg _objroot = os.path.realpath(objroot) 236246149Ssjg if objroot[-1] == '/': 237246149Ssjg _objroot += '/' 238246149Ssjg if not _objroot in self.objroots: 239246149Ssjg self.objroots.append(_objroot) 240246149Ssjg 241249033Ssjg # we want the longest match 242249033Ssjg self.srctops.sort(reverse=True) 243249033Ssjg self.objroots.sort(reverse=True) 244281812Ssjg 245281812Ssjg self.excludes = getv(conf, 'EXCLUDES', []) 246281812Ssjg 247246149Ssjg if self.debug: 248261212Ssjg print("host_target=", self.host_target, file=self.debug_out) 249261212Ssjg print("srctops=", self.srctops, file=self.debug_out) 250261212Ssjg print("objroots=", self.objroots, file=self.debug_out) 251281812Ssjg print("excludes=", self.excludes, file=self.debug_out) 252246149Ssjg 253246149Ssjg self.dirdep_re = re.compile(r'([^/]+)/(.+)') 254246149Ssjg 255246149Ssjg if self.dpdeps and not self.reldir: 256246149Ssjg if self.debug: 257261212Ssjg print("need reldir:", end=' ', file=self.debug_out) 258246149Ssjg if self.curdir: 259246149Ssjg srctop = self.find_top(self.curdir, self.srctops) 260246149Ssjg if srctop: 261246149Ssjg self.reldir = self.curdir.replace(srctop,'') 262246149Ssjg if self.debug: 263261212Ssjg print(self.reldir, file=self.debug_out) 264246149Ssjg if not self.reldir: 265246149Ssjg self.dpdeps = None # we cannot do it? 266246149Ssjg 267249033Ssjg self.cwd = os.getcwd() # make sure this is initialized 268281812Ssjg self.last_dir = self.cwd 269249033Ssjg 270246149Ssjg if name: 271253883Ssjg self.try_parse() 272246149Ssjg 273246149Ssjg def reset(self): 274246149Ssjg """reset state if we are being passed meta files from multiple directories.""" 275246149Ssjg self.seen = {} 276246149Ssjg self.obj_deps = [] 277246149Ssjg self.src_deps = [] 278246149Ssjg self.file_deps = [] 279246149Ssjg 280246149Ssjg def dirdeps(self, sep='\n'): 281246149Ssjg """return DIRDEPS""" 282246149Ssjg return sep.strip() + sep.join(self.obj_deps) 283246149Ssjg 284246149Ssjg def src_dirdeps(self, sep='\n'): 285246149Ssjg """return SRC_DIRDEPS""" 286246149Ssjg return sep.strip() + sep.join(self.src_deps) 287246149Ssjg 288246149Ssjg def file_depends(self, out=None): 289246149Ssjg """Append DPDEPS_${file} += ${RELDIR} 290246149Ssjg for each file we saw, to the output file.""" 291246149Ssjg if not self.reldir: 292246149Ssjg return None 293246149Ssjg for f in sort_unique(self.file_deps): 294261212Ssjg print('DPDEPS_%s += %s' % (f, self.reldir), file=out) 295246149Ssjg 296246149Ssjg def seenit(self, dir): 297246149Ssjg """rememer that we have seen dir.""" 298246149Ssjg self.seen[dir] = 1 299246149Ssjg 300246149Ssjg def add(self, list, data, clue=''): 301246149Ssjg """add data to list if it isn't already there.""" 302246149Ssjg if data not in list: 303246149Ssjg list.append(data) 304246149Ssjg if self.debug: 305261212Ssjg print("%s: %sAdd: %s" % (self.name, clue, data), file=self.debug_out) 306246149Ssjg 307246149Ssjg def find_top(self, path, list): 308268437Ssjg """the logical tree may be split across multiple trees""" 309246149Ssjg for top in list: 310246149Ssjg if path.startswith(top): 311246149Ssjg if self.debug > 2: 312261212Ssjg print("found in", top, file=self.debug_out) 313246149Ssjg return top 314246149Ssjg return None 315246149Ssjg 316246149Ssjg def find_obj(self, objroot, dir, path, input): 317246149Ssjg """return path within objroot, taking care of .dirdep files""" 318246149Ssjg ddep = None 319246149Ssjg for ddepf in [path + '.dirdep', dir + '/.dirdep']: 320246149Ssjg if not ddep and os.path.exists(ddepf): 321261212Ssjg ddep = open(ddepf, 'r').readline().strip('# \n') 322246149Ssjg if self.debug > 1: 323261212Ssjg print("found %s: %s\n" % (ddepf, ddep), file=self.debug_out) 324246149Ssjg if ddep.endswith(self.machine): 325246149Ssjg ddep = ddep[0:-(1+len(self.machine))] 326250837Ssjg elif self.target_spec and ddep.endswith(self.target_spec): 327250837Ssjg ddep = ddep[0:-(1+len(self.target_spec))] 328246149Ssjg 329246149Ssjg if not ddep: 330246149Ssjg # no .dirdeps, so remember that we've seen the raw input 331246149Ssjg self.seenit(input) 332246149Ssjg self.seenit(dir) 333246149Ssjg if self.machine == 'none': 334246149Ssjg if dir.startswith(objroot): 335246149Ssjg return dir.replace(objroot,'') 336246149Ssjg return None 337246149Ssjg m = self.dirdep_re.match(dir.replace(objroot,'')) 338246149Ssjg if m: 339246149Ssjg ddep = m.group(2) 340246149Ssjg dmachine = m.group(1) 341246149Ssjg if dmachine != self.machine: 342246149Ssjg if not (self.machine == 'host' and 343246149Ssjg dmachine == self.host_target): 344246149Ssjg if self.debug > 2: 345261212Ssjg print("adding .%s to %s" % (dmachine, ddep), file=self.debug_out) 346246149Ssjg ddep += '.' + dmachine 347246149Ssjg 348246149Ssjg return ddep 349246149Ssjg 350253883Ssjg def try_parse(self, name=None, file=None): 351253883Ssjg """give file and line number causing exception""" 352253883Ssjg try: 353253883Ssjg self.parse(name, file) 354253883Ssjg except: 355253883Ssjg # give a useful clue 356261212Ssjg print('{}:{}: '.format(self.name, self.line), end=' ', file=sys.stderr) 357253883Ssjg raise 358253883Ssjg 359246149Ssjg def parse(self, name=None, file=None): 360246149Ssjg """A meta file looks like: 361246149Ssjg 362246149Ssjg # Meta data file "path" 363246149Ssjg CMD "command-line" 364246149Ssjg CWD "cwd" 365246149Ssjg TARGET "target" 366246149Ssjg -- command output -- 367246149Ssjg -- filemon acquired metadata -- 368246149Ssjg # buildmon version 3 369246149Ssjg V 3 370246149Ssjg C "pid" "cwd" 371246149Ssjg E "pid" "path" 372281812Ssjg F "pid" "child" 373246149Ssjg R "pid" "path" 374246149Ssjg W "pid" "path" 375246149Ssjg X "pid" "status" 376281812Ssjg D "pid" "path" 377281812Ssjg L "pid" "src" "target" 378281812Ssjg M "pid" "old" "new" 379281812Ssjg S "pid" "path" 380281812Ssjg # Bye bye 381246149Ssjg 382281812Ssjg We go to some effort to avoid processing a dependency more than once. 383281812Ssjg Of the above record types only C,E,F,L,R,V and W are of interest. 384246149Ssjg """ 385246149Ssjg 386246149Ssjg version = 0 # unknown 387246149Ssjg if name: 388246149Ssjg self.name = name; 389246149Ssjg if file: 390246149Ssjg f = file 391281812Ssjg cwd = self.last_dir = self.cwd 392246149Ssjg else: 393261212Ssjg f = open(self.name, 'r') 394246149Ssjg skip = True 395246149Ssjg pid_cwd = {} 396246149Ssjg pid_last_dir = {} 397246149Ssjg last_pid = 0 398246149Ssjg 399253883Ssjg self.line = 0 400246149Ssjg if self.curdir: 401246149Ssjg self.seenit(self.curdir) # we ignore this 402246149Ssjg 403246149Ssjg interesting = 'CEFLRV' 404246149Ssjg for line in f: 405253883Ssjg self.line += 1 406246149Ssjg # ignore anything we don't care about 407246149Ssjg if not line[0] in interesting: 408246149Ssjg continue 409246149Ssjg if self.debug > 2: 410261212Ssjg print("input:", line, end=' ', file=self.debug_out) 411246149Ssjg w = line.split() 412246149Ssjg 413246149Ssjg if skip: 414246149Ssjg if w[0] == 'V': 415246149Ssjg skip = False 416246149Ssjg version = int(w[1]) 417246149Ssjg """ 418246149Ssjg if version < 4: 419246149Ssjg # we cannot ignore 'W' records 420246149Ssjg # as they may be 'rw' 421246149Ssjg interesting += 'W' 422246149Ssjg """ 423246149Ssjg elif w[0] == 'CWD': 424281812Ssjg self.cwd = cwd = self.last_dir = w[1] 425246149Ssjg self.seenit(cwd) # ignore this 426246149Ssjg if self.debug: 427261212Ssjg print("%s: CWD=%s" % (self.name, cwd), file=self.debug_out) 428246149Ssjg continue 429246149Ssjg 430246149Ssjg pid = int(w[1]) 431246149Ssjg if pid != last_pid: 432246149Ssjg if last_pid: 433246149Ssjg pid_cwd[last_pid] = cwd 434281812Ssjg pid_last_dir[last_pid] = self.last_dir 435246149Ssjg cwd = getv(pid_cwd, pid, self.cwd) 436281812Ssjg self.last_dir = getv(pid_last_dir, pid, self.cwd) 437246149Ssjg last_pid = pid 438246149Ssjg 439246149Ssjg # process operations 440246149Ssjg if w[0] == 'F': 441246149Ssjg npid = int(w[2]) 442246149Ssjg pid_cwd[npid] = cwd 443246149Ssjg pid_last_dir[npid] = cwd 444246149Ssjg last_pid = npid 445246149Ssjg continue 446246149Ssjg elif w[0] == 'C': 447246149Ssjg cwd = abspath(w[2], cwd, None, self.debug, self.debug_out) 448246149Ssjg if cwd.endswith('/.'): 449246149Ssjg cwd = cwd[0:-2] 450281812Ssjg self.last_dir = cwd 451246149Ssjg if self.debug > 1: 452261212Ssjg print("cwd=", cwd, file=self.debug_out) 453246149Ssjg continue 454246149Ssjg 455246149Ssjg if w[2] in self.seen: 456246149Ssjg if self.debug > 2: 457261212Ssjg print("seen:", w[2], file=self.debug_out) 458246149Ssjg continue 459246149Ssjg # file operations 460246149Ssjg if w[0] in 'ML': 461281812Ssjg # these are special, tread src as read and 462281812Ssjg # target as write 463281812Ssjg self.parse_path(w[1].strip("'"), cwd, 'R', w) 464281812Ssjg self.parse_path(w[2].strip("'"), cwd, 'W', w) 465281812Ssjg continue 466281812Ssjg elif w[0] in 'ERWS': 467246149Ssjg path = w[2] 468281812Ssjg self.parse_path(path, cwd, w[0], w) 469281812Ssjg 470281812Ssjg if not file: 471281812Ssjg f.close() 472281812Ssjg 473281812Ssjg def parse_path(self, path, cwd, op=None, w=[]): 474281812Ssjg """look at a path for the op specified""" 475281812Ssjg 476281812Ssjg if not op: 477281812Ssjg op = w[0] 478281812Ssjg 479281812Ssjg # we are never interested in .dirdep files as dependencies 480281812Ssjg if path.endswith('.dirdep'): 481281812Ssjg return 482281812Ssjg for p in self.excludes: 483281812Ssjg if p and path.startswith(p): 484246149Ssjg if self.debug > 2: 485281812Ssjg print >> self.debug_out, "exclude:", p, path 486281812Ssjg return 487281812Ssjg # we don't want to resolve the last component if it is 488281812Ssjg # a symlink 489281812Ssjg path = resolve(path, cwd, self.last_dir, self.debug, self.debug_out) 490281812Ssjg if not path: 491281812Ssjg return 492281812Ssjg dir,base = os.path.split(path) 493281812Ssjg if dir in self.seen: 494281812Ssjg if self.debug > 2: 495281812Ssjg print("seen:", dir, file=self.debug_out) 496281812Ssjg return 497281812Ssjg # we can have a path in an objdir which is a link 498281812Ssjg # to the src dir, we may need to add dependencies for each 499281812Ssjg rdir = dir 500281812Ssjg dir = abspath(dir, cwd, self.last_dir, self.debug, self.debug_out) 501281812Ssjg if rdir == dir or rdir.find('./') > 0: 502281812Ssjg rdir = None 503281812Ssjg # now put path back together 504281812Ssjg path = '/'.join([dir,base]) 505281812Ssjg if self.debug > 1: 506281812Ssjg print("raw=%s rdir=%s dir=%s path=%s" % (w[2], rdir, dir, path), file=self.debug_out) 507281812Ssjg if op in 'RWS': 508281812Ssjg if path in [self.last_dir, cwd, self.cwd, self.curdir]: 509281812Ssjg if self.debug > 1: 510281812Ssjg print("skipping:", path, file=self.debug_out) 511281812Ssjg return 512281812Ssjg if os.path.isdir(path): 513281812Ssjg if op in 'RW': 514281812Ssjg self.last_dir = path; 515281812Ssjg if self.debug > 1: 516281812Ssjg print("ldir=", self.last_dir, file=self.debug_out) 517281812Ssjg return 518246149Ssjg 519281812Ssjg if op in 'ERW': 520281812Ssjg # finally, we get down to it 521281812Ssjg if dir == self.cwd or dir == self.curdir: 522281812Ssjg return 523281812Ssjg srctop = self.find_top(path, self.srctops) 524281812Ssjg if srctop: 525281812Ssjg if self.dpdeps: 526281812Ssjg self.add(self.file_deps, path.replace(srctop,''), 'file') 527281812Ssjg self.add(self.src_deps, dir.replace(srctop,''), 'src') 528281812Ssjg self.seenit(w[2]) 529281812Ssjg self.seenit(dir) 530281812Ssjg if rdir and not rdir.startswith(srctop): 531281812Ssjg dir = rdir # for below 532281812Ssjg rdir = None 533281812Ssjg else: 534281812Ssjg return 535281812Ssjg 536281812Ssjg objroot = None 537281812Ssjg for dir in [dir,rdir]: 538281812Ssjg if not dir: 539246149Ssjg continue 540281812Ssjg objroot = self.find_top(dir, self.objroots) 541246149Ssjg if objroot: 542281812Ssjg break 543281812Ssjg if objroot: 544281812Ssjg ddep = self.find_obj(objroot, dir, path, w[2]) 545281812Ssjg if ddep: 546281812Ssjg self.add(self.obj_deps, ddep, 'obj') 547281812Ssjg else: 548281812Ssjg # don't waste time looking again 549281812Ssjg self.seenit(w[2]) 550281812Ssjg self.seenit(dir) 551246149Ssjg 552246149Ssjg 553246149Ssjgdef main(argv, klass=MetaFile, xopts='', xoptf=None): 554246149Ssjg """Simple driver for class MetaFile. 555246149Ssjg 556246149Ssjg Usage: 557281812Ssjg script [options] [key=value ...] "meta" ... 558246149Ssjg 559246149Ssjg Options and key=value pairs contribute to the 560246149Ssjg dictionary passed to MetaFile. 561246149Ssjg 562246149Ssjg -S "SRCTOP" 563281812Ssjg add "SRCTOP" to the "SRCTOPS" list. 564246149Ssjg 565246149Ssjg -C "CURDIR" 566246149Ssjg 567246149Ssjg -O "OBJROOT" 568281812Ssjg add "OBJROOT" to the "OBJROOTS" list. 569246149Ssjg 570246149Ssjg -m "MACHINE" 571246149Ssjg 572250837Ssjg -a "MACHINE_ARCH" 573250837Ssjg 574246149Ssjg -H "HOST_TARGET" 575246149Ssjg 576246149Ssjg -D "DPDEPS" 577246149Ssjg 578281812Ssjg -d bumps debug level 579246149Ssjg 580246149Ssjg """ 581246149Ssjg import getopt 582246149Ssjg 583246149Ssjg # import Psyco if we can 584246149Ssjg # it can speed things up quite a bit 585246149Ssjg have_psyco = 0 586246149Ssjg try: 587246149Ssjg import psyco 588246149Ssjg psyco.full() 589246149Ssjg have_psyco = 1 590246149Ssjg except: 591246149Ssjg pass 592246149Ssjg 593246149Ssjg conf = { 594246149Ssjg 'SRCTOPS': [], 595246149Ssjg 'OBJROOTS': [], 596281812Ssjg 'EXCLUDES': [], 597246149Ssjg } 598246149Ssjg 599246149Ssjg try: 600246149Ssjg machine = os.environ['MACHINE'] 601246149Ssjg if machine: 602246149Ssjg conf['MACHINE'] = machine 603250837Ssjg machine_arch = os.environ['MACHINE_ARCH'] 604250837Ssjg if machine_arch: 605250837Ssjg conf['MACHINE_ARCH'] = machine_arch 606246149Ssjg srctop = os.environ['SB_SRC'] 607246149Ssjg if srctop: 608246149Ssjg conf['SRCTOPS'].append(srctop) 609246149Ssjg objroot = os.environ['SB_OBJROOT'] 610246149Ssjg if objroot: 611246149Ssjg conf['OBJROOTS'].append(objroot) 612246149Ssjg except: 613246149Ssjg pass 614246149Ssjg 615246149Ssjg debug = 0 616246149Ssjg output = True 617246149Ssjg 618281812Ssjg opts, args = getopt.getopt(argv[1:], 'a:dS:C:O:R:m:D:H:qT:X:' + xopts) 619246149Ssjg for o, a in opts: 620250837Ssjg if o == '-a': 621250837Ssjg conf['MACHINE_ARCH'] = a 622250837Ssjg elif o == '-d': 623246149Ssjg debug += 1 624246149Ssjg elif o == '-q': 625246149Ssjg output = False 626246149Ssjg elif o == '-H': 627246149Ssjg conf['HOST_TARGET'] = a 628246149Ssjg elif o == '-S': 629246149Ssjg if a not in conf['SRCTOPS']: 630246149Ssjg conf['SRCTOPS'].append(a) 631246149Ssjg elif o == '-C': 632246149Ssjg conf['CURDIR'] = a 633246149Ssjg elif o == '-O': 634246149Ssjg if a not in conf['OBJROOTS']: 635246149Ssjg conf['OBJROOTS'].append(a) 636246149Ssjg elif o == '-R': 637246149Ssjg conf['RELDIR'] = a 638246149Ssjg elif o == '-D': 639246149Ssjg conf['DPDEPS'] = a 640246149Ssjg elif o == '-m': 641246149Ssjg conf['MACHINE'] = a 642250837Ssjg elif o == '-T': 643250837Ssjg conf['TARGET_SPEC'] = a 644281812Ssjg elif o == '-X': 645281812Ssjg if a not in conf['EXCLUDES']: 646281812Ssjg conf['EXCLUDES'].append(a) 647246149Ssjg elif xoptf: 648246149Ssjg xoptf(o, a, conf) 649246149Ssjg 650246149Ssjg conf['debug'] = debug 651246149Ssjg 652246149Ssjg # get any var=val assignments 653246149Ssjg eaten = [] 654246149Ssjg for a in args: 655246149Ssjg if a.find('=') > 0: 656246149Ssjg k,v = a.split('=') 657246149Ssjg if k in ['SRCTOP','OBJROOT','SRCTOPS','OBJROOTS']: 658246149Ssjg if k == 'SRCTOP': 659246149Ssjg k = 'SRCTOPS' 660246149Ssjg elif k == 'OBJROOT': 661246149Ssjg k = 'OBJROOTS' 662246149Ssjg if v not in conf[k]: 663246149Ssjg conf[k].append(v) 664246149Ssjg else: 665246149Ssjg conf[k] = v 666246149Ssjg eaten.append(a) 667246149Ssjg continue 668246149Ssjg break 669246149Ssjg 670246149Ssjg for a in eaten: 671246149Ssjg args.remove(a) 672246149Ssjg 673246149Ssjg debug_out = getv(conf, 'debug_out', sys.stderr) 674246149Ssjg 675246149Ssjg if debug: 676261212Ssjg print("config:", file=debug_out) 677261212Ssjg print("psyco=", have_psyco, file=debug_out) 678261212Ssjg for k,v in list(conf.items()): 679261212Ssjg print("%s=%s" % (k,v), file=debug_out) 680246149Ssjg 681281812Ssjg m = None 682246149Ssjg for a in args: 683253883Ssjg if a.endswith('.meta'): 684281812Ssjg if not os.path.exists(a): 685281812Ssjg continue 686253883Ssjg m = klass(a, conf) 687253883Ssjg elif a.startswith('@'): 688253883Ssjg # there can actually multiple files per line 689253883Ssjg for line in open(a[1:]): 690253883Ssjg for f in line.strip().split(): 691281812Ssjg if not os.path.exists(f): 692281812Ssjg continue 693253883Ssjg m = klass(f, conf) 694246149Ssjg 695281812Ssjg if output and m: 696261212Ssjg print(m.dirdeps()) 697246149Ssjg 698261212Ssjg print(m.src_dirdeps('\nsrc:')) 699246149Ssjg 700246149Ssjg dpdeps = getv(conf, 'DPDEPS') 701246149Ssjg if dpdeps: 702246149Ssjg m.file_depends(open(dpdeps, 'wb')) 703246149Ssjg 704246149Ssjg return m 705246149Ssjg 706246149Ssjgif __name__ == '__main__': 707246149Ssjg try: 708246149Ssjg main(sys.argv) 709246149Ssjg except: 710246149Ssjg # yes, this goes to stdout 711261212Ssjg print("ERROR: ", sys.exc_info()[1]) 712246149Ssjg raise 713246149Ssjg 714