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: 40321653Ssjg $Id: meta2deps.py,v 1.27 2017/05/24 00:04:04 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 93321653Ssjg if path == '..': 94321653Ssjg dw = d.split('/') 95321653Ssjg p = '/'.join(dw[:-1]) 96321653Ssjg if not p: 97321653Ssjg p = '/' 98321653Ssjg return p 99246149Ssjg p = '/'.join([d,path]) 100246149Ssjg if debug > 2: 101261212Ssjg print("looking for:", p, end=' ', file=debug_out) 102246149Ssjg if not os.path.exists(p): 103246149Ssjg if debug > 2: 104261212Ssjg print("nope", file=debug_out) 105246149Ssjg p = None 106246149Ssjg continue 107246149Ssjg if debug > 2: 108261212Ssjg print("found:", p, file=debug_out) 109246149Ssjg return p 110246149Ssjg return None 111246149Ssjg 112319884Ssjgdef cleanpath(path): 113319884Ssjg """cleanup path without using realpath(3)""" 114319884Ssjg if path.startswith('/'): 115319884Ssjg r = '/' 116319884Ssjg else: 117319884Ssjg r = '' 118319884Ssjg p = [] 119319884Ssjg w = path.split('/') 120319884Ssjg for d in w: 121319884Ssjg if not d or d == '.': 122319884Ssjg continue 123319884Ssjg if d == '..': 124321653Ssjg try: 125321653Ssjg p.pop() 126321653Ssjg continue 127321653Ssjg except: 128321653Ssjg break 129319884Ssjg p.append(d) 130319884Ssjg 131319884Ssjg return r + '/'.join(p) 132319884Ssjg 133246149Ssjgdef abspath(path, cwd, last_dir=None, debug=0, debug_out=sys.stderr): 134246149Ssjg """ 135246149Ssjg Return an absolute path, resolving via cwd or last_dir if needed. 136319884Ssjg this gets called a lot, so we try to avoid calling realpath. 137246149Ssjg """ 138253883Ssjg rpath = resolve(path, cwd, last_dir, debug, debug_out) 139253883Ssjg if rpath: 140253883Ssjg path = rpath 141281812Ssjg if (path.find('/') < 0 or 142319884Ssjg path.find('./') > 0 or 143319884Ssjg path.endswith('/..')): 144319884Ssjg path = cleanpath(path) 145246149Ssjg return path 146246149Ssjg 147246149Ssjgdef sort_unique(list, cmp=None, key=None, reverse=False): 148246149Ssjg list.sort(cmp, key, reverse) 149246149Ssjg nl = [] 150246149Ssjg le = None 151246149Ssjg for e in list: 152246149Ssjg if e == le: 153246149Ssjg continue 154319884Ssjg le = e 155246149Ssjg nl.append(e) 156246149Ssjg return nl 157246149Ssjg 158250837Ssjgdef add_trims(x): 159250837Ssjg return ['/' + x + '/', 160250837Ssjg '/' + x, 161250837Ssjg x + '/', 162250837Ssjg x] 163250837Ssjg 164246149Ssjgclass MetaFile: 165246149Ssjg """class to parse meta files generated by bmake.""" 166246149Ssjg 167246149Ssjg conf = None 168246149Ssjg dirdep_re = None 169246149Ssjg host_target = None 170246149Ssjg srctops = [] 171246149Ssjg objroots = [] 172281812Ssjg excludes = [] 173246149Ssjg seen = {} 174246149Ssjg obj_deps = [] 175246149Ssjg src_deps = [] 176246149Ssjg file_deps = [] 177246149Ssjg 178246149Ssjg def __init__(self, name, conf={}): 179246149Ssjg """if name is set we will parse it now. 180246149Ssjg conf can have the follwing keys: 181246149Ssjg 182319884Ssjg SRCTOPS list of tops of the src tree(s). 183246149Ssjg 184319884Ssjg CURDIR the src directory 'bmake' was run from. 185246149Ssjg 186319884Ssjg RELDIR the relative path from SRCTOP to CURDIR 187246149Ssjg 188319884Ssjg MACHINE the machine we built for. 189319884Ssjg set to 'none' if we are not cross-building. 190319884Ssjg More specifically if machine cannot be deduced from objdirs. 191246149Ssjg 192250837Ssjg TARGET_SPEC 193319884Ssjg Sometimes MACHINE isn't enough. 194250837Ssjg 195246149Ssjg HOST_TARGET 196319884Ssjg when we build for the pseudo machine 'host' 197319884Ssjg the object tree uses HOST_TARGET rather than MACHINE. 198246149Ssjg 199246149Ssjg OBJROOTS a list of the common prefix for all obj dirs it might 200319884Ssjg end in '/' or '-'. 201246149Ssjg 202319884Ssjg DPDEPS names an optional file to which per file dependencies 203319884Ssjg will be appended. 204319884Ssjg For example if 'some/path/foo.h' is read from SRCTOP 205319884Ssjg then 'DPDEPS_some/path/foo.h +=' "RELDIR" is output. 206319884Ssjg This can allow 'bmake' to learn all the dirs within 207319884Ssjg the tree that depend on 'foo.h' 208246149Ssjg 209319884Ssjg EXCLUDES 210319884Ssjg A list of paths to ignore. 211319884Ssjg ccache(1) can otherwise be trouble. 212281812Ssjg 213319884Ssjg debug desired debug level 214246149Ssjg 215246149Ssjg debug_out open file to send debug output to (sys.stderr) 216246149Ssjg 217246149Ssjg """ 218246149Ssjg 219246149Ssjg self.name = name 220246149Ssjg self.debug = getv(conf, 'debug', 0) 221246149Ssjg self.debug_out = getv(conf, 'debug_out', sys.stderr) 222246149Ssjg 223249033Ssjg self.machine = getv(conf, 'MACHINE', '') 224250837Ssjg self.machine_arch = getv(conf, 'MACHINE_ARCH', '') 225250837Ssjg self.target_spec = getv(conf, 'TARGET_SPEC', '') 226249033Ssjg self.curdir = getv(conf, 'CURDIR') 227249033Ssjg self.reldir = getv(conf, 'RELDIR') 228249033Ssjg self.dpdeps = getv(conf, 'DPDEPS') 229253883Ssjg self.line = 0 230249033Ssjg 231246149Ssjg if not self.conf: 232246149Ssjg # some of the steps below we want to do only once 233246149Ssjg self.conf = conf 234246149Ssjg self.host_target = getv(conf, 'HOST_TARGET') 235246149Ssjg for srctop in getv(conf, 'SRCTOPS', []): 236246149Ssjg if srctop[-1] != '/': 237246149Ssjg srctop += '/' 238246149Ssjg if not srctop in self.srctops: 239246149Ssjg self.srctops.append(srctop) 240246149Ssjg _srctop = os.path.realpath(srctop) 241246149Ssjg if _srctop[-1] != '/': 242246149Ssjg _srctop += '/' 243246149Ssjg if not _srctop in self.srctops: 244246149Ssjg self.srctops.append(_srctop) 245246149Ssjg 246250837Ssjg trim_list = add_trims(self.machine) 247249033Ssjg if self.machine == 'host': 248250837Ssjg trim_list += add_trims(self.host_target) 249250837Ssjg if self.target_spec: 250250837Ssjg trim_list += add_trims(self.target_spec) 251249033Ssjg 252246149Ssjg for objroot in getv(conf, 'OBJROOTS', []): 253249033Ssjg for e in trim_list: 254249033Ssjg if objroot.endswith(e): 255249033Ssjg # this is not what we want - fix it 256249033Ssjg objroot = objroot[0:-len(e)] 257319884Ssjg 258319884Ssjg if objroot[-1] != '/': 259319884Ssjg objroot += '/' 260246149Ssjg if not objroot in self.objroots: 261246149Ssjg self.objroots.append(objroot) 262246149Ssjg _objroot = os.path.realpath(objroot) 263246149Ssjg if objroot[-1] == '/': 264246149Ssjg _objroot += '/' 265246149Ssjg if not _objroot in self.objroots: 266246149Ssjg self.objroots.append(_objroot) 267246149Ssjg 268249033Ssjg # we want the longest match 269249033Ssjg self.srctops.sort(reverse=True) 270249033Ssjg self.objroots.sort(reverse=True) 271281812Ssjg 272281812Ssjg self.excludes = getv(conf, 'EXCLUDES', []) 273281812Ssjg 274246149Ssjg if self.debug: 275261212Ssjg print("host_target=", self.host_target, file=self.debug_out) 276261212Ssjg print("srctops=", self.srctops, file=self.debug_out) 277261212Ssjg print("objroots=", self.objroots, file=self.debug_out) 278281812Ssjg print("excludes=", self.excludes, file=self.debug_out) 279246149Ssjg 280246149Ssjg self.dirdep_re = re.compile(r'([^/]+)/(.+)') 281246149Ssjg 282246149Ssjg if self.dpdeps and not self.reldir: 283246149Ssjg if self.debug: 284261212Ssjg print("need reldir:", end=' ', file=self.debug_out) 285246149Ssjg if self.curdir: 286246149Ssjg srctop = self.find_top(self.curdir, self.srctops) 287246149Ssjg if srctop: 288246149Ssjg self.reldir = self.curdir.replace(srctop,'') 289246149Ssjg if self.debug: 290261212Ssjg print(self.reldir, file=self.debug_out) 291246149Ssjg if not self.reldir: 292246149Ssjg self.dpdeps = None # we cannot do it? 293246149Ssjg 294249033Ssjg self.cwd = os.getcwd() # make sure this is initialized 295281812Ssjg self.last_dir = self.cwd 296249033Ssjg 297246149Ssjg if name: 298253883Ssjg self.try_parse() 299246149Ssjg 300246149Ssjg def reset(self): 301246149Ssjg """reset state if we are being passed meta files from multiple directories.""" 302246149Ssjg self.seen = {} 303246149Ssjg self.obj_deps = [] 304246149Ssjg self.src_deps = [] 305246149Ssjg self.file_deps = [] 306246149Ssjg 307246149Ssjg def dirdeps(self, sep='\n'): 308246149Ssjg """return DIRDEPS""" 309246149Ssjg return sep.strip() + sep.join(self.obj_deps) 310246149Ssjg 311246149Ssjg def src_dirdeps(self, sep='\n'): 312246149Ssjg """return SRC_DIRDEPS""" 313246149Ssjg return sep.strip() + sep.join(self.src_deps) 314246149Ssjg 315246149Ssjg def file_depends(self, out=None): 316246149Ssjg """Append DPDEPS_${file} += ${RELDIR} 317246149Ssjg for each file we saw, to the output file.""" 318246149Ssjg if not self.reldir: 319246149Ssjg return None 320246149Ssjg for f in sort_unique(self.file_deps): 321261212Ssjg print('DPDEPS_%s += %s' % (f, self.reldir), file=out) 322319884Ssjg # these entries provide for reverse DIRDEPS lookup 323319884Ssjg for f in self.obj_deps: 324319884Ssjg print('DEPDIRS_%s += %s' % (f, self.reldir), file=out) 325246149Ssjg 326246149Ssjg def seenit(self, dir): 327246149Ssjg """rememer that we have seen dir.""" 328246149Ssjg self.seen[dir] = 1 329246149Ssjg 330246149Ssjg def add(self, list, data, clue=''): 331246149Ssjg """add data to list if it isn't already there.""" 332246149Ssjg if data not in list: 333246149Ssjg list.append(data) 334246149Ssjg if self.debug: 335261212Ssjg print("%s: %sAdd: %s" % (self.name, clue, data), file=self.debug_out) 336246149Ssjg 337246149Ssjg def find_top(self, path, list): 338268437Ssjg """the logical tree may be split across multiple trees""" 339246149Ssjg for top in list: 340246149Ssjg if path.startswith(top): 341246149Ssjg if self.debug > 2: 342261212Ssjg print("found in", top, file=self.debug_out) 343246149Ssjg return top 344246149Ssjg return None 345246149Ssjg 346246149Ssjg def find_obj(self, objroot, dir, path, input): 347246149Ssjg """return path within objroot, taking care of .dirdep files""" 348246149Ssjg ddep = None 349246149Ssjg for ddepf in [path + '.dirdep', dir + '/.dirdep']: 350246149Ssjg if not ddep and os.path.exists(ddepf): 351261212Ssjg ddep = open(ddepf, 'r').readline().strip('# \n') 352246149Ssjg if self.debug > 1: 353261212Ssjg print("found %s: %s\n" % (ddepf, ddep), file=self.debug_out) 354246149Ssjg if ddep.endswith(self.machine): 355246149Ssjg ddep = ddep[0:-(1+len(self.machine))] 356250837Ssjg elif self.target_spec and ddep.endswith(self.target_spec): 357250837Ssjg ddep = ddep[0:-(1+len(self.target_spec))] 358246149Ssjg 359246149Ssjg if not ddep: 360246149Ssjg # no .dirdeps, so remember that we've seen the raw input 361246149Ssjg self.seenit(input) 362246149Ssjg self.seenit(dir) 363246149Ssjg if self.machine == 'none': 364246149Ssjg if dir.startswith(objroot): 365246149Ssjg return dir.replace(objroot,'') 366246149Ssjg return None 367246149Ssjg m = self.dirdep_re.match(dir.replace(objroot,'')) 368246149Ssjg if m: 369246149Ssjg ddep = m.group(2) 370246149Ssjg dmachine = m.group(1) 371246149Ssjg if dmachine != self.machine: 372246149Ssjg if not (self.machine == 'host' and 373246149Ssjg dmachine == self.host_target): 374246149Ssjg if self.debug > 2: 375261212Ssjg print("adding .%s to %s" % (dmachine, ddep), file=self.debug_out) 376246149Ssjg ddep += '.' + dmachine 377246149Ssjg 378246149Ssjg return ddep 379246149Ssjg 380253883Ssjg def try_parse(self, name=None, file=None): 381253883Ssjg """give file and line number causing exception""" 382253883Ssjg try: 383253883Ssjg self.parse(name, file) 384253883Ssjg except: 385253883Ssjg # give a useful clue 386261212Ssjg print('{}:{}: '.format(self.name, self.line), end=' ', file=sys.stderr) 387253883Ssjg raise 388253883Ssjg 389246149Ssjg def parse(self, name=None, file=None): 390246149Ssjg """A meta file looks like: 391246149Ssjg 392319884Ssjg # Meta data file "path" 393319884Ssjg CMD "command-line" 394319884Ssjg CWD "cwd" 395319884Ssjg TARGET "target" 396319884Ssjg -- command output -- 397319884Ssjg -- filemon acquired metadata -- 398319884Ssjg # buildmon version 3 399319884Ssjg V 3 400319884Ssjg C "pid" "cwd" 401319884Ssjg E "pid" "path" 402319884Ssjg F "pid" "child" 403319884Ssjg R "pid" "path" 404319884Ssjg W "pid" "path" 405319884Ssjg X "pid" "status" 406319884Ssjg D "pid" "path" 407319884Ssjg L "pid" "src" "target" 408319884Ssjg M "pid" "old" "new" 409319884Ssjg S "pid" "path" 410319884Ssjg # Bye bye 411246149Ssjg 412319884Ssjg We go to some effort to avoid processing a dependency more than once. 413319884Ssjg Of the above record types only C,E,F,L,R,V and W are of interest. 414246149Ssjg """ 415246149Ssjg 416246149Ssjg version = 0 # unknown 417246149Ssjg if name: 418246149Ssjg self.name = name; 419246149Ssjg if file: 420246149Ssjg f = file 421281812Ssjg cwd = self.last_dir = self.cwd 422246149Ssjg else: 423261212Ssjg f = open(self.name, 'r') 424246149Ssjg skip = True 425246149Ssjg pid_cwd = {} 426246149Ssjg pid_last_dir = {} 427246149Ssjg last_pid = 0 428246149Ssjg 429253883Ssjg self.line = 0 430246149Ssjg if self.curdir: 431246149Ssjg self.seenit(self.curdir) # we ignore this 432246149Ssjg 433246149Ssjg interesting = 'CEFLRV' 434246149Ssjg for line in f: 435253883Ssjg self.line += 1 436246149Ssjg # ignore anything we don't care about 437246149Ssjg if not line[0] in interesting: 438246149Ssjg continue 439246149Ssjg if self.debug > 2: 440261212Ssjg print("input:", line, end=' ', file=self.debug_out) 441246149Ssjg w = line.split() 442246149Ssjg 443246149Ssjg if skip: 444246149Ssjg if w[0] == 'V': 445246149Ssjg skip = False 446246149Ssjg version = int(w[1]) 447246149Ssjg """ 448246149Ssjg if version < 4: 449246149Ssjg # we cannot ignore 'W' records 450246149Ssjg # as they may be 'rw' 451246149Ssjg interesting += 'W' 452246149Ssjg """ 453246149Ssjg elif w[0] == 'CWD': 454281812Ssjg self.cwd = cwd = self.last_dir = w[1] 455246149Ssjg self.seenit(cwd) # ignore this 456246149Ssjg if self.debug: 457261212Ssjg print("%s: CWD=%s" % (self.name, cwd), file=self.debug_out) 458246149Ssjg continue 459246149Ssjg 460246149Ssjg pid = int(w[1]) 461246149Ssjg if pid != last_pid: 462246149Ssjg if last_pid: 463281812Ssjg pid_last_dir[last_pid] = self.last_dir 464246149Ssjg cwd = getv(pid_cwd, pid, self.cwd) 465281812Ssjg self.last_dir = getv(pid_last_dir, pid, self.cwd) 466246149Ssjg last_pid = pid 467246149Ssjg 468246149Ssjg # process operations 469246149Ssjg if w[0] == 'F': 470246149Ssjg npid = int(w[2]) 471246149Ssjg pid_cwd[npid] = cwd 472246149Ssjg pid_last_dir[npid] = cwd 473246149Ssjg last_pid = npid 474246149Ssjg continue 475246149Ssjg elif w[0] == 'C': 476246149Ssjg cwd = abspath(w[2], cwd, None, self.debug, self.debug_out) 477246149Ssjg if cwd.endswith('/.'): 478246149Ssjg cwd = cwd[0:-2] 479319884Ssjg self.last_dir = pid_last_dir[pid] = cwd 480319884Ssjg pid_cwd[pid] = cwd 481246149Ssjg if self.debug > 1: 482261212Ssjg print("cwd=", cwd, file=self.debug_out) 483246149Ssjg continue 484246149Ssjg 485246149Ssjg if w[2] in self.seen: 486246149Ssjg if self.debug > 2: 487261212Ssjg print("seen:", w[2], file=self.debug_out) 488246149Ssjg continue 489246149Ssjg # file operations 490246149Ssjg if w[0] in 'ML': 491281812Ssjg # these are special, tread src as read and 492281812Ssjg # target as write 493281812Ssjg self.parse_path(w[1].strip("'"), cwd, 'R', w) 494281812Ssjg self.parse_path(w[2].strip("'"), cwd, 'W', w) 495281812Ssjg continue 496281812Ssjg elif w[0] in 'ERWS': 497246149Ssjg path = w[2] 498281812Ssjg self.parse_path(path, cwd, w[0], w) 499281812Ssjg 500281812Ssjg if not file: 501281812Ssjg f.close() 502281812Ssjg 503319884Ssjg def is_src(self, base, dir, rdir): 504319884Ssjg """is base in srctop""" 505319884Ssjg for dir in [dir,rdir]: 506319884Ssjg if not dir: 507319884Ssjg continue 508319884Ssjg path = '/'.join([dir,base]) 509319884Ssjg srctop = self.find_top(path, self.srctops) 510319884Ssjg if srctop: 511319884Ssjg if self.dpdeps: 512319884Ssjg self.add(self.file_deps, path.replace(srctop,''), 'file') 513319884Ssjg self.add(self.src_deps, dir.replace(srctop,''), 'src') 514319884Ssjg self.seenit(dir) 515319884Ssjg return True 516319884Ssjg return False 517319884Ssjg 518281812Ssjg def parse_path(self, path, cwd, op=None, w=[]): 519281812Ssjg """look at a path for the op specified""" 520281812Ssjg 521281812Ssjg if not op: 522281812Ssjg op = w[0] 523281812Ssjg 524281812Ssjg # we are never interested in .dirdep files as dependencies 525281812Ssjg if path.endswith('.dirdep'): 526281812Ssjg return 527281812Ssjg for p in self.excludes: 528281812Ssjg if p and path.startswith(p): 529246149Ssjg if self.debug > 2: 530300313Ssjg print("exclude:", p, path, file=self.debug_out) 531281812Ssjg return 532281812Ssjg # we don't want to resolve the last component if it is 533281812Ssjg # a symlink 534281812Ssjg path = resolve(path, cwd, self.last_dir, self.debug, self.debug_out) 535281812Ssjg if not path: 536281812Ssjg return 537281812Ssjg dir,base = os.path.split(path) 538281812Ssjg if dir in self.seen: 539281812Ssjg if self.debug > 2: 540281812Ssjg print("seen:", dir, file=self.debug_out) 541281812Ssjg return 542281812Ssjg # we can have a path in an objdir which is a link 543281812Ssjg # to the src dir, we may need to add dependencies for each 544281812Ssjg rdir = dir 545281812Ssjg dir = abspath(dir, cwd, self.last_dir, self.debug, self.debug_out) 546319884Ssjg rdir = os.path.realpath(dir) 547319884Ssjg if rdir == dir: 548281812Ssjg rdir = None 549281812Ssjg # now put path back together 550281812Ssjg path = '/'.join([dir,base]) 551281812Ssjg if self.debug > 1: 552281812Ssjg print("raw=%s rdir=%s dir=%s path=%s" % (w[2], rdir, dir, path), file=self.debug_out) 553281812Ssjg if op in 'RWS': 554281812Ssjg if path in [self.last_dir, cwd, self.cwd, self.curdir]: 555281812Ssjg if self.debug > 1: 556281812Ssjg print("skipping:", path, file=self.debug_out) 557281812Ssjg return 558281812Ssjg if os.path.isdir(path): 559281812Ssjg if op in 'RW': 560281812Ssjg self.last_dir = path; 561281812Ssjg if self.debug > 1: 562281812Ssjg print("ldir=", self.last_dir, file=self.debug_out) 563281812Ssjg return 564246149Ssjg 565281812Ssjg if op in 'ERW': 566281812Ssjg # finally, we get down to it 567281812Ssjg if dir == self.cwd or dir == self.curdir: 568281812Ssjg return 569319884Ssjg if self.is_src(base, dir, rdir): 570281812Ssjg self.seenit(w[2]) 571319884Ssjg if not rdir: 572281812Ssjg return 573281812Ssjg 574281812Ssjg objroot = None 575281812Ssjg for dir in [dir,rdir]: 576281812Ssjg if not dir: 577246149Ssjg continue 578281812Ssjg objroot = self.find_top(dir, self.objroots) 579246149Ssjg if objroot: 580281812Ssjg break 581281812Ssjg if objroot: 582281812Ssjg ddep = self.find_obj(objroot, dir, path, w[2]) 583281812Ssjg if ddep: 584281812Ssjg self.add(self.obj_deps, ddep, 'obj') 585319884Ssjg if self.dpdeps and objroot.endswith('/stage/'): 586319884Ssjg sp = '/'.join(path.replace(objroot,'').split('/')[1:]) 587319884Ssjg self.add(self.file_deps, sp, 'file') 588281812Ssjg else: 589281812Ssjg # don't waste time looking again 590281812Ssjg self.seenit(w[2]) 591281812Ssjg self.seenit(dir) 592246149Ssjg 593246149Ssjg 594246149Ssjgdef main(argv, klass=MetaFile, xopts='', xoptf=None): 595246149Ssjg """Simple driver for class MetaFile. 596246149Ssjg 597246149Ssjg Usage: 598281812Ssjg script [options] [key=value ...] "meta" ... 599246149Ssjg 600246149Ssjg Options and key=value pairs contribute to the 601246149Ssjg dictionary passed to MetaFile. 602246149Ssjg 603246149Ssjg -S "SRCTOP" 604281812Ssjg add "SRCTOP" to the "SRCTOPS" list. 605246149Ssjg 606246149Ssjg -C "CURDIR" 607246149Ssjg 608246149Ssjg -O "OBJROOT" 609281812Ssjg add "OBJROOT" to the "OBJROOTS" list. 610246149Ssjg 611246149Ssjg -m "MACHINE" 612246149Ssjg 613250837Ssjg -a "MACHINE_ARCH" 614250837Ssjg 615246149Ssjg -H "HOST_TARGET" 616246149Ssjg 617246149Ssjg -D "DPDEPS" 618246149Ssjg 619281812Ssjg -d bumps debug level 620246149Ssjg 621246149Ssjg """ 622246149Ssjg import getopt 623246149Ssjg 624246149Ssjg # import Psyco if we can 625246149Ssjg # it can speed things up quite a bit 626246149Ssjg have_psyco = 0 627246149Ssjg try: 628246149Ssjg import psyco 629246149Ssjg psyco.full() 630246149Ssjg have_psyco = 1 631246149Ssjg except: 632246149Ssjg pass 633246149Ssjg 634246149Ssjg conf = { 635246149Ssjg 'SRCTOPS': [], 636246149Ssjg 'OBJROOTS': [], 637281812Ssjg 'EXCLUDES': [], 638246149Ssjg } 639246149Ssjg 640246149Ssjg try: 641246149Ssjg machine = os.environ['MACHINE'] 642246149Ssjg if machine: 643246149Ssjg conf['MACHINE'] = machine 644250837Ssjg machine_arch = os.environ['MACHINE_ARCH'] 645250837Ssjg if machine_arch: 646250837Ssjg conf['MACHINE_ARCH'] = machine_arch 647246149Ssjg srctop = os.environ['SB_SRC'] 648246149Ssjg if srctop: 649246149Ssjg conf['SRCTOPS'].append(srctop) 650246149Ssjg objroot = os.environ['SB_OBJROOT'] 651246149Ssjg if objroot: 652246149Ssjg conf['OBJROOTS'].append(objroot) 653246149Ssjg except: 654246149Ssjg pass 655246149Ssjg 656246149Ssjg debug = 0 657246149Ssjg output = True 658246149Ssjg 659281812Ssjg opts, args = getopt.getopt(argv[1:], 'a:dS:C:O:R:m:D:H:qT:X:' + xopts) 660246149Ssjg for o, a in opts: 661250837Ssjg if o == '-a': 662250837Ssjg conf['MACHINE_ARCH'] = a 663250837Ssjg elif o == '-d': 664246149Ssjg debug += 1 665246149Ssjg elif o == '-q': 666246149Ssjg output = False 667246149Ssjg elif o == '-H': 668246149Ssjg conf['HOST_TARGET'] = a 669246149Ssjg elif o == '-S': 670246149Ssjg if a not in conf['SRCTOPS']: 671246149Ssjg conf['SRCTOPS'].append(a) 672246149Ssjg elif o == '-C': 673246149Ssjg conf['CURDIR'] = a 674246149Ssjg elif o == '-O': 675246149Ssjg if a not in conf['OBJROOTS']: 676246149Ssjg conf['OBJROOTS'].append(a) 677246149Ssjg elif o == '-R': 678246149Ssjg conf['RELDIR'] = a 679246149Ssjg elif o == '-D': 680246149Ssjg conf['DPDEPS'] = a 681246149Ssjg elif o == '-m': 682246149Ssjg conf['MACHINE'] = a 683250837Ssjg elif o == '-T': 684250837Ssjg conf['TARGET_SPEC'] = a 685281812Ssjg elif o == '-X': 686281812Ssjg if a not in conf['EXCLUDES']: 687281812Ssjg conf['EXCLUDES'].append(a) 688246149Ssjg elif xoptf: 689246149Ssjg xoptf(o, a, conf) 690246149Ssjg 691246149Ssjg conf['debug'] = debug 692246149Ssjg 693246149Ssjg # get any var=val assignments 694246149Ssjg eaten = [] 695246149Ssjg for a in args: 696246149Ssjg if a.find('=') > 0: 697246149Ssjg k,v = a.split('=') 698246149Ssjg if k in ['SRCTOP','OBJROOT','SRCTOPS','OBJROOTS']: 699246149Ssjg if k == 'SRCTOP': 700246149Ssjg k = 'SRCTOPS' 701246149Ssjg elif k == 'OBJROOT': 702246149Ssjg k = 'OBJROOTS' 703246149Ssjg if v not in conf[k]: 704246149Ssjg conf[k].append(v) 705246149Ssjg else: 706246149Ssjg conf[k] = v 707246149Ssjg eaten.append(a) 708246149Ssjg continue 709246149Ssjg break 710246149Ssjg 711246149Ssjg for a in eaten: 712246149Ssjg args.remove(a) 713246149Ssjg 714246149Ssjg debug_out = getv(conf, 'debug_out', sys.stderr) 715246149Ssjg 716246149Ssjg if debug: 717261212Ssjg print("config:", file=debug_out) 718261212Ssjg print("psyco=", have_psyco, file=debug_out) 719261212Ssjg for k,v in list(conf.items()): 720261212Ssjg print("%s=%s" % (k,v), file=debug_out) 721246149Ssjg 722281812Ssjg m = None 723246149Ssjg for a in args: 724253883Ssjg if a.endswith('.meta'): 725281812Ssjg if not os.path.exists(a): 726281812Ssjg continue 727253883Ssjg m = klass(a, conf) 728253883Ssjg elif a.startswith('@'): 729253883Ssjg # there can actually multiple files per line 730253883Ssjg for line in open(a[1:]): 731253883Ssjg for f in line.strip().split(): 732281812Ssjg if not os.path.exists(f): 733281812Ssjg continue 734253883Ssjg m = klass(f, conf) 735246149Ssjg 736281812Ssjg if output and m: 737261212Ssjg print(m.dirdeps()) 738246149Ssjg 739261212Ssjg print(m.src_dirdeps('\nsrc:')) 740246149Ssjg 741246149Ssjg dpdeps = getv(conf, 'DPDEPS') 742246149Ssjg if dpdeps: 743246149Ssjg m.file_depends(open(dpdeps, 'wb')) 744246149Ssjg 745246149Ssjg return m 746246149Ssjg 747246149Ssjgif __name__ == '__main__': 748246149Ssjg try: 749246149Ssjg main(sys.argv) 750246149Ssjg except: 751246149Ssjg # yes, this goes to stdout 752261212Ssjg print("ERROR: ", sys.exc_info()[1]) 753246149Ssjg raise 754246149Ssjg 755