__init__.py 36 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229
  1. # ex:ts=4:sw=4:sts=4:et
  2. # -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
  3. #
  4. # BitBake Build System Python Library
  5. #
  6. # Copyright (C) 2003 Holger Schurig
  7. # Copyright (C) 2003, 2004 Chris Larson
  8. #
  9. # Based on Gentoo's portage.py.
  10. #
  11. # This program is free software; you can redistribute it and/or modify
  12. # it under the terms of the GNU General Public License version 2 as
  13. # published by the Free Software Foundation.
  14. #
  15. # This program is distributed in the hope that it will be useful,
  16. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. # GNU General Public License for more details.
  19. #
  20. # You should have received a copy of the GNU General Public License along
  21. # with this program; if not, write to the Free Software Foundation, Inc.,
  22. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  23. __version__ = "1.8.5"
  24. __all__ = [
  25. "debug",
  26. "note",
  27. "error",
  28. "fatal",
  29. "mkdirhier",
  30. "movefile",
  31. "tokenize",
  32. "evaluate",
  33. "flatten",
  34. "relparse",
  35. "ververify",
  36. "isjustname",
  37. "isspecific",
  38. "pkgsplit",
  39. "catpkgsplit",
  40. "vercmp",
  41. "pkgcmp",
  42. "dep_parenreduce",
  43. "dep_opconvert",
  44. "digraph",
  45. # fetch
  46. "decodeurl",
  47. "encodeurl",
  48. # modules
  49. "parse",
  50. "data",
  51. "event",
  52. "build",
  53. "fetch",
  54. "manifest",
  55. "methodpool",
  56. "cache",
  57. "runqueue",
  58. "taskdata",
  59. "providers",
  60. ]
  61. whitespace = '\t\n\x0b\x0c\r '
  62. lowercase = 'abcdefghijklmnopqrstuvwxyz'
  63. import sys, os, types, re, string, bb
  64. from bb import msg
  65. #projectdir = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
  66. projectdir = os.getcwd()
  67. if "BBDEBUG" in os.environ:
  68. level = int(os.environ["BBDEBUG"])
  69. if level:
  70. bb.msg.set_debug_level(level)
  71. class VarExpandError(Exception):
  72. pass
  73. class MalformedUrl(Exception):
  74. """Exception raised when encountering an invalid url"""
  75. #######################################################################
  76. #######################################################################
  77. #
  78. # SECTION: Debug
  79. #
  80. # PURPOSE: little functions to make yourself known
  81. #
  82. #######################################################################
  83. #######################################################################
  84. def debug(lvl, *args):
  85. bb.msg.std_debug(lvl, ''.join(args))
  86. def note(*args):
  87. bb.msg.std_note(''.join(args))
  88. def error(*args):
  89. bb.msg.std_error(''.join(args))
  90. def fatal(*args):
  91. bb.msg.std_fatal(''.join(args))
  92. #######################################################################
  93. #######################################################################
  94. #
  95. # SECTION: File
  96. #
  97. # PURPOSE: Basic file and directory tree related functions
  98. #
  99. #######################################################################
  100. #######################################################################
  101. def mkdirhier(dir):
  102. """Create a directory like 'mkdir -p', but does not complain if
  103. directory already exists like os.makedirs
  104. """
  105. debug(3, "mkdirhier(%s)" % dir)
  106. try:
  107. os.makedirs(dir)
  108. debug(2, "created " + dir)
  109. except OSError, e:
  110. if e.errno != 17: raise e
  111. #######################################################################
  112. import stat
  113. def movefile(src,dest,newmtime=None,sstat=None):
  114. """Moves a file from src to dest, preserving all permissions and
  115. attributes; mtime will be preserved even when moving across
  116. filesystems. Returns true on success and false on failure. Move is
  117. atomic.
  118. """
  119. #print "movefile("+src+","+dest+","+str(newmtime)+","+str(sstat)+")"
  120. try:
  121. if not sstat:
  122. sstat=os.lstat(src)
  123. except Exception, e:
  124. print "!!! Stating source file failed... movefile()"
  125. print "!!!",e
  126. return None
  127. destexists=1
  128. try:
  129. dstat=os.lstat(dest)
  130. except:
  131. dstat=os.lstat(os.path.dirname(dest))
  132. destexists=0
  133. if destexists:
  134. if stat.S_ISLNK(dstat[stat.ST_MODE]):
  135. try:
  136. os.unlink(dest)
  137. destexists=0
  138. except Exception, e:
  139. pass
  140. if stat.S_ISLNK(sstat[stat.ST_MODE]):
  141. try:
  142. target=os.readlink(src)
  143. if destexists and not stat.S_ISDIR(dstat[stat.ST_MODE]):
  144. os.unlink(dest)
  145. os.symlink(target,dest)
  146. # os.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
  147. os.unlink(src)
  148. return os.lstat(dest)
  149. except Exception, e:
  150. print "!!! failed to properly create symlink:"
  151. print "!!!",dest,"->",target
  152. print "!!!",e
  153. return None
  154. renamefailed=1
  155. if sstat[stat.ST_DEV]==dstat[stat.ST_DEV]:
  156. try:
  157. ret=os.rename(src,dest)
  158. renamefailed=0
  159. except Exception, e:
  160. import errno
  161. if e[0]!=errno.EXDEV:
  162. # Some random error.
  163. print "!!! Failed to move",src,"to",dest
  164. print "!!!",e
  165. return None
  166. # Invalid cross-device-link 'bind' mounted or actually Cross-Device
  167. if renamefailed:
  168. didcopy=0
  169. if stat.S_ISREG(sstat[stat.ST_MODE]):
  170. try: # For safety copy then move it over.
  171. shutil.copyfile(src,dest+"#new")
  172. os.rename(dest+"#new",dest)
  173. didcopy=1
  174. except Exception, e:
  175. print '!!! copy',src,'->',dest,'failed.'
  176. print "!!!",e
  177. return None
  178. else:
  179. #we don't yet handle special, so we need to fall back to /bin/mv
  180. a=getstatusoutput("/bin/mv -f "+"'"+src+"' '"+dest+"'")
  181. if a[0]!=0:
  182. print "!!! Failed to move special file:"
  183. print "!!! '"+src+"' to '"+dest+"'"
  184. print "!!!",a
  185. return None # failure
  186. try:
  187. if didcopy:
  188. missingos.lchown(dest,sstat[stat.ST_UID],sstat[stat.ST_GID])
  189. os.chmod(dest, stat.S_IMODE(sstat[stat.ST_MODE])) # Sticky is reset on chown
  190. os.unlink(src)
  191. except Exception, e:
  192. print "!!! Failed to chown/chmod/unlink in movefile()"
  193. print "!!!",dest
  194. print "!!!",e
  195. return None
  196. if newmtime:
  197. os.utime(dest,(newmtime,newmtime))
  198. else:
  199. os.utime(dest, (sstat[stat.ST_ATIME], sstat[stat.ST_MTIME]))
  200. newmtime=sstat[stat.ST_MTIME]
  201. return newmtime
  202. #######################################################################
  203. #######################################################################
  204. #
  205. # SECTION: Download
  206. #
  207. # PURPOSE: Download via HTTP, FTP, CVS, BITKEEPER, handling of MD5-signatures
  208. # and mirrors
  209. #
  210. #######################################################################
  211. #######################################################################
  212. def decodeurl(url):
  213. """Decodes an URL into the tokens (scheme, network location, path,
  214. user, password, parameters).
  215. >>> decodeurl("http://www.google.com/index.html")
  216. ('http', 'www.google.com', '/index.html', '', '', {})
  217. CVS url with username, host and cvsroot. The cvs module to check out is in the
  218. parameters:
  219. >>> decodeurl("cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg")
  220. ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'})
  221. Dito, but this time the username has a password part. And we also request a special tag
  222. to check out.
  223. >>> decodeurl("cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;module=familiar/dist/ipkg;tag=V0-99-81")
  224. ('cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'})
  225. """
  226. m = re.compile('(?P<type>[^:]*)://((?P<user>.+)@)?(?P<location>[^;]+)(;(?P<parm>.*))?').match(url)
  227. if not m:
  228. raise MalformedUrl(url)
  229. type = m.group('type')
  230. location = m.group('location')
  231. if not location:
  232. raise MalformedUrl(url)
  233. user = m.group('user')
  234. parm = m.group('parm')
  235. m = re.compile('(?P<host>[^/;]+)(?P<path>/[^;]+)').match(location)
  236. if m:
  237. host = m.group('host')
  238. path = m.group('path')
  239. else:
  240. host = ""
  241. path = location
  242. if user:
  243. m = re.compile('(?P<user>[^:]+)(:?(?P<pswd>.*))').match(user)
  244. if m:
  245. user = m.group('user')
  246. pswd = m.group('pswd')
  247. else:
  248. user = ''
  249. pswd = ''
  250. p = {}
  251. if parm:
  252. for s in parm.split(';'):
  253. s1,s2 = s.split('=')
  254. p[s1] = s2
  255. return (type, host, path, user, pswd, p)
  256. #######################################################################
  257. def encodeurl(decoded):
  258. """Encodes a URL from tokens (scheme, network location, path,
  259. user, password, parameters).
  260. >>> encodeurl(['http', 'www.google.com', '/index.html', '', '', {}])
  261. 'http://www.google.com/index.html'
  262. CVS with username, host and cvsroot. The cvs module to check out is in the
  263. parameters:
  264. >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', '', {'module': 'familiar/dist/ipkg'}])
  265. 'cvs://anoncvs@cvs.handhelds.org/cvs;module=familiar/dist/ipkg'
  266. Dito, but this time the username has a password part. And we also request a special tag
  267. to check out.
  268. >>> encodeurl(['cvs', 'cvs.handhelds.org', '/cvs', 'anoncvs', 'anonymous', {'tag': 'V0-99-81', 'module': 'familiar/dist/ipkg'}])
  269. 'cvs://anoncvs:anonymous@cvs.handhelds.org/cvs;tag=V0-99-81;module=familiar/dist/ipkg'
  270. """
  271. (type, host, path, user, pswd, p) = decoded
  272. if not type or not path:
  273. fatal("invalid or missing parameters for url encoding")
  274. url = '%s://' % type
  275. if user:
  276. url += "%s" % user
  277. if pswd:
  278. url += ":%s" % pswd
  279. url += "@"
  280. if host:
  281. url += "%s" % host
  282. url += "%s" % path
  283. if p:
  284. for parm in p.keys():
  285. url += ";%s=%s" % (parm, p[parm])
  286. return url
  287. #######################################################################
  288. def which(path, item, direction = 0):
  289. """Useful function for locating a file in a PATH"""
  290. found = ""
  291. for p in (path or "").split(':'):
  292. if os.path.exists(os.path.join(p, item)):
  293. found = os.path.join(p, item)
  294. if direction == 0:
  295. break
  296. return found
  297. #######################################################################
  298. #######################################################################
  299. #######################################################################
  300. #
  301. # SECTION: Dependency
  302. #
  303. # PURPOSE: Compare build & run dependencies
  304. #
  305. #######################################################################
  306. #######################################################################
  307. def tokenize(mystring):
  308. """Breaks a string like 'foo? (bar) oni? (blah (blah))' into (possibly embedded) lists:
  309. >>> tokenize("x")
  310. ['x']
  311. >>> tokenize("x y")
  312. ['x', 'y']
  313. >>> tokenize("(x y)")
  314. [['x', 'y']]
  315. >>> tokenize("(x y) b c")
  316. [['x', 'y'], 'b', 'c']
  317. >>> tokenize("foo? (bar) oni? (blah (blah))")
  318. ['foo?', ['bar'], 'oni?', ['blah', ['blah']]]
  319. >>> tokenize("sys-apps/linux-headers nls? (sys-devel/gettext)")
  320. ['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']]
  321. """
  322. newtokens = []
  323. curlist = newtokens
  324. prevlists = []
  325. level = 0
  326. accum = ""
  327. for x in mystring:
  328. if x=="(":
  329. if accum:
  330. curlist.append(accum)
  331. accum=""
  332. prevlists.append(curlist)
  333. curlist=[]
  334. level=level+1
  335. elif x==")":
  336. if accum:
  337. curlist.append(accum)
  338. accum=""
  339. if level==0:
  340. print "!!! tokenizer: Unmatched left parenthesis in:\n'"+mystring+"'"
  341. return None
  342. newlist=curlist
  343. curlist=prevlists.pop()
  344. curlist.append(newlist)
  345. level=level-1
  346. elif x in whitespace:
  347. if accum:
  348. curlist.append(accum)
  349. accum=""
  350. else:
  351. accum=accum+x
  352. if accum:
  353. curlist.append(accum)
  354. if (level!=0):
  355. print "!!! tokenizer: Exiting with unterminated parenthesis in:\n'"+mystring+"'"
  356. return None
  357. return newtokens
  358. #######################################################################
  359. def evaluate(tokens,mydefines,allon=0):
  360. """Removes tokens based on whether conditional definitions exist or not.
  361. Recognizes !
  362. >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {})
  363. ['sys-apps/linux-headers']
  364. Negate the flag:
  365. >>> evaluate(['sys-apps/linux-headers', '!nls?', ['sys-devel/gettext']], {})
  366. ['sys-apps/linux-headers', ['sys-devel/gettext']]
  367. Define 'nls':
  368. >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {"nls":1})
  369. ['sys-apps/linux-headers', ['sys-devel/gettext']]
  370. Turn allon on:
  371. >>> evaluate(['sys-apps/linux-headers', 'nls?', ['sys-devel/gettext']], {}, True)
  372. ['sys-apps/linux-headers', ['sys-devel/gettext']]
  373. """
  374. if tokens == None:
  375. return None
  376. mytokens = tokens + [] # this copies the list
  377. pos = 0
  378. while pos < len(mytokens):
  379. if type(mytokens[pos]) == types.ListType:
  380. evaluate(mytokens[pos], mydefines)
  381. if not len(mytokens[pos]):
  382. del mytokens[pos]
  383. continue
  384. elif mytokens[pos][-1] == "?":
  385. cur = mytokens[pos][:-1]
  386. del mytokens[pos]
  387. if allon:
  388. if cur[0] == "!":
  389. del mytokens[pos]
  390. else:
  391. if cur[0] == "!":
  392. if (cur[1:] in mydefines) and (pos < len(mytokens)):
  393. del mytokens[pos]
  394. continue
  395. elif (cur not in mydefines) and (pos < len(mytokens)):
  396. del mytokens[pos]
  397. continue
  398. pos = pos + 1
  399. return mytokens
  400. #######################################################################
  401. def flatten(mytokens):
  402. """Converts nested arrays into a flat arrays:
  403. >>> flatten([1,[2,3]])
  404. [1, 2, 3]
  405. >>> flatten(['sys-apps/linux-headers', ['sys-devel/gettext']])
  406. ['sys-apps/linux-headers', 'sys-devel/gettext']
  407. """
  408. newlist=[]
  409. for x in mytokens:
  410. if type(x)==types.ListType:
  411. newlist.extend(flatten(x))
  412. else:
  413. newlist.append(x)
  414. return newlist
  415. #######################################################################
  416. _package_weights_ = {"pre":-2,"p":0,"alpha":-4,"beta":-3,"rc":-1} # dicts are unordered
  417. _package_ends_ = ["pre", "p", "alpha", "beta", "rc", "cvs", "bk", "HEAD" ] # so we need ordered list
  418. def relparse(myver):
  419. """Parses the last elements of a version number into a triplet, that can
  420. later be compared:
  421. >>> relparse('1.2_pre3')
  422. [1.2, -2, 3.0]
  423. >>> relparse('1.2b')
  424. [1.2, 98, 0]
  425. >>> relparse('1.2')
  426. [1.2, 0, 0]
  427. """
  428. number = 0
  429. p1 = 0
  430. p2 = 0
  431. mynewver = myver.split('_')
  432. if len(mynewver)==2:
  433. # an _package_weights_
  434. number = float(mynewver[0])
  435. match = 0
  436. for x in _package_ends_:
  437. elen = len(x)
  438. if mynewver[1][:elen] == x:
  439. match = 1
  440. p1 = _package_weights_[x]
  441. try:
  442. p2 = float(mynewver[1][elen:])
  443. except:
  444. p2 = 0
  445. break
  446. if not match:
  447. # normal number or number with letter at end
  448. divider = len(myver)-1
  449. if myver[divider:] not in "1234567890":
  450. # letter at end
  451. p1 = ord(myver[divider:])
  452. number = float(myver[0:divider])
  453. else:
  454. number = float(myver)
  455. else:
  456. # normal number or number with letter at end
  457. divider = len(myver)-1
  458. if myver[divider:] not in "1234567890":
  459. #letter at end
  460. p1 = ord(myver[divider:])
  461. number = float(myver[0:divider])
  462. else:
  463. number = float(myver)
  464. return [number,p1,p2]
  465. #######################################################################
  466. __ververify_cache__ = {}
  467. def ververify(myorigval,silent=1):
  468. """Returns 1 if given a valid version string, els 0. Valid versions are in the format
  469. <v1>.<v2>...<vx>[a-z,_{_package_weights_}[vy]]
  470. >>> ververify('2.4.20')
  471. 1
  472. >>> ververify('2.4..20') # two dots
  473. 0
  474. >>> ververify('2.x.20') # 'x' is not numeric
  475. 0
  476. >>> ververify('2.4.20a')
  477. 1
  478. >>> ververify('2.4.20cvs') # only one trailing letter
  479. 0
  480. >>> ververify('1a')
  481. 1
  482. >>> ververify('test_a') # no version at all
  483. 0
  484. >>> ververify('2.4.20_beta1')
  485. 1
  486. >>> ververify('2.4.20_beta')
  487. 1
  488. >>> ververify('2.4.20_wrongext') # _wrongext is no valid trailer
  489. 0
  490. """
  491. # Lookup the cache first
  492. try:
  493. return __ververify_cache__[myorigval]
  494. except KeyError:
  495. pass
  496. if len(myorigval) == 0:
  497. if not silent:
  498. error("package version is empty")
  499. __ververify_cache__[myorigval] = 0
  500. return 0
  501. myval = myorigval.split('.')
  502. if len(myval)==0:
  503. if not silent:
  504. error("package name has empty version string")
  505. __ververify_cache__[myorigval] = 0
  506. return 0
  507. # all but the last version must be a numeric
  508. for x in myval[:-1]:
  509. if not len(x):
  510. if not silent:
  511. error("package version has two points in a row")
  512. __ververify_cache__[myorigval] = 0
  513. return 0
  514. try:
  515. foo = int(x)
  516. except:
  517. if not silent:
  518. error("package version contains non-numeric '"+x+"'")
  519. __ververify_cache__[myorigval] = 0
  520. return 0
  521. if not len(myval[-1]):
  522. if not silent:
  523. error("package version has trailing dot")
  524. __ververify_cache__[myorigval] = 0
  525. return 0
  526. try:
  527. foo = int(myval[-1])
  528. __ververify_cache__[myorigval] = 1
  529. return 1
  530. except:
  531. pass
  532. # ok, our last component is not a plain number or blank, let's continue
  533. if myval[-1][-1] in lowercase:
  534. try:
  535. foo = int(myval[-1][:-1])
  536. return 1
  537. __ververify_cache__[myorigval] = 1
  538. # 1a, 2.0b, etc.
  539. except:
  540. pass
  541. # ok, maybe we have a 1_alpha or 1_beta2; let's see
  542. ep=string.split(myval[-1],"_")
  543. if len(ep)!= 2:
  544. if not silent:
  545. error("package version has more than one letter at then end")
  546. __ververify_cache__[myorigval] = 0
  547. return 0
  548. try:
  549. foo = string.atoi(ep[0])
  550. except:
  551. # this needs to be numeric, i.e. the "1" in "1_alpha"
  552. if not silent:
  553. error("package version must have numeric part before the '_'")
  554. __ververify_cache__[myorigval] = 0
  555. return 0
  556. for mye in _package_ends_:
  557. if ep[1][0:len(mye)] == mye:
  558. if len(mye) == len(ep[1]):
  559. # no trailing numeric is ok
  560. __ververify_cache__[myorigval] = 1
  561. return 1
  562. else:
  563. try:
  564. foo = string.atoi(ep[1][len(mye):])
  565. __ververify_cache__[myorigval] = 1
  566. return 1
  567. except:
  568. # if no _package_weights_ work, *then* we return 0
  569. pass
  570. if not silent:
  571. error("package version extension after '_' is invalid")
  572. __ververify_cache__[myorigval] = 0
  573. return 0
  574. def isjustname(mypkg):
  575. myparts = string.split(mypkg,'-')
  576. for x in myparts:
  577. if ververify(x):
  578. return 0
  579. return 1
  580. _isspecific_cache_={}
  581. def isspecific(mypkg):
  582. "now supports packages with no category"
  583. try:
  584. return __isspecific_cache__[mypkg]
  585. except:
  586. pass
  587. mysplit = string.split(mypkg,"/")
  588. if not isjustname(mysplit[-1]):
  589. __isspecific_cache__[mypkg] = 1
  590. return 1
  591. __isspecific_cache__[mypkg] = 0
  592. return 0
  593. #######################################################################
  594. __pkgsplit_cache__={}
  595. def pkgsplit(mypkg, silent=1):
  596. """This function can be used as a package verification function. If
  597. it is a valid name, pkgsplit will return a list containing:
  598. [pkgname, pkgversion(norev), pkgrev ].
  599. >>> pkgsplit('')
  600. >>> pkgsplit('x')
  601. >>> pkgsplit('x-')
  602. >>> pkgsplit('-1')
  603. >>> pkgsplit('glibc-1.2-8.9-r7')
  604. >>> pkgsplit('glibc-2.2.5-r7')
  605. ['glibc', '2.2.5', 'r7']
  606. >>> pkgsplit('foo-1.2-1')
  607. >>> pkgsplit('Mesa-3.0')
  608. ['Mesa', '3.0', 'r0']
  609. """
  610. try:
  611. return __pkgsplit_cache__[mypkg]
  612. except KeyError:
  613. pass
  614. myparts = string.split(mypkg,'-')
  615. if len(myparts) < 2:
  616. if not silent:
  617. error("package name without name or version part")
  618. __pkgsplit_cache__[mypkg] = None
  619. return None
  620. for x in myparts:
  621. if len(x) == 0:
  622. if not silent:
  623. error("package name with empty name or version part")
  624. __pkgsplit_cache__[mypkg] = None
  625. return None
  626. # verify rev
  627. revok = 0
  628. myrev = myparts[-1]
  629. ververify(myrev, silent)
  630. if len(myrev) and myrev[0] == "r":
  631. try:
  632. string.atoi(myrev[1:])
  633. revok = 1
  634. except:
  635. pass
  636. if revok:
  637. if ververify(myparts[-2]):
  638. if len(myparts) == 2:
  639. __pkgsplit_cache__[mypkg] = None
  640. return None
  641. else:
  642. for x in myparts[:-2]:
  643. if ververify(x):
  644. __pkgsplit_cache__[mypkg]=None
  645. return None
  646. # names can't have versiony looking parts
  647. myval=[string.join(myparts[:-2],"-"),myparts[-2],myparts[-1]]
  648. __pkgsplit_cache__[mypkg]=myval
  649. return myval
  650. else:
  651. __pkgsplit_cache__[mypkg] = None
  652. return None
  653. elif ververify(myparts[-1],silent):
  654. if len(myparts)==1:
  655. if not silent:
  656. print "!!! Name error in",mypkg+": missing name part."
  657. __pkgsplit_cache__[mypkg]=None
  658. return None
  659. else:
  660. for x in myparts[:-1]:
  661. if ververify(x):
  662. if not silent: error("package name has multiple version parts")
  663. __pkgsplit_cache__[mypkg] = None
  664. return None
  665. myval = [string.join(myparts[:-1],"-"), myparts[-1],"r0"]
  666. __pkgsplit_cache__[mypkg] = myval
  667. return myval
  668. else:
  669. __pkgsplit_cache__[mypkg] = None
  670. return None
  671. #######################################################################
  672. __catpkgsplit_cache__ = {}
  673. def catpkgsplit(mydata,silent=1):
  674. """returns [cat, pkgname, version, rev ]
  675. >>> catpkgsplit('sys-libs/glibc-1.2-r7')
  676. ['sys-libs', 'glibc', '1.2', 'r7']
  677. >>> catpkgsplit('glibc-1.2-r7')
  678. [None, 'glibc', '1.2', 'r7']
  679. """
  680. try:
  681. return __catpkgsplit_cache__[mydata]
  682. except KeyError:
  683. pass
  684. cat = os.path.basename(os.path.dirname(mydata))
  685. mydata = os.path.join(cat, os.path.basename(mydata))
  686. if mydata[-3:] == '.bb':
  687. mydata = mydata[:-3]
  688. mysplit = mydata.split("/")
  689. p_split = None
  690. splitlen = len(mysplit)
  691. if splitlen == 1:
  692. retval = [None]
  693. p_split = pkgsplit(mydata,silent)
  694. else:
  695. retval = [mysplit[splitlen - 2]]
  696. p_split = pkgsplit(mysplit[splitlen - 1],silent)
  697. if not p_split:
  698. __catpkgsplit_cache__[mydata] = None
  699. return None
  700. retval.extend(p_split)
  701. __catpkgsplit_cache__[mydata] = retval
  702. return retval
  703. #######################################################################
  704. __vercmp_cache__ = {}
  705. def vercmp(val1,val2):
  706. """This takes two version strings and returns an integer to tell you whether
  707. the versions are the same, val1>val2 or val2>val1.
  708. >>> vercmp('1', '2')
  709. -1.0
  710. >>> vercmp('2', '1')
  711. 1.0
  712. >>> vercmp('1', '1.0')
  713. 0
  714. >>> vercmp('1', '1.1')
  715. -1.0
  716. >>> vercmp('1.1', '1_p2')
  717. 1.0
  718. """
  719. # quick short-circuit
  720. if val1 == val2:
  721. return 0
  722. valkey = val1+" "+val2
  723. # cache lookup
  724. try:
  725. return __vercmp_cache__[valkey]
  726. try:
  727. return - __vercmp_cache__[val2+" "+val1]
  728. except KeyError:
  729. pass
  730. except KeyError:
  731. pass
  732. # consider 1_p2 vc 1.1
  733. # after expansion will become (1_p2,0) vc (1,1)
  734. # then 1_p2 is compared with 1 before 0 is compared with 1
  735. # to solve the bug we need to convert it to (1,0_p2)
  736. # by splitting _prepart part and adding it back _after_expansion
  737. val1_prepart = val2_prepart = ''
  738. if val1.count('_'):
  739. val1, val1_prepart = val1.split('_', 1)
  740. if val2.count('_'):
  741. val2, val2_prepart = val2.split('_', 1)
  742. # replace '-' by '.'
  743. # FIXME: Is it needed? can val1/2 contain '-'?
  744. val1 = string.split(val1,'-')
  745. if len(val1) == 2:
  746. val1[0] = val1[0] +"."+ val1[1]
  747. val2 = string.split(val2,'-')
  748. if len(val2) == 2:
  749. val2[0] = val2[0] +"."+ val2[1]
  750. val1 = string.split(val1[0],'.')
  751. val2 = string.split(val2[0],'.')
  752. # add back decimal point so that .03 does not become "3" !
  753. for x in range(1,len(val1)):
  754. if val1[x][0] == '0' :
  755. val1[x] = '.' + val1[x]
  756. for x in range(1,len(val2)):
  757. if val2[x][0] == '0' :
  758. val2[x] = '.' + val2[x]
  759. # extend varion numbers
  760. if len(val2) < len(val1):
  761. val2.extend(["0"]*(len(val1)-len(val2)))
  762. elif len(val1) < len(val2):
  763. val1.extend(["0"]*(len(val2)-len(val1)))
  764. # add back _prepart tails
  765. if val1_prepart:
  766. val1[-1] += '_' + val1_prepart
  767. if val2_prepart:
  768. val2[-1] += '_' + val2_prepart
  769. # The above code will extend version numbers out so they
  770. # have the same number of digits.
  771. for x in range(0,len(val1)):
  772. cmp1 = relparse(val1[x])
  773. cmp2 = relparse(val2[x])
  774. for y in range(0,3):
  775. myret = cmp1[y] - cmp2[y]
  776. if myret != 0:
  777. __vercmp_cache__[valkey] = myret
  778. return myret
  779. __vercmp_cache__[valkey] = 0
  780. return 0
  781. #######################################################################
  782. def pkgcmp(pkg1,pkg2):
  783. """ Compares two packages, which should have been split via
  784. pkgsplit(). if the return value val is less than zero, then pkg2 is
  785. newer than pkg1, zero if equal and positive if older.
  786. >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r7'])
  787. 0
  788. >>> pkgcmp(['glibc', '2.2.5', 'r4'], ['glibc', '2.2.5', 'r7'])
  789. -1
  790. >>> pkgcmp(['glibc', '2.2.5', 'r7'], ['glibc', '2.2.5', 'r2'])
  791. 1
  792. """
  793. mycmp = vercmp(pkg1[1],pkg2[1])
  794. if mycmp > 0:
  795. return 1
  796. if mycmp < 0:
  797. return -1
  798. r1=string.atoi(pkg1[2][1:])
  799. r2=string.atoi(pkg2[2][1:])
  800. if r1 > r2:
  801. return 1
  802. if r2 > r1:
  803. return -1
  804. return 0
  805. #######################################################################
  806. def dep_parenreduce(mysplit, mypos=0):
  807. """Accepts a list of strings, and converts '(' and ')' surrounded items to sub-lists:
  808. >>> dep_parenreduce([''])
  809. ['']
  810. >>> dep_parenreduce(['1', '2', '3'])
  811. ['1', '2', '3']
  812. >>> dep_parenreduce(['1', '(', '2', '3', ')', '4'])
  813. ['1', ['2', '3'], '4']
  814. """
  815. while mypos < len(mysplit):
  816. if mysplit[mypos] == "(":
  817. firstpos = mypos
  818. mypos = mypos + 1
  819. while mypos < len(mysplit):
  820. if mysplit[mypos] == ")":
  821. mysplit[firstpos:mypos+1] = [mysplit[firstpos+1:mypos]]
  822. mypos = firstpos
  823. break
  824. elif mysplit[mypos] == "(":
  825. # recurse
  826. mysplit = dep_parenreduce(mysplit,mypos)
  827. mypos = mypos + 1
  828. mypos = mypos + 1
  829. return mysplit
  830. def dep_opconvert(mysplit, myuse):
  831. "Does dependency operator conversion"
  832. mypos = 0
  833. newsplit = []
  834. while mypos < len(mysplit):
  835. if type(mysplit[mypos]) == types.ListType:
  836. newsplit.append(dep_opconvert(mysplit[mypos],myuse))
  837. mypos += 1
  838. elif mysplit[mypos] == ")":
  839. # mismatched paren, error
  840. return None
  841. elif mysplit[mypos]=="||":
  842. if ((mypos+1)>=len(mysplit)) or (type(mysplit[mypos+1])!=types.ListType):
  843. # || must be followed by paren'd list
  844. return None
  845. try:
  846. mynew = dep_opconvert(mysplit[mypos+1],myuse)
  847. except Exception, e:
  848. error("unable to satisfy OR dependancy: " + string.join(mysplit," || "))
  849. raise e
  850. mynew[0:0] = ["||"]
  851. newsplit.append(mynew)
  852. mypos += 2
  853. elif mysplit[mypos][-1] == "?":
  854. # use clause, i.e "gnome? ( foo bar )"
  855. # this is a quick and dirty hack so that repoman can enable all USE vars:
  856. if (len(myuse) == 1) and (myuse[0] == "*"):
  857. # enable it even if it's ! (for repoman) but kill it if it's
  858. # an arch variable that isn't for this arch. XXX Sparc64?
  859. if (mysplit[mypos][:-1] not in settings.usemask) or \
  860. (mysplit[mypos][:-1]==settings["ARCH"]):
  861. enabled=1
  862. else:
  863. enabled=0
  864. else:
  865. if mysplit[mypos][0] == "!":
  866. myusevar = mysplit[mypos][1:-1]
  867. enabled = not myusevar in myuse
  868. #if myusevar in myuse:
  869. # enabled = 0
  870. #else:
  871. # enabled = 1
  872. else:
  873. myusevar=mysplit[mypos][:-1]
  874. enabled = myusevar in myuse
  875. #if myusevar in myuse:
  876. # enabled=1
  877. #else:
  878. # enabled=0
  879. if (mypos +2 < len(mysplit)) and (mysplit[mypos+2] == ":"):
  880. # colon mode
  881. if enabled:
  882. # choose the first option
  883. if type(mysplit[mypos+1]) == types.ListType:
  884. newsplit.append(dep_opconvert(mysplit[mypos+1],myuse))
  885. else:
  886. newsplit.append(mysplit[mypos+1])
  887. else:
  888. # choose the alternate option
  889. if type(mysplit[mypos+1]) == types.ListType:
  890. newsplit.append(dep_opconvert(mysplit[mypos+3],myuse))
  891. else:
  892. newsplit.append(mysplit[mypos+3])
  893. mypos += 4
  894. else:
  895. # normal use mode
  896. if enabled:
  897. if type(mysplit[mypos+1]) == types.ListType:
  898. newsplit.append(dep_opconvert(mysplit[mypos+1],myuse))
  899. else:
  900. newsplit.append(mysplit[mypos+1])
  901. # otherwise, continue
  902. mypos += 2
  903. else:
  904. # normal item
  905. newsplit.append(mysplit[mypos])
  906. mypos += 1
  907. return newsplit
  908. class digraph:
  909. """beautiful directed graph object"""
  910. def __init__(self):
  911. self.dict={}
  912. #okeys = keys, in order they were added (to optimize firstzero() ordering)
  913. self.okeys=[]
  914. self.__callback_cache=[]
  915. def __str__(self):
  916. str = ""
  917. for key in self.okeys:
  918. str += "%s:\t%s\n" % (key, self.dict[key][1])
  919. return str
  920. def addnode(self,mykey,myparent):
  921. if not mykey in self.dict:
  922. self.okeys.append(mykey)
  923. if myparent==None:
  924. self.dict[mykey]=[0,[]]
  925. else:
  926. self.dict[mykey]=[0,[myparent]]
  927. self.dict[myparent][0]=self.dict[myparent][0]+1
  928. return
  929. if myparent and (not myparent in self.dict[mykey][1]):
  930. self.dict[mykey][1].append(myparent)
  931. self.dict[myparent][0]=self.dict[myparent][0]+1
  932. def delnode(self,mykey, ref = 1):
  933. """Delete a node
  934. If ref is 1, remove references to this node from other nodes.
  935. If ref is 2, remove nodes that reference this node."""
  936. if not mykey in self.dict:
  937. return
  938. for x in self.dict[mykey][1]:
  939. self.dict[x][0]=self.dict[x][0]-1
  940. del self.dict[mykey]
  941. while 1:
  942. try:
  943. self.okeys.remove(mykey)
  944. except ValueError:
  945. break
  946. if ref:
  947. __kill = []
  948. for k in self.okeys:
  949. if mykey in self.dict[k][1]:
  950. if ref == 1 or ref == 2:
  951. self.dict[k][1].remove(mykey)
  952. if ref == 2:
  953. __kill.append(k)
  954. for l in __kill:
  955. self.delnode(l, ref)
  956. def allnodes(self):
  957. "returns all nodes in the dictionary"
  958. return self.dict.keys()
  959. def firstzero(self):
  960. "returns first node with zero references, or NULL if no such node exists"
  961. for x in self.okeys:
  962. if self.dict[x][0]==0:
  963. return x
  964. return None
  965. def firstnonzero(self):
  966. "returns first node with nonzero references, or NULL if no such node exists"
  967. for x in self.okeys:
  968. if self.dict[x][0]!=0:
  969. return x
  970. return None
  971. def allzeros(self):
  972. "returns all nodes with zero references, or NULL if no such node exists"
  973. zerolist = []
  974. for x in self.dict.keys():
  975. if self.dict[x][0]==0:
  976. zerolist.append(x)
  977. return zerolist
  978. def hasallzeros(self):
  979. "returns 0/1, Are all nodes zeros? 1 : 0"
  980. zerolist = []
  981. for x in self.dict.keys():
  982. if self.dict[x][0]!=0:
  983. return 0
  984. return 1
  985. def empty(self):
  986. if len(self.dict)==0:
  987. return 1
  988. return 0
  989. def hasnode(self,mynode):
  990. return mynode in self.dict
  991. def getparents(self, item):
  992. if not self.hasnode(item):
  993. return []
  994. return self.dict[item][1]
  995. def getchildren(self, item):
  996. if not self.hasnode(item):
  997. return []
  998. children = [i for i in self.okeys if item in self.getparents(i)]
  999. return children
  1000. def walkdown(self, item, callback, debug = None, usecache = False):
  1001. if not self.hasnode(item):
  1002. return 0
  1003. if usecache:
  1004. if self.__callback_cache.count(item):
  1005. if debug:
  1006. print "hit cache for item: %s" % item
  1007. return 1
  1008. parents = self.getparents(item)
  1009. children = self.getchildren(item)
  1010. for p in parents:
  1011. if p in children:
  1012. # print "%s is both parent and child of %s" % (p, item)
  1013. if usecache:
  1014. self.__callback_cache.append(p)
  1015. ret = callback(self, p)
  1016. if ret == 0:
  1017. return 0
  1018. continue
  1019. if item == p:
  1020. print "eek, i'm my own parent!"
  1021. return 0
  1022. if debug:
  1023. print "item: %s, p: %s" % (item, p)
  1024. ret = self.walkdown(p, callback, debug, usecache)
  1025. if ret == 0:
  1026. return 0
  1027. if usecache:
  1028. self.__callback_cache.append(item)
  1029. return callback(self, item)
  1030. def walkup(self, item, callback):
  1031. if not self.hasnode(item):
  1032. return 0
  1033. parents = self.getparents(item)
  1034. children = self.getchildren(item)
  1035. for c in children:
  1036. if c in parents:
  1037. ret = callback(self, item)
  1038. if ret == 0:
  1039. return 0
  1040. continue
  1041. if item == c:
  1042. print "eek, i'm my own child!"
  1043. return 0
  1044. ret = self.walkup(c, callback)
  1045. if ret == 0:
  1046. return 0
  1047. return callback(self, item)
  1048. def copy(self):
  1049. mygraph=digraph()
  1050. for x in self.dict.keys():
  1051. mygraph.dict[x]=self.dict[x][:]
  1052. mygraph.okeys=self.okeys[:]
  1053. return mygraph
  1054. if __name__ == "__main__":
  1055. import doctest, bb
  1056. doctest.testmod(bb)