PageRenderTime 96ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 1ms

/conary/build/packagepolicy.py

https://bitbucket.org/brcha/conary
Python | 4773 lines | 4315 code | 141 blank | 317 comment | 169 complexity | 4810480e32e4d213727191b6645265c2 MD5 | raw file
Possible License(s): GPL-3.0
  1. #
  2. # Copyright (c) rPath, Inc.
  3. #
  4. # This program is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, either version 3 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. #
  17. """
  18. Module used after C{%(destdir)s} has been finalized to create the
  19. initial packaging. Also contains error reporting.
  20. """
  21. import codecs
  22. import imp
  23. import itertools
  24. import os
  25. import re
  26. import site
  27. import sre_constants
  28. import stat
  29. import subprocess
  30. import sys
  31. from conary import files, trove
  32. from conary.build import buildpackage, filter, policy, recipe, tags, use
  33. from conary.build import smartform
  34. from conary.deps import deps
  35. from conary.lib import elf, magic, util, pydeps, fixedglob, graph
  36. from conary.build.action import TARGET_LINUX
  37. from conary.build.action import TARGET_WINDOWS
  38. try:
  39. from xml.etree import ElementTree
  40. except ImportError:
  41. try:
  42. from elementtree import ElementTree
  43. except ImportError:
  44. ElementTree = None
  45. # Helper class
  46. class _DatabaseDepCache(object):
  47. __slots__ = ['db', 'cache']
  48. def __init__(self, db):
  49. self.db = db
  50. self.cache = {}
  51. def getProvides(self, depSetList):
  52. ret = {}
  53. missing = []
  54. for depSet in depSetList:
  55. if depSet in self.cache:
  56. ret[depSet] = self.cache[depSet]
  57. else:
  58. missing.append(depSet)
  59. newresults = self.db.getTrovesWithProvides(missing)
  60. ret.update(newresults)
  61. self.cache.update(newresults)
  62. return ret
  63. class _filterSpec(policy.Policy):
  64. """
  65. Pure virtual base class from which C{ComponentSpec} and C{PackageSpec}
  66. are derived.
  67. """
  68. bucket = policy.PACKAGE_CREATION
  69. processUnmodified = False
  70. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  71. def __init__(self, *args, **keywords):
  72. self.extraFilters = []
  73. policy.Policy.__init__(self, *args, **keywords)
  74. def updateArgs(self, *args, **keywords):
  75. """
  76. Call derived classes (C{ComponentSpec} or C{PackageSpec}) as::
  77. ThisClass('<name>', 'filterexp1', 'filterexp2')
  78. where C{filterexp} is either a regular expression or a
  79. tuple of C{(regexp[, setmodes[, unsetmodes]])}
  80. """
  81. if args:
  82. theName = args[0]
  83. for filterexp in args[1:]:
  84. self.extraFilters.append((theName, filterexp))
  85. policy.Policy.updateArgs(self, **keywords)
  86. class _addInfo(policy.Policy):
  87. """
  88. Pure virtual class for policies that add information such as tags,
  89. requirements, and provision, to files.
  90. """
  91. bucket = policy.PACKAGE_CREATION
  92. processUnmodified = False
  93. requires = (
  94. ('PackageSpec', policy.REQUIRED_PRIOR),
  95. )
  96. keywords = {
  97. 'included': {},
  98. 'excluded': {}
  99. }
  100. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  101. def updateArgs(self, *args, **keywords):
  102. """
  103. Call as::
  104. C{I{ClassName}(I{info}, I{filterexp})}
  105. or::
  106. C{I{ClassName}(I{info}, exceptions=I{filterexp})}
  107. where C{I{filterexp}} is either a regular expression or a
  108. tuple of C{(regexp[, setmodes[, unsetmodes]])}
  109. """
  110. if args:
  111. args = list(args)
  112. info = args.pop(0)
  113. if args:
  114. if not self.included:
  115. self.included = {}
  116. if info not in self.included:
  117. self.included[info] = []
  118. self.included[info].extend(args)
  119. elif 'exceptions' in keywords:
  120. # not the usual exception handling, this is an exception
  121. if not self.excluded:
  122. self.excluded = {}
  123. if info not in self.excluded:
  124. self.excluded[info] = []
  125. self.excluded[info].append(keywords.pop('exceptions'))
  126. else:
  127. raise TypeError, 'no paths provided'
  128. policy.Policy.updateArgs(self, **keywords)
  129. def doProcess(self, recipe):
  130. # for filters
  131. self.rootdir = self.rootdir % recipe.macros
  132. # instantiate filters
  133. d = {}
  134. for info in self.included:
  135. newinfo = info % recipe.macros
  136. l = []
  137. for item in self.included[info]:
  138. l.append(filter.Filter(item, recipe.macros))
  139. d[newinfo] = l
  140. self.included = d
  141. d = {}
  142. for info in self.excluded:
  143. newinfo = info % recipe.macros
  144. l = []
  145. for item in self.excluded[info]:
  146. l.append(filter.Filter(item, recipe.macros))
  147. d[newinfo] = l
  148. self.excluded = d
  149. policy.Policy.doProcess(self, recipe)
  150. def doFile(self, path):
  151. fullpath = self.recipe.macros.destdir+path
  152. if not util.isregular(fullpath) and not os.path.islink(fullpath):
  153. return
  154. self.runInfo(path)
  155. def runInfo(self, path):
  156. 'pure virtual'
  157. pass
  158. class Config(policy.Policy):
  159. """
  160. NAME
  161. ====
  162. B{C{r.Config()}} - Mark files as configuration files
  163. SYNOPSIS
  164. ========
  165. C{r.Config([I{filterexp}] || [I{exceptions=filterexp}])}
  166. DESCRIPTION
  167. ===========
  168. The C{r.Config} policy marks all files below C{%(sysconfdir)s}
  169. (that is, C{/etc}) and C{%(taghandlerdir)s} (that is,
  170. C{/usr/libexec/conary/tags/}), and any other files explicitly
  171. mentioned, as configuration files.
  172. - To mark files as exceptions, use
  173. C{r.Config(exceptions='I{filterexp}')}.
  174. - To mark explicit inclusions as configuration files, use:
  175. C{r.Config('I{filterexp}')}
  176. A file marked as a Config file cannot also be marked as a
  177. Transient file or an InitialContents file. Conary enforces this
  178. requirement.
  179. EXAMPLES
  180. ========
  181. C{r.Config(exceptions='%(sysconfdir)s/X11/xkb/xkbcomp')}
  182. The file C{/etc/X11/xkb/xkbcomp} is marked as an exception, since it is
  183. not actually a configuration file even though it is within the C{/etc}
  184. (C{%(sysconfdir)s}) directory hierarchy and would be marked as a
  185. configuration file by default.
  186. C{r.Config('%(mmdir)s/Mailman/mm_cfg.py')}
  187. Marks the file C{%(mmdir)s/Mailman/mm_cfg.py} as a configuration file;
  188. it would not be automatically marked as a configuration file otherwise.
  189. """
  190. bucket = policy.PACKAGE_CREATION
  191. processUnmodified = True
  192. requires = (
  193. # for :config component, ComponentSpec must run after Config
  194. # Otherwise, this policy would follow PackageSpec and just set isConfig
  195. # on each config file
  196. ('ComponentSpec', policy.REQUIRED_SUBSEQUENT),
  197. )
  198. invariantinclusions = [ '%(sysconfdir)s/', '%(taghandlerdir)s/']
  199. invariantexceptions = [ '%(userinfodir)s/', '%(groupinfodir)s' ]
  200. def doFile(self, filename):
  201. m = self.recipe.magic[filename]
  202. if m and m.name == "ELF":
  203. # an ELF file cannot be a config file, some programs put
  204. # ELF files under /etc (X, for example), and tag handlers
  205. # can be ELF or shell scripts; we just want tag handlers
  206. # to be config files if they are shell scripts.
  207. # Just in case it was not intentional, warn...
  208. if self.macros.sysconfdir in filename:
  209. self.info('ELF file %s found in config directory', filename)
  210. return
  211. fullpath = self.macros.destdir + filename
  212. if os.path.isfile(fullpath) and util.isregular(fullpath):
  213. if self._fileIsBinary(filename, fullpath):
  214. self.error("binary file '%s' is marked as config" % \
  215. filename)
  216. self._markConfig(filename, fullpath)
  217. def _fileIsBinary(self, path, fn, maxsize=None, decodeFailIsError=True):
  218. limit = os.stat(fn)[stat.ST_SIZE]
  219. if maxsize is not None and limit > maxsize:
  220. self.warn('%s: file size %d longer than max %d',
  221. path, limit, maxsize)
  222. return True
  223. # we'll consider file to be binary file if we don't find any
  224. # good reason to mark it as text, or if we find a good reason
  225. # to mark it as binary
  226. foundFF = False
  227. foundNL = False
  228. f = open(fn, 'r')
  229. try:
  230. while f.tell() < limit:
  231. buf = f.read(65536)
  232. if chr(0) in buf:
  233. self.warn('%s: file contains NULL byte', path)
  234. return True
  235. if '\xff\xff' in buf:
  236. self.warn('%s: file contains 0xFFFF sequence', path)
  237. return True
  238. if '\xff' in buf:
  239. foundFF = True
  240. if '\n' in buf:
  241. foundNL = True
  242. finally:
  243. f.close()
  244. if foundFF and not foundNL:
  245. self.error('%s: found 0xFF without newline', path)
  246. utf8 = codecs.open(fn, 'r', 'utf-8')
  247. win1252 = codecs.open(fn, 'r', 'windows-1252')
  248. try:
  249. try:
  250. while utf8.tell() < limit:
  251. utf8.read(65536)
  252. except UnicodeDecodeError, e:
  253. # Still want to print a warning if it is not unicode;
  254. # Note that Code Page 1252 is considered a legacy
  255. # encoding on Windows
  256. self.warn('%s: %s', path, str(e))
  257. try:
  258. while win1252.tell() < limit:
  259. win1252.read(65536)
  260. except UnicodeDecodeError, e:
  261. self.warn('%s: %s', path, str(e))
  262. return decodeFailIsError
  263. finally:
  264. utf8.close()
  265. win1252.close()
  266. return False
  267. def _addTrailingNewline(self, filename, fullpath):
  268. # FIXME: This exists only for stability; there is no longer
  269. # any need to add trailing newlines to config files. This
  270. # also violates the rule that no files are modified after
  271. # destdir modification has been completed.
  272. self.warn("adding trailing newline to config file '%s'" % \
  273. filename)
  274. mode = os.lstat(fullpath)[stat.ST_MODE]
  275. oldmode = None
  276. if mode & 0600 != 0600:
  277. # need to be able to read and write the file to fix it
  278. oldmode = mode
  279. os.chmod(fullpath, mode|0600)
  280. f = open(fullpath, 'a')
  281. f.seek(0, 2)
  282. f.write('\n')
  283. f.close()
  284. if oldmode is not None:
  285. os.chmod(fullpath, oldmode)
  286. def _markConfig(self, filename, fullpath):
  287. self.info(filename)
  288. f = file(fullpath)
  289. f.seek(0, 2)
  290. if f.tell():
  291. # file has contents
  292. f.seek(-1, 2)
  293. lastchar = f.read(1)
  294. f.close()
  295. if lastchar != '\n':
  296. self._addTrailingNewline(filename, fullpath)
  297. f.close()
  298. self.recipe.ComponentSpec(_config=filename)
  299. class ComponentSpec(_filterSpec):
  300. """
  301. NAME
  302. ====
  303. B{C{r.ComponentSpec()}} - Determines which component each file is in
  304. SYNOPSIS
  305. ========
  306. C{r.ComponentSpec([I{componentname}, I{filterexp}] || [I{packagename}:I{componentname}, I{filterexp}])}
  307. DESCRIPTION
  308. ===========
  309. The C{r.ComponentSpec} policy includes the filter expressions that specify
  310. the default assignment of files to components. The expressions are
  311. considered in the order in which they are evaluated in the recipe, and the
  312. first match wins. After all the recipe-provided expressions are
  313. evaluated, the default expressions are evaluated. If no expression
  314. matches, then the file is assigned to the C{catchall} component.
  315. Note that in the C{I{packagename}:I{componentname}} form, the C{:}
  316. must be literal, it cannot be part of a macro.
  317. KEYWORDS
  318. ========
  319. B{catchall} : Specify the component name which gets all otherwise
  320. unassigned files. Default: C{runtime}
  321. EXAMPLES
  322. ========
  323. C{r.ComponentSpec('manual', '%(contentdir)s/manual/')}
  324. Uses C{r.ComponentSpec} to specify that all files below the
  325. C{%(contentdir)s/manual/} directory are part of the C{:manual} component.
  326. C{r.ComponentSpec('foo:bar', '%(sharedir)s/foo/')}
  327. Uses C{r.ComponentSpec} to specify that all files below the
  328. C{%(sharedir)s/foo/} directory are part of the C{:bar} component
  329. of the C{foo} package, avoiding the need to invoke both the
  330. C{ComponentSpec} and C{PackageSpec} policies.
  331. C{r.ComponentSpec(catchall='data')}
  332. Uses C{r.ComponentSpec} to specify that all files not otherwise specified
  333. go into the C{:data} component instead of the default {:runtime}
  334. component.
  335. """
  336. requires = (
  337. ('Config', policy.REQUIRED_PRIOR),
  338. ('PackageSpec', policy.REQUIRED_SUBSEQUENT),
  339. )
  340. keywords = { 'catchall': 'runtime' }
  341. def __init__(self, *args, **keywords):
  342. """
  343. @keyword catchall: The component name which gets all otherwise
  344. unassigned files. Default: C{runtime}
  345. """
  346. _filterSpec.__init__(self, *args, **keywords)
  347. self.configFilters = []
  348. self.derivedFilters = []
  349. def updateArgs(self, *args, **keywords):
  350. if '_config' in keywords:
  351. configPath=keywords.pop('_config')
  352. self.recipe.PackageSpec(_config=configPath)
  353. if args:
  354. name = args[0]
  355. if ':' in name:
  356. package, name = name.split(':')
  357. args = list(itertools.chain([name], args[1:]))
  358. if package:
  359. # we've got a package as well as a component, pass it on
  360. pkgargs = list(itertools.chain((package,), args[1:]))
  361. self.recipe.PackageSpec(*pkgargs)
  362. _filterSpec.updateArgs(self, *args, **keywords)
  363. def doProcess(self, recipe):
  364. compFilters = []
  365. self.macros = recipe.macros
  366. self.rootdir = self.rootdir % recipe.macros
  367. self.loadFilterDirs()
  368. # The extras need to come before base in order to override decisions
  369. # in the base subfilters; invariants come first for those very few
  370. # specs that absolutely should not be overridden in recipes.
  371. for filteritem in itertools.chain(self.invariantFilters,
  372. self.extraFilters,
  373. self.derivedFilters,
  374. self.configFilters,
  375. self.baseFilters):
  376. if not isinstance(filteritem, (filter.Filter, filter.PathSet)):
  377. name = filteritem[0] % self.macros
  378. assert(name != 'source')
  379. args, kwargs = self.filterExpArgs(filteritem[1:], name=name)
  380. filteritem = filter.Filter(*args, **kwargs)
  381. compFilters.append(filteritem)
  382. # by default, everything that hasn't matched a filter pattern yet
  383. # goes in the catchall component ('runtime' by default)
  384. compFilters.append(filter.Filter('.*', self.macros, name=self.catchall))
  385. # pass these down to PackageSpec for building the package
  386. recipe.PackageSpec(compFilters=compFilters)
  387. def loadFilterDirs(self):
  388. invariantFilterMap = {}
  389. baseFilterMap = {}
  390. self.invariantFilters = []
  391. self.baseFilters = []
  392. # Load all component python files
  393. for componentDir in self.recipe.cfg.componentDirs:
  394. for filterType, map in (('invariant', invariantFilterMap),
  395. ('base', baseFilterMap)):
  396. oneDir = os.sep.join((componentDir, filterType))
  397. if not os.path.isdir(oneDir):
  398. continue
  399. for filename in os.listdir(oneDir):
  400. fullpath = os.sep.join((oneDir, filename))
  401. if (not filename.endswith('.py') or
  402. not util.isregular(fullpath)):
  403. continue
  404. self.loadFilter(filterType, map, filename, fullpath)
  405. # populate the lists with dependency-sorted information
  406. for filterType, map, filterList in (
  407. ('invariant', invariantFilterMap, self.invariantFilters),
  408. ('base', baseFilterMap, self.baseFilters)):
  409. dg = graph.DirectedGraph()
  410. for filterName in map.keys():
  411. dg.addNode(filterName)
  412. filter, follows, precedes = map[filterName]
  413. def warnMissing(missing):
  414. self.error('%s depends on missing %s', filterName, missing)
  415. for prior in follows:
  416. if not prior in map:
  417. warnMissing(prior)
  418. dg.addEdge(prior, filterName)
  419. for subsequent in precedes:
  420. if not subsequent in map:
  421. warnMissing(subsequent)
  422. dg.addEdge(filterName, subsequent)
  423. # test for dependency loops
  424. depLoops = [x for x in dg.getStronglyConnectedComponents()
  425. if len(x) > 1]
  426. if depLoops:
  427. self.error('dependency loop(s) in component filters: %s',
  428. ' '.join(sorted(':'.join(x)
  429. for x in sorted(list(depLoops)))))
  430. return
  431. # Create a stably-sorted list of config filters where
  432. # the filter is not empty. (An empty filter with both
  433. # follows and precedes specified can be used to induce
  434. # ordering between otherwise unrelated components.)
  435. #for name in dg.getTotalOrdering(nodeSort=lambda a, b: cmp(a,b)):
  436. for name in dg.getTotalOrdering():
  437. filters = map[name][0]
  438. if not filters:
  439. continue
  440. componentName = filters[0]
  441. for filterExp in filters[1]:
  442. filterList.append((componentName, filterExp))
  443. def loadFilter(self, filterType, map, filename, fullpath):
  444. # do not load shared libraries
  445. desc = [x for x in imp.get_suffixes() if x[0] == '.py'][0]
  446. f = file(fullpath)
  447. modname = filename[:-3]
  448. m = imp.load_module(modname, f, fullpath, desc)
  449. f.close()
  450. if not 'filters' in m.__dict__:
  451. self.warn('%s missing "filters"; not a valid component'
  452. ' specification file', fullpath)
  453. return
  454. filters = m.__dict__['filters']
  455. if filters and len(filters) > 1 and type(filters[1]) not in (list,
  456. tuple):
  457. self.error('invalid expression in %s: filters specification'
  458. " must be ('name', ('expression', ...))", fullpath)
  459. follows = ()
  460. if 'follows' in m.__dict__:
  461. follows = m.__dict__['follows']
  462. precedes = ()
  463. if 'precedes' in m.__dict__:
  464. precedes = m.__dict__['precedes']
  465. map[modname] = (filters, follows, precedes)
  466. class PackageSpec(_filterSpec):
  467. """
  468. NAME
  469. ====
  470. B{C{r.PackageSpec()}} - Determines which package each file is in
  471. SYNOPSIS
  472. ========
  473. C{r.PackageSpec(I{packagename}, I{filterexp})}
  474. DESCRIPTION
  475. ===========
  476. The C{r.PackageSpec()} policy determines which package each file
  477. is in. (Use C{r.ComponentSpec()} to specify the component without
  478. specifying the package, or to specify C{I{package}:I{component}}
  479. in one invocation.)
  480. EXAMPLES
  481. ========
  482. C{r.PackageSpec('openssh-server', '%(sysconfdir)s/pam.d/sshd')}
  483. Specifies that the file C{%(sysconfdir)s/pam.d/sshd} is in the package
  484. C{openssh-server} rather than the default (which in this case would have
  485. been C{openssh} because this example was provided by C{openssh.recipe}).
  486. """
  487. requires = (
  488. ('ComponentSpec', policy.REQUIRED_PRIOR),
  489. )
  490. keywords = { 'compFilters': None }
  491. def __init__(self, *args, **keywords):
  492. """
  493. @keyword compFilters: reserved for C{ComponentSpec} to pass information
  494. needed by C{PackageSpec}.
  495. """
  496. _filterSpec.__init__(self, *args, **keywords)
  497. self.configFiles = []
  498. self.derivedFilters = []
  499. def updateArgs(self, *args, **keywords):
  500. if '_config' in keywords:
  501. self.configFiles.append(keywords.pop('_config'))
  502. # keep a list of packages filtered for in PackageSpec in the recipe
  503. if args:
  504. newTrove = args[0] % self.recipe.macros
  505. self.recipe.packages[newTrove] = True
  506. _filterSpec.updateArgs(self, *args, **keywords)
  507. def preProcess(self):
  508. self.pkgFilters = []
  509. recipe = self.recipe
  510. self.destdir = recipe.macros.destdir
  511. if self.exceptions:
  512. self.warn('PackageSpec does not honor exceptions')
  513. self.exceptions = None
  514. if self.inclusions:
  515. # would have an effect only with exceptions listed, so no warning...
  516. self.inclusions = None
  517. # userinfo and groupinfo are invariant filters, so they must come first
  518. for infoType in ('user', 'group'):
  519. infoDir = '%%(%sinfodir)s' % infoType % self.macros
  520. realDir = util.joinPaths(self.destdir, infoDir)
  521. if not os.path.isdir(realDir):
  522. continue
  523. for infoPkgName in os.listdir(realDir):
  524. pkgPath = util.joinPaths(infoDir, infoPkgName)
  525. self.pkgFilters.append( \
  526. filter.Filter(pkgPath, self.macros,
  527. name = 'info-%s' % infoPkgName))
  528. # extras need to come before derived so that derived packages
  529. # can change the package to which a file is assigned
  530. for filteritem in itertools.chain(self.extraFilters,
  531. self.derivedFilters):
  532. if not isinstance(filteritem, (filter.Filter, filter.PathSet)):
  533. name = filteritem[0] % self.macros
  534. if not trove.troveNameIsValid(name):
  535. self.error('%s is not a valid package name', name)
  536. args, kwargs = self.filterExpArgs(filteritem[1:], name=name)
  537. self.pkgFilters.append(filter.Filter(*args, **kwargs))
  538. else:
  539. self.pkgFilters.append(filteritem)
  540. # by default, everything that hasn't matched a pattern in the
  541. # main package filter goes in the package named recipe.name
  542. self.pkgFilters.append(filter.Filter('.*', self.macros, name=recipe.name))
  543. # OK, all the filters exist, build an autopackage object that
  544. # knows about them
  545. recipe.autopkg = buildpackage.AutoBuildPackage(
  546. self.pkgFilters, self.compFilters, recipe)
  547. self.autopkg = recipe.autopkg
  548. def do(self):
  549. # Walk capsule contents ignored by doFile
  550. for filePath, _, componentName in self.recipe._iterCapsulePaths():
  551. realPath = self.destdir + filePath
  552. if util.exists(realPath):
  553. # Files that do not exist on the filesystem (devices)
  554. # are handled separately
  555. self.autopkg.addFile(filePath, realPath, componentName)
  556. # Walk normal files
  557. _filterSpec.do(self)
  558. def doFile(self, path):
  559. # all policy classes after this require that the initial tree is built
  560. if not self.recipe._getCapsulePathsForFile(path):
  561. realPath = self.destdir + path
  562. self.autopkg.addFile(path, realPath)
  563. def postProcess(self):
  564. # flag all config files
  565. for confname in self.configFiles:
  566. self.recipe.autopkg.pathMap[confname].flags.isConfig(True)
  567. class InitialContents(policy.Policy):
  568. """
  569. NAME
  570. ====
  571. B{C{r.InitialContents()}} - Mark only explicit inclusions as initial
  572. contents files
  573. SYNOPSIS
  574. ========
  575. C{InitialContents([I{filterexp}])}
  576. DESCRIPTION
  577. ===========
  578. By default, C{r.InitialContents()} does not apply to any files.
  579. It is used to specify all files that Conary needs to mark as
  580. providing only initial contents. When Conary installs or
  581. updates one of these files, it will never replace existing
  582. contents; it uses the provided contents only if the file does
  583. not yet exist at the time Conary is creating it.
  584. A file marked as an InitialContents file cannot also be marked
  585. as a Transient file or a Config file. Conary enforces this
  586. requirement.
  587. EXAMPLES
  588. ========
  589. C{r.InitialContents('%(sysconfdir)s/conary/.*gpg')}
  590. The files C{%(sysconfdir)s/conary/.*gpg} are being marked as initial
  591. contents files. Conary will use those contents when creating the files
  592. the first time, but will never overwrite existing contents in those files.
  593. """
  594. requires = (
  595. ('PackageSpec', policy.REQUIRED_PRIOR),
  596. ('Config', policy.REQUIRED_PRIOR),
  597. )
  598. bucket = policy.PACKAGE_CREATION
  599. processUnmodified = True
  600. invariantexceptions = [ '%(userinfodir)s/', '%(groupinfodir)s' ]
  601. invariantinclusions = ['%(localstatedir)s/run/',
  602. '%(localstatedir)s/log/',
  603. '%(cachedir)s/']
  604. def postInit(self, *args, **kwargs):
  605. self.recipe.Config(exceptions = self.invariantinclusions,
  606. allowUnusedFilters = True)
  607. def updateArgs(self, *args, **keywords):
  608. policy.Policy.updateArgs(self, *args, **keywords)
  609. self.recipe.Config(exceptions=args, allowUnusedFilters = True)
  610. def doFile(self, filename):
  611. fullpath = self.macros.destdir + filename
  612. recipe = self.recipe
  613. if os.path.isfile(fullpath) and util.isregular(fullpath):
  614. self.info(filename)
  615. f = recipe.autopkg.pathMap[filename]
  616. f.flags.isInitialContents(True)
  617. if f.flags.isConfig():
  618. self.error(
  619. '%s is marked as both a configuration file and'
  620. ' an initial contents file', filename)
  621. class Transient(policy.Policy):
  622. """
  623. NAME
  624. ====
  625. B{C{r.Transient()}} - Mark files that have transient contents
  626. SYNOPSIS
  627. ========
  628. C{r.Transient([I{filterexp}])}
  629. DESCRIPTION
  630. ===========
  631. The C{r.Transient()} policy marks files as containing transient
  632. contents. It automatically marks the two most common uses of transient
  633. contents: python and emacs byte-compiled files
  634. (C{.pyc}, C{.pyo}, and C{.elc} files).
  635. Files containing transient contents are almost the opposite of
  636. configuration files: their contents should be overwritten by
  637. the new contents without question at update time, even if the
  638. contents in the filesystem have changed. (Conary raises an
  639. error if file contents have changed in the filesystem for normal
  640. files.)
  641. A file marked as a Transient file cannot also be marked as an
  642. InitialContents file or a Config file. Conary enforces this
  643. requirement.
  644. EXAMPLES
  645. ========
  646. C{r.Transient('%(libdir)s/firefox/extensions/')}
  647. Marks all the files in the directory C{%(libdir)s/firefox/extensions/} as
  648. having transient contents.
  649. """
  650. bucket = policy.PACKAGE_CREATION
  651. filetree = policy.PACKAGE
  652. processUnmodified = True
  653. requires = (
  654. ('PackageSpec', policy.REQUIRED_PRIOR),
  655. ('Config', policy.REQUIRED_PRIOR),
  656. ('InitialContents', policy.REQUIRED_PRIOR),
  657. )
  658. invariantinclusions = [
  659. r'..*\.py(c|o)$',
  660. r'..*\.elc$',
  661. r'%(userinfodir)s/',
  662. r'%(groupinfodir)s'
  663. ]
  664. def doFile(self, filename):
  665. fullpath = self.macros.destdir + filename
  666. if os.path.isfile(fullpath) and util.isregular(fullpath):
  667. recipe = self.recipe
  668. f = recipe.autopkg.pathMap[filename]
  669. f.flags.isTransient(True)
  670. if f.flags.isConfig() or f.flags.isInitialContents():
  671. self.error(
  672. '%s is marked as both a transient file and'
  673. ' a configuration or initial contents file', filename)
  674. class TagDescription(policy.Policy):
  675. """
  676. NAME
  677. ====
  678. B{C{r.TagDescription()}} - Marks tag description files
  679. SYNOPSIS
  680. ========
  681. C{r.TagDescription([I{filterexp}])}
  682. DESCRIPTION
  683. ===========
  684. The C{r.TagDescription} class marks tag description files as
  685. such so that conary handles them correctly. Every file in
  686. C{%(tagdescriptiondir)s/} is marked as a tag description file by default.
  687. No file outside of C{%(tagdescriptiondir)s/} will be considered by this
  688. policy.
  689. EXAMPLES
  690. ========
  691. This policy is not called explicitly.
  692. """
  693. bucket = policy.PACKAGE_CREATION
  694. processUnmodified = False
  695. requires = (
  696. ('PackageSpec', policy.REQUIRED_PRIOR),
  697. )
  698. invariantsubtrees = [ '%(tagdescriptiondir)s/' ]
  699. def doFile(self, path):
  700. if self.recipe._getCapsulePathsForFile(path):
  701. return
  702. fullpath = self.macros.destdir + path
  703. if os.path.isfile(fullpath) and util.isregular(fullpath):
  704. self.info('conary tag file: %s', path)
  705. self.recipe.autopkg.pathMap[path].tags.set("tagdescription")
  706. class TagHandler(policy.Policy):
  707. """
  708. NAME
  709. ====
  710. B{C{r.TagHandler()}} - Mark tag handler files
  711. SYNOPSIS
  712. ========
  713. C{r.TagHandler([I{filterexp}])}
  714. DESCRIPTION
  715. ===========
  716. All files in C{%(taghandlerdir)s/} are marked as a tag
  717. handler files.
  718. EXAMPLES
  719. ========
  720. This policy is not called explicitly.
  721. """
  722. bucket = policy.PACKAGE_CREATION
  723. processUnmodified = False
  724. requires = (
  725. ('PackageSpec', policy.REQUIRED_PRIOR),
  726. )
  727. invariantsubtrees = [ '%(taghandlerdir)s/' ]
  728. def doFile(self, path):
  729. if self.recipe._getCapsulePathsForFile(path):
  730. return
  731. fullpath = self.macros.destdir + path
  732. if os.path.isfile(fullpath) and util.isregular(fullpath):
  733. self.info('conary tag handler: %s', path)
  734. self.recipe.autopkg.pathMap[path].tags.set("taghandler")
  735. class TagSpec(_addInfo):
  736. """
  737. NAME
  738. ====
  739. B{C{r.TagSpec()}} - Apply tags defined by tag descriptions
  740. SYNOPSIS
  741. ========
  742. C{r.TagSpec([I{tagname}, I{filterexp}] || [I{tagname}, I{exceptions=filterexp}])}
  743. DESCRIPTION
  744. ===========
  745. The C{r.TagSpec()} policy automatically applies tags defined by tag
  746. descriptions in both the current system and C{%(destdir)s} to all
  747. files in C{%(destdir)s}.
  748. To apply tags manually (removing a dependency on the tag description
  749. file existing when the packages is cooked), use the syntax:
  750. C{r.TagSpec(I{tagname}, I{filterexp})}.
  751. To set an exception to this policy, use:
  752. C{r.TagSpec(I{tagname}, I{exceptions=filterexp})}.
  753. EXAMPLES
  754. ========
  755. C{r.TagSpec('initscript', '%(initdir)s/')}
  756. Applies the C{initscript} tag to all files in the directory
  757. C{%(initdir)s/}.
  758. """
  759. requires = (
  760. ('PackageSpec', policy.REQUIRED_PRIOR),
  761. )
  762. def doProcess(self, recipe):
  763. self.tagList = []
  764. self.buildReqsComputedForTags = set()
  765. self.suggestBuildRequires = set()
  766. # read the system and %(destdir)s tag databases
  767. for directory in (recipe.macros.destdir+'/etc/conary/tags/',
  768. '/etc/conary/tags/'):
  769. if os.path.isdir(directory):
  770. for filename in os.listdir(directory):
  771. path = util.joinPaths(directory, filename)
  772. self.tagList.append(tags.TagFile(path, recipe.macros, True))
  773. self.fullReqs = self.recipe._getTransitiveBuildRequiresNames()
  774. _addInfo.doProcess(self, recipe)
  775. def markTag(self, name, tag, path, tagFile=None):
  776. # commonly, a tagdescription will nominate a file to be
  777. # tagged, but it will also be set explicitly in the recipe,
  778. # and therefore markTag will be called twice.
  779. if (len(tag.split()) > 1 or
  780. not tag.replace('-', '').replace('_', '').isalnum()):
  781. # handlers for multiple tags require strict tag names:
  782. # no whitespace, only alphanumeric plus - and _ characters
  783. self.error('illegal tag name %s for file %s' %(tag, path))
  784. return
  785. tags = self.recipe.autopkg.pathMap[path].tags
  786. if tag not in tags:
  787. self.info('%s: %s', name, path)
  788. tags.set(tag)
  789. if tagFile and tag not in self.buildReqsComputedForTags:
  790. self.buildReqsComputedForTags.add(tag)
  791. db = self._getDb()
  792. for trove in db.iterTrovesByPath(tagFile.tagFile):
  793. troveName = trove.getName()
  794. if troveName not in self.fullReqs:
  795. # XXX should be error, change after bootstrap
  796. self.warn("%s assigned by %s to file %s, so add '%s'"
  797. ' to buildRequires or call r.TagSpec()'
  798. %(tag, tagFile.tagFile, path, troveName))
  799. self.suggestBuildRequires.add(troveName)
  800. def runInfo(self, path):
  801. if self.recipe._getCapsulePathsForFile(path):
  802. # capsules do not participate in the tag protocol
  803. return
  804. excludedTags = {}
  805. for tag in self.included:
  806. for filt in self.included[tag]:
  807. if filt.match(path):
  808. isExcluded = False
  809. if tag in self.excluded:
  810. for filt in self.excluded[tag]:
  811. if filt.match(path):
  812. s = excludedTags.setdefault(tag, set())
  813. s.add(path)
  814. isExcluded = True
  815. break
  816. if not isExcluded:
  817. self.markTag(tag, tag, path)
  818. for tag in self.tagList:
  819. if tag.match(path):
  820. if tag.name:
  821. name = tag.name
  822. else:
  823. name = tag.tag
  824. isExcluded = False
  825. if tag.tag in self.excluded:
  826. for filt in self.excluded[tag.tag]:
  827. # exception handling is per-tag, so handled specially
  828. if filt.match(path):
  829. s = excludedTags.setdefault(name, set())
  830. s.add(path)
  831. isExcluded = True
  832. break
  833. if not isExcluded:
  834. self.markTag(name, tag.tag, path, tag)
  835. if excludedTags:
  836. for tag in excludedTags:
  837. self.info('ignoring tag match for %s: %s',
  838. tag, ', '.join(sorted(excludedTags[tag])))
  839. def postProcess(self):
  840. if self.suggestBuildRequires:
  841. self.info('possibly add to buildRequires: %s',
  842. str(sorted(list(self.suggestBuildRequires))))
  843. self.recipe.reportMissingBuildRequires(self.suggestBuildRequires)
  844. class Properties(policy.Policy):
  845. """
  846. NAME
  847. ====
  848. B{C{r.Properties()}} - Read property definition files
  849. SYNOPSIS
  850. ========
  851. C{r.Properties(I{exceptions=filterexp} || [I{contents=xml},
  852. I{package=pkg:component}])}
  853. DESCRIPTION
  854. ===========
  855. The C{r.Properties()} policy automatically parses iconfig property
  856. definition files, making the properties available for configuration
  857. management with iconfig.
  858. To add configuration properties manually, use the syntax:
  859. C{r.Properties(I{contents=ipropcontents}, I{package=pkg:component}}
  860. Where contents is the xml string that would normally be stored in the iprop
  861. file and package is the component where to attach the config metadata.
  862. (NOTE: This component must exist)
  863. """
  864. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  865. bucket = policy.PACKAGE_CREATION
  866. processUnmodified = True
  867. invariantinclusions = [ r'%(prefix)s/lib/iconfig/properties/.*\.iprop' ]
  868. requires = (
  869. # We need to know what component files have been assigned to
  870. ('PackageSpec', policy.REQUIRED_PRIOR),
  871. )
  872. def __init__(self, *args, **kwargs):
  873. policy.Policy.__init__(self, *args, **kwargs)
  874. self.contents = []
  875. def updateArgs(self, *args, **kwargs):
  876. if 'contents' in kwargs:
  877. contents = kwargs.pop('contents')
  878. pkg = kwargs.pop('package', None)
  879. self.contents.append((pkg, contents))
  880. policy.Policy.updateArgs(self, *args, **kwargs)
  881. def doFile(self, path):
  882. fullpath = self.recipe.macros.destdir + path
  883. if not os.path.isfile(fullpath) or not util.isregular(fullpath):
  884. return
  885. componentMap = self.recipe.autopkg.componentMap
  886. if path not in componentMap:
  887. return
  888. main, comp = componentMap[path].getName().split(':')
  889. xml = open(fullpath).read()
  890. self._parsePropertyData(xml, main, comp)
  891. def postProcess(self):
  892. for pkg, content in self.contents:
  893. pkg = pkg % self.macros
  894. pkgName, compName = pkg.split(':')
  895. self._parsePropertyData(content, pkgName, compName)
  896. def _parsePropertyData(self, xml, pkgName, compName):
  897. xmldata = smartform.SmartFormFieldParser(xml)
  898. self.recipe._addProperty(trove._PROPERTY_TYPE_SMARTFORM,
  899. pkgName, compName, xmldata.name, xml, xmldata.default)
  900. class MakeDevices(policy.Policy):
  901. """
  902. NAME
  903. ====
  904. B{C{r.MakeDevices()}} - Make device nodes
  905. SYNOPSIS
  906. ========
  907. C{MakeDevices([I{path},] [I{type},] [I{major},] [I{minor},] [I{owner},] [I{groups},] [I{mode}])}
  908. DESCRIPTION
  909. ===========
  910. The C{r.MakeDevices()} policy creates device nodes. Conary's
  911. policy of non-root builds requires that these nodes exist only in the
  912. package, and not in the filesystem, as only root may actually create
  913. device nodes.
  914. EXAMPLES
  915. ========
  916. C{r.MakeDevices(I{'/dev/tty', 'c', 5, 0, 'root', 'root', mode=0666, package=':dev'})}
  917. Creates the device node C{/dev/tty}, as type 'c' (character, as opposed to
  918. type 'b', or block) with a major number of '5', minor number of '0',
  919. owner, and group are both the root user, and permissions are 0666.
  920. """
  921. bucket = policy.PACKAGE_CREATION
  922. processUnmodified = True
  923. requires = (
  924. ('PackageSpec', policy.REQUIRED_PRIOR),
  925. ('Ownership', policy.REQUIRED_SUBSEQUENT),
  926. )
  927. def __init__(self, *args, **keywords):
  928. self.devices = []
  929. policy.Policy.__init__(self, *args, **keywords)
  930. def updateArgs(self, *args, **keywords):
  931. """
  932. MakeDevices(path, devtype, major, minor, owner, group, mode=0400)
  933. """
  934. if args:
  935. args = list(args)
  936. l = len(args)
  937. if not ((l > 5) and (l < 9)):
  938. self.recipe.error('MakeDevices: incorrect arguments: %r %r'
  939. %(args, keywords))
  940. mode = keywords.pop('mode', None)
  941. package = keywords.pop('package', None)
  942. if l > 6 and mode is None:
  943. mode = args[6]
  944. if mode is None:
  945. mode = 0400
  946. if l > 7 and package is None:
  947. package = args[7]
  948. self.devices.append(
  949. (args[0:6], {'perms': mode, 'package': package}))
  950. policy.Policy.updateArgs(self, **keywords)
  951. def do(self):
  952. for device, kwargs in self.devices:
  953. r = self.recipe
  954. filename = device[0]
  955. owner = device[4]
  956. group = device[5]
  957. r.Ownership(owner, group, filename)
  958. device[0] = device[0] % r.macros
  959. r.autopkg.addDevice(*device, **kwargs)
  960. class setModes(policy.Policy):
  961. """
  962. Do not call from recipes; this is used internally by C{r.SetModes},
  963. C{r.ParseManifest}, and unpacking derived packages. This policy
  964. modified modes relative to the mode on the file in the filesystem.
  965. It adds setuid/setgid bits not otherwise set/honored on files on the
  966. filesystem, and sets user r/w/x bits if they were altered for the
  967. purposes of accessing the files during packaging. Otherwise,
  968. it honors the bits found on the filesystem. It does not modify
  969. bits in capsules.
  970. """
  971. bucket = policy.PACKAGE_CREATION
  972. processUnmodified = True
  973. requires = (
  974. ('PackageSpec', policy.REQUIRED_PRIOR),
  975. ('WarnWriteable', policy.REQUIRED_SUBSEQUENT),
  976. ('ExcludeDirectories', policy.CONDITIONAL_SUBSEQUENT),
  977. )
  978. def __init__(self, *args, **keywords):
  979. self.sidbits = {}
  980. self.userbits = {}
  981. policy.Policy.__init__(self, *args, **keywords)
  982. def updateArgs(self, *args, **keywords):
  983. """
  984. setModes(path(s), [sidbits=int], [userbits=int])
  985. """
  986. sidbits = keywords.pop('sidbits', None)
  987. userbits = keywords.pop('userbits', None)
  988. for path in args:
  989. if sidbits is not None:
  990. self.sidbits[path] = sidbits
  991. if userbits is not None:
  992. self.userbits[path] = userbits
  993. self.recipe.WarnWriteable(
  994. exceptions=re.escape(path).replace('%', '%%'),
  995. allowUnusedFilters = True)
  996. policy.Policy.updateArgs(self, **keywords)
  997. def doFile(self, path):
  998. # Don't set modes on capsule files
  999. if self.recipe._getCapsulePathsForFile(path):
  1000. return
  1001. # Skip files that aren't part of the package
  1002. if path not in self.recipe.autopkg.pathMap:
  1003. return
  1004. newmode = oldmode = self.recipe.autopkg.pathMap[path].inode.perms()
  1005. if path in self.userbits:
  1006. newmode = (newmode & 077077) | self.userbits[path]
  1007. if path in self.sidbits and self.sidbits[path]:
  1008. newmode |= self.sidbits[path]
  1009. self.info('suid/sgid: %s mode 0%o', path, newmode & 07777)
  1010. if newmode != oldmode:
  1011. self.recipe.autopkg.pathMap[path].inode.perms.set(newmode)
  1012. class LinkType(policy.Policy):
  1013. """
  1014. NAME
  1015. ====
  1016. B{C{r.LinkType()}} - Ensures only regular, non-configuration files are hardlinked
  1017. SYNOPSIS
  1018. ========
  1019. C{r.LinkType([I{filterexp}])}
  1020. DESCRIPTION
  1021. ===========
  1022. The C{r.LinkType()} policy ensures that only regular, non-configuration
  1023. files are hardlinked.
  1024. EXAMPLES
  1025. ========
  1026. This policy is not called explicitly.
  1027. """
  1028. bucket = policy.PACKAGE_CREATION
  1029. processUnmodified = True
  1030. requires = (
  1031. ('Config', policy.REQUIRED_PRIOR),
  1032. ('PackageSpec', policy.REQUIRED_PRIOR),
  1033. )
  1034. def do(self):
  1035. for component in self.recipe.autopkg.getComponents():
  1036. for path in sorted(component.hardlinkMap.keys()):
  1037. if self.recipe.autopkg.pathMap[path].flags.isConfig():
  1038. self.error("Config file %s has illegal hard links", path)
  1039. for path in component.badhardlinks:
  1040. self.error("Special file %s has illegal hard links", path)
  1041. class LinkCount(policy.Policy):
  1042. """
  1043. NAME
  1044. ====
  1045. B{C{r.LinkCount()}} - Restricts hardlinks across directories.
  1046. SYNOPSIS
  1047. ========
  1048. C{LinkCount([I{filterexp}] | [I{exceptions=filterexp}])}
  1049. DESCRIPTION
  1050. ===========
  1051. The C{r.LinkCount()} policy restricts hardlinks across directories.
  1052. It is generally an error to have hardlinks across directories, except when
  1053. the packager knows that there is no reasonable chance that they will be on
  1054. separate filesystems.
  1055. In cases where the packager is certain hardlinks will not cross
  1056. filesystems, a list of regular expressions specifying files
  1057. which are excepted from this rule may be passed to C{r.LinkCount}.
  1058. EXAMPLES
  1059. ========
  1060. C{r.LinkCount(exceptions='/usr/share/zoneinfo/')}
  1061. Uses C{r.LinkCount} to except zoneinfo files, located in
  1062. C{/usr/share/zoneinfo/}, from the policy against cross-directory
  1063. hardlinks.
  1064. """
  1065. bucket = policy.PACKAGE_CREATION
  1066. processUnmodified = False
  1067. requires = (
  1068. ('PackageSpec', policy.REQUIRED_PRIOR),
  1069. )
  1070. def __init__(self, *args, **keywords):
  1071. policy.Policy.__init__(self, *args, **keywords)
  1072. self.excepts = set()
  1073. def updateArgs(self, *args, **keywords):
  1074. allowUnusedFilters = keywords.pop('allowUnusedFilters', False) or \
  1075. self.allowUnusedFilters
  1076. exceptions = keywords.pop('exceptions', None)
  1077. if exceptions:
  1078. if type(exceptions) is str:
  1079. self.excepts.add(exceptions)
  1080. if not allowUnusedFilters:
  1081. self.unusedFilters['exceptions'].add(exceptions)
  1082. elif type(exceptions) in (tuple, list):
  1083. self.excepts.update(exceptions)
  1084. if not allowUnusedFilters:
  1085. self.unusedFilters['exceptions'].update(exceptions)
  1086. # FIXME: we may want to have another keyword argument
  1087. # that passes information down to the buildpackage
  1088. # that causes link groups to be broken for some
  1089. # directories but not others. We need to research
  1090. # first whether this is useful; it may not be.
  1091. def do(self):
  1092. if self.recipe.getType() == recipe.RECIPE_TYPE_CAPSULE:
  1093. return
  1094. filters = [(x, filter.Filter(x, self.macros)) for x in self.excepts]
  1095. for component in self.recipe.autopkg.getComponents():
  1096. for inode in component.linkGroups:
  1097. # ensure all in same directory, except for directories
  1098. # matching regexps that have been passed in
  1099. allPaths = [x for x in component.linkGroups[inode]]
  1100. for path in allPaths[:]:
  1101. for regexp, f in filters:
  1102. if f.match(path):
  1103. self.unusedFilters['exceptions'].discard(regexp)
  1104. allPaths.remove(path)
  1105. dirSet = set(os.path.dirname(x) + '/' for x in allPaths)
  1106. if len(dirSet) > 1:
  1107. self.error('files %s are hard links across directories %s',
  1108. ', '.join(sorted(component.linkGroups[inode])),
  1109. ', '.join(sorted(list(dirSet))))
  1110. self.error('If these directories cannot reasonably be'
  1111. ' on different filesystems, disable this'
  1112. ' warning by calling'
  1113. " r.LinkCount(exceptions=('%s')) or"
  1114. " equivalent"
  1115. % "', '".join(sorted(list(dirSet))))
  1116. class ExcludeDirectories(policy.Policy):
  1117. """
  1118. NAME
  1119. ====
  1120. B{C{r.ExcludeDirectories()}} - Exclude directories from package
  1121. SYNOPSIS
  1122. ========
  1123. C{r.ExcludeDirectories([I{filterexp}] | [I{exceptions=filterexp}])}
  1124. DESCRIPTION
  1125. ===========
  1126. The C{r.ExcludeDirectories} policy causes directories to be
  1127. excluded from the package by default. Use
  1128. C{r.ExcludeDirectories(exceptions=I{filterexp})} to set exceptions to this
  1129. policy, which will cause directories matching the regular expression
  1130. C{filterexp} to be included in the package. Remember that Conary
  1131. packages cannot share files, including directories, so only one
  1132. package installed on a system at any one time can own the same
  1133. directory.
  1134. There are only three reasons to explicitly package a directory: the
  1135. directory needs permissions other than 0755, it needs non-root owner
  1136. or group, or it must exist even if it is empty.
  1137. Therefore, it should generally not be necessary to invoke this policy
  1138. directly. If your directory requires permissions other than 0755, simply
  1139. use C{r.SetMode} to specify the permissions, and the directory will be
  1140. automatically included. Similarly, if you wish to include an empty
  1141. directory with owner or group information, call C{r.Ownership} on that
  1142. empty directory,
  1143. Because C{r.Ownership} can reasonably be called on an entire
  1144. subdirectory tree and indiscriminately applied to files and
  1145. directories alike, non-empty directories with owner or group
  1146. set will be excluded from packaging unless an exception is
  1147. explicitly provided.
  1148. If you call C{r.Ownership} with a filter that applies to an
  1149. empty directory, but you do not want to package that directory,
  1150. you will have to remove the directory with C{r.Remove}.
  1151. Packages do not need to explicitly include directories to ensure
  1152. existence of a target to place a file in. Conary will appropriately
  1153. create the directory, and delete it later if the directory becomes empty.
  1154. EXAMPLES
  1155. ========
  1156. C{r.ExcludeDirectories(exceptions='/tftpboot')}
  1157. Sets the directory C{/tftboot} as an exception to the
  1158. C{r.ExcludeDirectories} policy, so that the C{/tftpboot}
  1159. directory will be included in the package.
  1160. """
  1161. bucket = policy.PACKAGE_CREATION
  1162. processUnmodified = True
  1163. requires = (
  1164. ('PackageSpec', policy.REQUIRED_PRIOR),
  1165. ('Ownership', policy.REQUIRED_PRIOR),
  1166. ('MakeDevices', policy.CONDITIONAL_PRIOR),
  1167. )
  1168. invariantinclusions = [ ('.*', stat.S_IFDIR) ]
  1169. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  1170. def doFile(self, path):
  1171. # temporarily do nothing for capsules, we might do something later
  1172. if self.recipe._getCapsulePathsForFile(path):
  1173. return
  1174. fullpath = self.recipe.macros.destdir + os.sep + path
  1175. s = os.lstat(fullpath)
  1176. mode = s[stat.ST_MODE]
  1177. if mode & 0777 != 0755:
  1178. self.info('excluding directory %s with mode %o', path, mode&0777)
  1179. elif not os.listdir(fullpath):
  1180. d = self.recipe.autopkg.pathMap[path]
  1181. if d.inode.owner.freeze() != 'root':
  1182. self.info('not excluding empty directory %s'
  1183. ' because of non-root owner', path)
  1184. return
  1185. elif d.inode.group.freeze() != 'root':
  1186. self.info('not excluding empty directory %s'
  1187. ' because of non-root group', path)
  1188. return
  1189. self.info('excluding empty directory %s', path)
  1190. # if its empty and we're not packaging it, there's no need for it
  1191. # to continue to exist on the filesystem to potentially confuse
  1192. # other policy actions... see CNP-18
  1193. os.rmdir(fullpath)
  1194. self.recipe.autopkg.delFile(path)
  1195. class ByDefault(policy.Policy):
  1196. """
  1197. NAME
  1198. ====
  1199. B{C{r.ByDefault()}} - Determines components to be installed by default
  1200. SYNOPSIS
  1201. ========
  1202. C{r.ByDefault([I{inclusions} || C{exceptions}=I{exceptions}])}
  1203. DESCRIPTION
  1204. ===========
  1205. The C{r.ByDefault()} policy determines which components should
  1206. be installed by default at the time the package is installed on the
  1207. system. The default setting for the C{ByDefault} policy is that the
  1208. C{:debug}, and C{:test} packages are not installed with the package.
  1209. The inclusions and exceptions do B{not} specify filenames. They are
  1210. either C{I{package}:I{component}} or C{:I{component}}. Inclusions
  1211. are considered before exceptions, and inclusions and exceptions are
  1212. considered in the order provided in the recipe, and first match wins.
  1213. EXAMPLES
  1214. ========
  1215. C{r.ByDefault(exceptions=[':manual'])}
  1216. Uses C{r.ByDefault} to ignore C{:manual} components when enforcing the
  1217. policy.
  1218. C{r.ByDefault(exceptions=[':manual'])}
  1219. C{r.ByDefault('foo:manual')}
  1220. If these lines are in the C{bar} package, and there is both a
  1221. C{foo:manual} and a C{bar:manual} component, then the C{foo:manual}
  1222. component will be installed by default when the C{foo} package is
  1223. installed, but the C{bar:manual} component will not be installed by
  1224. default when the C{bar} package is installed.
  1225. """
  1226. bucket = policy.PACKAGE_CREATION
  1227. requires = (
  1228. ('PackageSpec', policy.REQUIRED_PRIOR),
  1229. )
  1230. filetree = policy.NO_FILES
  1231. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  1232. invariantexceptions = [':test', ':debuginfo']
  1233. allowUnusedFilters = True
  1234. def doProcess(self, recipe):
  1235. if not self.inclusions:
  1236. self.inclusions = []
  1237. if not self.exceptions:
  1238. self.exceptions = []
  1239. recipe.setByDefaultOn(frozenset(self.inclusions))
  1240. recipe.setByDefaultOff(frozenset(self.exceptions +
  1241. self.invariantexceptions))
  1242. class _UserGroup(policy.Policy):
  1243. """
  1244. Abstract base class that implements marking owner/group dependencies.
  1245. """
  1246. bucket = policy.PACKAGE_CREATION
  1247. # All classes that descend from _UserGroup must run before the
  1248. # Requires policy, as they implicitly depend on it to set the
  1249. # file requirements and union the requirements up to the package.
  1250. requires = (
  1251. ('PackageSpec', policy.REQUIRED_PRIOR),
  1252. ('Requires', policy.REQUIRED_SUBSEQUENT),
  1253. )
  1254. filetree = policy.PACKAGE
  1255. processUnmodified = True
  1256. def setUserGroupDep(self, path, info, depClass):
  1257. componentMap = self.recipe.autopkg.componentMap
  1258. if path not in componentMap:
  1259. return
  1260. pkg = componentMap[path]
  1261. f = pkg.getFile(path)
  1262. if path not in pkg.requiresMap:
  1263. pkg.requiresMap[path] = deps.DependencySet()
  1264. pkg.requiresMap[path].addDep(depClass, deps.Dependency(info, []))
  1265. class Ownership(_UserGroup):
  1266. """
  1267. NAME
  1268. ====
  1269. B{C{r.Ownership()}} - Set file ownership
  1270. SYNOPSIS
  1271. ========
  1272. C{r.Ownership([I{username},] [I{groupname},] [I{filterexp}])}
  1273. DESCRIPTION
  1274. ===========
  1275. The C{r.Ownership()} policy sets user and group ownership of files when
  1276. the default of C{root:root} is not appropriate.
  1277. List the ownerships in order, most specific first, ending with least
  1278. specific. The filespecs will be matched in the order that you provide them.
  1279. KEYWORDS
  1280. ========
  1281. None.
  1282. EXAMPLES
  1283. ========
  1284. C{r.Ownership('apache', 'apache', '%(localstatedir)s/lib/php/session')}
  1285. Sets ownership of C{%(localstatedir)s/lib/php/session} to owner
  1286. C{apache}, and group C{apache}.
  1287. """
  1288. def __init__(self, *args, **keywords):
  1289. self.filespecs = []
  1290. self.systemusers = ('root',)
  1291. self.systemgroups = ('root',)
  1292. policy.Policy.__init__(self, *args, **keywords)
  1293. def updateArgs(self, *args, **keywords):
  1294. if args:
  1295. for filespec in args[2:]:
  1296. self.filespecs.append((filespec, args[0], args[1]))
  1297. policy.Policy.updateArgs(self, **keywords)
  1298. def doProcess(self, recipe):
  1299. # we must NEVER take ownership from the filesystem
  1300. assert(not self.exceptions)
  1301. self.rootdir = self.rootdir % recipe.macros
  1302. self.fileFilters = []
  1303. for (filespec, user, group) in self.filespecs:
  1304. self.fileFilters.append(
  1305. (filter.Filter(filespec, recipe.macros),
  1306. user %recipe.macros,
  1307. group %recipe.macros))
  1308. del self.filespecs
  1309. policy.Policy.doProcess(self, recipe)
  1310. def doFile(self, path):
  1311. if self.recipe._getCapsulePathsForFile(path):
  1312. return
  1313. pkgfile = self.recipe.autopkg.pathMap[path]
  1314. pkgOwner = pkgfile.inode.owner()
  1315. pkgGroup = pkgfile.inode.group()
  1316. bestOwner = pkgOwner
  1317. bestGroup = pkgGroup
  1318. for (f, owner, group) in self.fileFilters:
  1319. if f.match(path):
  1320. bestOwner, bestGroup = owner, group
  1321. break
  1322. if bestOwner != pkgOwner:
  1323. pkgfile.inode.owner.set(bestOwner)
  1324. if bestGroup != pkgGroup:
  1325. pkgfile.inode.group.set(bestGroup)
  1326. if bestOwner and bestOwner not in self.systemusers:
  1327. self.setUserGroupDep(path, bestOwner, deps.UserInfoDependencies)
  1328. if bestGroup and bestGroup not in self.systemgroups:
  1329. self.setUserGroupDep(path, bestGroup, deps.GroupInfoDependencies)
  1330. class _Utilize(_UserGroup):
  1331. """
  1332. Pure virtual base class for C{UtilizeUser} and C{UtilizeGroup}
  1333. """
  1334. def __init__(self, *args, **keywords):
  1335. self.filespecs = []
  1336. policy.Policy.__init__(self, *args, **keywords)
  1337. def updateArgs(self, *args, **keywords):
  1338. """
  1339. call as::
  1340. UtilizeFoo(item, filespec(s)...)
  1341. List them in order, most specific first, ending with most
  1342. general; the filespecs will be matched in the order that
  1343. you provide them.
  1344. """
  1345. item = args[0] % self.recipe.macros
  1346. if args:
  1347. for filespec in args[1:]:
  1348. self.filespecs.append((filespec, item))
  1349. policy.Policy.updateArgs(self, **keywords)
  1350. def doProcess(self, recipe):
  1351. self.rootdir = self.rootdir % recipe.macros
  1352. self.fileFilters = []
  1353. for (filespec, item) in self.filespecs:
  1354. self.fileFilters.append(
  1355. (filter.Filter(filespec, recipe.macros), item))
  1356. del self.filespecs
  1357. policy.Policy.doProcess(self, recipe)
  1358. def doFile(self, path):
  1359. for (f, item) in self.fileFilters:
  1360. if f.match(path):
  1361. self._markItem(path, item)
  1362. return
  1363. def _markItem(self, path, item):
  1364. # pure virtual
  1365. assert(False)
  1366. class UtilizeUser(_Utilize):
  1367. """
  1368. NAME
  1369. ====
  1370. B{C{r.UtilizeUser()}} - Marks files as requiring a user definition to exist
  1371. SYNOPSIS
  1372. ========
  1373. C{r.UtilizeUser([I{username}, I{filterexp}])}
  1374. DESCRIPTION
  1375. ===========
  1376. The C{r.UtilizeUser} policy marks files as requiring a user definition
  1377. to exist even though the file is not owned by that user.
  1378. This is particularly useful for daemons that are setuid root
  1379. ant change their user id to a user id with no filesystem permissions
  1380. after they start.
  1381. EXAMPLES
  1382. ========
  1383. C{r.UtilizeUser('sshd', '%(sbindir)s/sshd')}
  1384. Marks the file C{%(sbindir)s/sshd} as requiring the user definition
  1385. 'sshd' although the file is not owned by the 'sshd' user.
  1386. """
  1387. def _markItem(self, path, user):
  1388. if not self.recipe._getCapsulePathsForFile(path):
  1389. self.info('user %s: %s' % (user, path))
  1390. self.setUserGroupDep(path, user, deps.UserInfoDependencies)
  1391. class UtilizeGroup(_Utilize):
  1392. """
  1393. NAME
  1394. ====
  1395. B{C{r.UtilizeGroup()}} - Marks files as requiring a user definition to
  1396. exist
  1397. SYNOPSIS
  1398. ========
  1399. C{r.UtilizeGroup([groupname, filterexp])}
  1400. DESCRIPTION
  1401. ===========
  1402. The C{r.UtilizeGroup} policy marks files as requiring a group definition
  1403. to exist even though the file is not owned by that group.
  1404. This is particularly useful for daemons that are setuid root
  1405. ant change their user id to a group id with no filesystem permissions
  1406. after they start.
  1407. EXAMPLES
  1408. ========
  1409. C{r.UtilizeGroup('users', '%(sysconfdir)s/default/useradd')}
  1410. Marks the file C{%(sysconfdir)s/default/useradd} as requiring the group
  1411. definition 'users' although the file is not owned by the 'users' group.
  1412. """
  1413. def _markItem(self, path, group):
  1414. if not self.recipe._getCapsulePathsForFile(path):
  1415. self.info('group %s: %s' % (group, path))
  1416. self.setUserGroupDep(path, group, deps.GroupInfoDependencies)
  1417. class ComponentRequires(policy.Policy):
  1418. """
  1419. NAME
  1420. ====
  1421. B{C{r.ComponentRequires()}} - Create automatic intra-package,
  1422. inter-component dependencies
  1423. SYNOPSIS
  1424. ========
  1425. C{r.ComponentRequires([{'I{componentname}': I{requiringComponentSet}}] |
  1426. [{'I{packagename}': {'I{componentname}': I{requiringComponentSet}}}])}
  1427. DESCRIPTION
  1428. ===========
  1429. The C{r.ComponentRequires()} policy creates automatic,
  1430. intra-package, inter-component dependencies, such as a corresponding
  1431. dependency between C{:lib} and C{:data} components.
  1432. Changes are passed in using dictionaries, both for additions that
  1433. are specific to a specific package, and additions that apply
  1434. generally to all binary packages being cooked from one recipe.
  1435. For general changes that are not specific to a package, use this syntax:
  1436. C{r.ComponentRequires({'I{componentname}': I{requiringComponentSet}})}.
  1437. For package-specific changes, you need to specify packages as well
  1438. as components:
  1439. C{r.ComponentRequires({'I{packagename}': 'I{componentname}': I{requiringComponentSet}})}.
  1440. By default, both C{:lib} and C{:runtime} components (if they exist)
  1441. require the C{:data} component (if it exists). If you call
  1442. C{r.ComponentRequires({'data': set(('lib',))})}, you limit it
  1443. so that C{:runtime} components will not require C{:data} components
  1444. for this recipe.
  1445. In recipes that create more than one binary package, you may need
  1446. to limit your changes to a single binary package. To do so, use
  1447. the package-specific syntax. For example, to remove the C{:runtime}
  1448. requirement on C{:data} only for the C{foo} package, call:
  1449. C{r.ComponentRequires({'foo': 'data': set(('lib',))})}.
  1450. Note that C{r.ComponentRequires} cannot require capability flags; use
  1451. C{r.Requires} if you need to specify requirements, including capability
  1452. flags.
  1453. EXAMPLES
  1454. ========
  1455. C{r.ComponentRequires({'openssl': {'config': set(('runtime', 'lib'))}})}
  1456. Uses C{r.ComponentRequires} to create dependencies in a top-level manner
  1457. for the C{:runtime} and C{:lib} component sets to require the
  1458. C{:config} component for the C{openssl} package.
  1459. """
  1460. bucket = policy.PACKAGE_CREATION
  1461. requires = (
  1462. ('PackageSpec', policy.REQUIRED_PRIOR),
  1463. ('ExcludeDirectories', policy.CONDITIONAL_PRIOR),
  1464. )
  1465. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  1466. def __init__(self, *args, **keywords):
  1467. self.depMap = {
  1468. # component: components that require it if they both exist
  1469. 'data': frozenset(('lib', 'runtime', 'devellib', 'cil', 'java',
  1470. 'perl', 'python', 'ruby')),
  1471. 'devellib': frozenset(('devel',)),
  1472. 'lib': frozenset(('devel', 'devellib', 'runtime')),
  1473. 'config': frozenset(('runtime', 'lib', 'devellib', 'devel')),
  1474. }
  1475. self.overridesMap = {}
  1476. policy.Policy.__init__(self, *args, **keywords)
  1477. def updateArgs(self, *args, **keywords):
  1478. d = args[0]
  1479. if isinstance(d[d.keys()[0]], dict): # dict of dicts
  1480. for packageName in d:
  1481. if packageName not in self.overridesMap:
  1482. # start with defaults, then override them individually
  1483. o = {}
  1484. o.update(self.depMap)
  1485. self.overridesMap[packageName] = o
  1486. self.overridesMap[packageName].update(d[packageName])
  1487. else: # dict of sets
  1488. self.depMap.update(d)
  1489. def do(self):
  1490. flags = []
  1491. if self.recipe.isCrossCompileTool():
  1492. flags.append((_getTargetDepFlag(self.macros), deps.FLAG_SENSE_REQUIRED))
  1493. components = self.recipe.autopkg.components
  1494. for packageName in [x.name for x in self.recipe.autopkg.packageMap]:
  1495. if packageName in self.overridesMap:
  1496. d = self.overridesMap[packageName]
  1497. else:
  1498. d = self.depMap
  1499. for requiredComponent in d:
  1500. for requiringComponent in d[requiredComponent]:
  1501. reqName = ':'.join((packageName, requiredComponent))
  1502. wantName = ':'.join((packageName, requiringComponent))
  1503. if (reqName in components and wantName in components and
  1504. components[reqName] and components[wantName]):
  1505. if (d == self.depMap and
  1506. reqName in self.recipe._componentReqs and
  1507. wantName in self.recipe._componentReqs):
  1508. # this is an automatically generated dependency
  1509. # which was not in the parent of a derived
  1510. # pacakge. don't add it here either
  1511. continue
  1512. # Note: this does not add dependencies to files;
  1513. # these dependencies are insufficiently specific
  1514. # to attach to files.
  1515. ds = deps.DependencySet()
  1516. depClass = deps.TroveDependencies
  1517. ds.addDep(depClass, deps.Dependency(reqName, flags))
  1518. p = components[wantName]
  1519. p.requires.union(ds)
  1520. class ComponentProvides(policy.Policy):
  1521. """
  1522. NAME
  1523. ====
  1524. B{C{r.ComponentProvides()}} - Causes each trove to explicitly provide
  1525. itself.
  1526. SYNOPSIS
  1527. ========
  1528. C{r.ComponentProvides(I{flags})}
  1529. DESCRIPTION
  1530. ===========
  1531. The C{r.ComponentProvides()} policy causes each trove to explicitly
  1532. provide its name. Call it to provide optional capability flags
  1533. consisting of a single string, or a list, tuple, or set of strings,
  1534. It is impossible to provide a capability flag for one component but
  1535. not another within a single package.
  1536. EXAMPLES
  1537. ========
  1538. C{r.ComponentProvides("addcolumn")}
  1539. Uses C{r.ComponentProvides} in the context of the sqlite recipe, and
  1540. causes sqlite to provide itself explicitly with the capability flag
  1541. C{addcolumn}.
  1542. """
  1543. bucket = policy.PACKAGE_CREATION
  1544. requires = (
  1545. ('PackageSpec', policy.REQUIRED_PRIOR),
  1546. ('ExcludeDirectories', policy.CONDITIONAL_PRIOR),
  1547. )
  1548. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  1549. def __init__(self, *args, **keywords):
  1550. self.flags = set()
  1551. self.excepts = set()
  1552. policy.Policy.__init__(self, *args, **keywords)
  1553. def updateArgs(self, *args, **keywords):
  1554. if 'exceptions' in keywords:
  1555. exceptions = keywords.pop('exceptions')
  1556. if type(exceptions) is str:
  1557. self.excepts.add(exceptions)
  1558. elif type(exceptions) in (tuple, list):
  1559. self.excepts.update(set(exceptions))
  1560. if not args:
  1561. return
  1562. if len(args) >= 2:
  1563. # update the documentation if we ever support the
  1564. # pkgname, flags calling convention
  1565. #pkgname = args[0]
  1566. flags = args[1]
  1567. else:
  1568. flags = args[0]
  1569. if not isinstance(flags, (list, tuple, set)):
  1570. flags=(flags,)
  1571. self.flags |= set(flags)
  1572. def do(self):
  1573. self.excepts = set(re.compile(x) for x in self.excepts)
  1574. self.flags = set(x for x in self.flags
  1575. if not [y.match(x) for y in self.excepts])
  1576. if self.flags:
  1577. flags = [ (x % self.macros, deps.FLAG_SENSE_REQUIRED)
  1578. for x in self.flags ]
  1579. else:
  1580. flags = []
  1581. if self.recipe.isCrossCompileTool():
  1582. flags.append(('target-%s' % self.macros.target,
  1583. deps.FLAG_SENSE_REQUIRED))
  1584. for component in self.recipe.autopkg.components.values():
  1585. component.provides.addDep(deps.TroveDependencies,
  1586. deps.Dependency(component.name, flags))
  1587. def _getTargetDepFlag(macros):
  1588. return 'target-%s' % macros.target
  1589. class _dependency(policy.Policy):
  1590. """
  1591. Internal class for shared code between Provides and Requires
  1592. """
  1593. def __init__(self, *args, **kwargs):
  1594. # bootstrap keeping only one copy of these around
  1595. self.bootstrapPythonFlags = None
  1596. self.bootstrapSysPath = []
  1597. self.bootstrapPerlIncPath = []
  1598. self.pythonFlagNamespace = None
  1599. self.removeFlagsByDependencyClass = None # pre-transform
  1600. self.removeFlagsByDependencyClassMap = {}
  1601. def updateArgs(self, *args, **keywords):
  1602. removeFlagsByDependencyClass = keywords.pop(
  1603. 'removeFlagsByDependencyClass', None)
  1604. if removeFlagsByDependencyClass is not None:
  1605. clsName, ignoreFlags = removeFlagsByDependencyClass
  1606. cls = deps.dependencyClassesByName[clsName]
  1607. l = self.removeFlagsByDependencyClassMap.setdefault(cls, [])
  1608. if isinstance(ignoreFlags, (list, set, tuple)):
  1609. l.append(set(ignoreFlags))
  1610. else:
  1611. l.append(re.compile(ignoreFlags))
  1612. policy.Policy.updateArgs(self, **keywords)
  1613. def preProcess(self):
  1614. self.CILPolicyRE = re.compile(r'.*mono/.*/policy.*/policy.*\.config$')
  1615. self.legalCharsRE = re.compile('[.0-9A-Za-z_+-/]')
  1616. # interpolate macros, using canonical path form with no trailing /
  1617. self.sonameSubtrees = set(os.path.normpath(x % self.macros)
  1618. for x in self.sonameSubtrees)
  1619. self.pythonFlagCache = {}
  1620. self.pythonTroveFlagCache = {}
  1621. self.pythonVersionCache = {}
  1622. def _hasContents(self, m, contents):
  1623. """
  1624. Return False if contents is set and m does not have that contents
  1625. """
  1626. if contents and (contents not in m.contents or not m.contents[contents]):
  1627. return False
  1628. return True
  1629. def _isELF(self, m, contents=None):
  1630. "Test whether is ELF file and optionally has certain contents"
  1631. # Note: for provides, check for 'abi' not 'provides' because we
  1632. # can provide the filename even if there is no provides list
  1633. # as long as a DT_NEEDED entry has been present to set the abi
  1634. return m and m.name == 'ELF' and self._hasContents(m, contents)
  1635. def _isPython(self, path):
  1636. return path.endswith('.py') or path.endswith('.pyc')
  1637. def _isPythonModuleCandidate(self, path):
  1638. return path.endswith('.so') or self._isPython(path)
  1639. def _runPythonScript(self, binPath, destdir, libdir, scriptLines):
  1640. script = '\n'.join(scriptLines)
  1641. environ = {}
  1642. if binPath.startswith(destdir):
  1643. environ['LD_LIBRARY_PATH'] = destdir + libdir
  1644. proc = subprocess.Popen([binPath, '-Ec', script],
  1645. executable=binPath,
  1646. stdout=subprocess.PIPE,
  1647. shell=False,
  1648. env=environ,
  1649. )
  1650. stdout, _ = proc.communicate()
  1651. if proc.returncode:
  1652. raise RuntimeError("Process exited with status %s" %
  1653. (proc.returncode,))
  1654. return stdout
  1655. def _getPythonVersion(self, pythonPath, destdir, libdir):
  1656. if pythonPath not in self.pythonVersionCache:
  1657. try:
  1658. stdout = self._runPythonScript(pythonPath, destdir, libdir,
  1659. ["import sys", "print('%d.%d' % sys.version_info[:2])"])
  1660. self.pythonVersionCache[pythonPath] = stdout.strip()
  1661. except (OSError, RuntimeError):
  1662. self.warn("Unable to determine Python version directly; "
  1663. "guessing based on path.")
  1664. self.pythonVersionCache[pythonPath] = self._getPythonVersionFromPath(pythonPath, destdir)
  1665. return self.pythonVersionCache[pythonPath]
  1666. def _getPythonSysPath(self, pythonPath, destdir, libdir, useDestDir=False):
  1667. """Return the system path for the python interpreter at C{pythonPath}
  1668. @param pythonPath: Path to the target python interpreter
  1669. @param destdir: Destination root, in case of a python bootstrap
  1670. @param libdir: Destination libdir, in case of a python bootstrap
  1671. @param useDestDir: If True, look in the destdir instead.
  1672. """
  1673. script = ["import sys, site"]
  1674. if useDestDir:
  1675. # Repoint site.py at the destdir so it picks up .pth files there.
  1676. script.extend([
  1677. "sys.path = []",
  1678. "sys.prefix = %r + sys.prefix" % (destdir,),
  1679. "sys.exec_prefix = %r + sys.exec_prefix" % (destdir,),
  1680. "site.PREFIXES = [sys.prefix, sys.exec_prefix]",
  1681. "site.addsitepackages(None)",
  1682. ])
  1683. script.append(r"print('\0'.join(sys.path))")
  1684. try:
  1685. stdout = self._runPythonScript(pythonPath, destdir, libdir, script)
  1686. except (OSError, RuntimeError):
  1687. # something went wrong, don't trust any output
  1688. self.info('Could not run system python "%s", guessing sys.path...',
  1689. pythonPath)
  1690. sysPath = []
  1691. else:
  1692. sysPath = [x.strip() for x in stdout.split('\0') if x.strip()]
  1693. if sysPath == []:
  1694. # probably a cross-build -- let's try a decent assumption
  1695. # for the syspath.
  1696. pyVer = self._getPythonVersionFromPath(pythonPath, destdir)
  1697. if not pyVer and self.bootstrapPythonFlags is not None:
  1698. pyVer = self._getPythonVersionFromFlags(
  1699. self.bootstrapPythonFlags)
  1700. if pyVer and self.bootstrapSysPath is not None:
  1701. lib = self.recipe.macros.lib
  1702. # this list needs to include all sys.path elements that
  1703. # might be needed for python per se -- note that
  1704. # bootstrapPythonFlags and bootstrapSysPath go
  1705. # together
  1706. sysPath = self.bootstrapSysPath + [
  1707. '/usr/%s/%s' %(lib, pyVer),
  1708. '/usr/%s/%s/plat-linux2' %(lib, pyVer),
  1709. '/usr/%s/%s/lib-tk' %(lib, pyVer),
  1710. '/usr/%s/%s/lib-dynload' %(lib, pyVer),
  1711. '/usr/%s/%s/site-packages' %(lib, pyVer),
  1712. # for purelib python on x86_64
  1713. '/usr/lib/%s/site-packages' %pyVer,
  1714. ]
  1715. return sysPath
  1716. def _warnPythonPathNotInDB(self, pathName):
  1717. self.warn('%s found on system but not provided by'
  1718. ' system database; python requirements'
  1719. ' may be generated incorrectly as a result', pathName)
  1720. return set([])
  1721. def _getPythonTroveFlags(self, pathName):
  1722. if pathName in self.pythonTroveFlagCache:
  1723. return self.pythonTroveFlagCache[pathName]
  1724. db = self._getDb()
  1725. foundPath = False
  1726. pythonFlags = set()
  1727. pythonTroveList = db.iterTrovesByPath(pathName)
  1728. if pythonTroveList:
  1729. depContainer = pythonTroveList[0]
  1730. assert(depContainer.getName())
  1731. foundPath = True
  1732. for dep in depContainer.getRequires().iterDepsByClass(
  1733. deps.PythonDependencies):
  1734. flagNames = [x[0] for x in dep.getFlags()[0]]
  1735. pythonFlags.update(flagNames)
  1736. self.pythonTroveFlagCache[pathName] = pythonFlags
  1737. if not foundPath:
  1738. self.pythonTroveFlagCache[pathName] = self._warnPythonPathNotInDB(
  1739. pathName)
  1740. return self.pythonTroveFlagCache[pathName]
  1741. def _getPythonFlags(self, pathName, bootstrapPythonFlags=None):
  1742. if pathName in self.pythonFlagCache:
  1743. return self.pythonFlagCache[pathName]
  1744. if bootstrapPythonFlags:
  1745. self.pythonFlagCache[pathName] = bootstrapPythonFlags
  1746. return self.pythonFlagCache[pathName]
  1747. db = self._getDb()
  1748. foundPath = False
  1749. # FIXME: This should be iterFilesByPath when implemented (CNY-1833)
  1750. # For now, cache all the python deps in all the files in the
  1751. # trove(s) so that we iterate over each trove only once
  1752. containingTroveList = db.iterTrovesByPath(pathName)
  1753. for containerTrove in containingTroveList:
  1754. for pathid, p, fileid, v in containerTrove.iterFileList():
  1755. if pathName == p:
  1756. foundPath = True
  1757. pythonFlags = set()
  1758. f = files.ThawFile(db.getFileStream(fileid), pathid)
  1759. for dep in f.provides().iterDepsByClass(
  1760. deps.PythonDependencies):
  1761. flagNames = [x[0] for x in dep.getFlags()[0]]
  1762. pythonFlags.update(flagNames)
  1763. self.pythonFlagCache[p] = pythonFlags
  1764. if not foundPath:
  1765. self.pythonFlagCache[pathName] = self._warnPythonPathNotInDB(
  1766. pathName)
  1767. return self.pythonFlagCache[pathName]
  1768. def _getPythonFlagsFromPath(self, pathName):
  1769. pathList = pathName.split('/')
  1770. foundLib = False
  1771. foundVer = False
  1772. flags = set()
  1773. for dirName in pathList:
  1774. if not foundVer and not foundLib and dirName.startswith('lib'):
  1775. # lib will always come before ver
  1776. foundLib = True
  1777. flags.add(dirName)
  1778. elif not foundVer and dirName.startswith('python'):
  1779. foundVer = True
  1780. flags.add(dirName[6:])
  1781. if foundLib and foundVer:
  1782. break
  1783. if self.pythonFlagNamespace:
  1784. flags = set('%s:%s' %(self.pythonFlagNamespace, x) for x in flags)
  1785. return flags
  1786. def _stringIsPythonVersion(self, s):
  1787. return not set(s).difference(set('.0123456789'))
  1788. def _getPythonVersionFromFlags(self, flags):
  1789. nameSpace = self.pythonFlagNamespace
  1790. for flag in flags:
  1791. if nameSpace and flag.startswith(nameSpace):
  1792. flag = flag[len(nameSpace):]
  1793. if self._stringIsPythonVersion(flag):
  1794. return 'python'+flag
  1795. def _getPythonVersionFromPath(self, pathName, destdir):
  1796. if destdir and pathName.startswith(destdir):
  1797. pathName = pathName[len(destdir):]
  1798. pathList = pathName.split('/')
  1799. for dirName in pathList:
  1800. if dirName.startswith('python') and self._stringIsPythonVersion(
  1801. dirName[6:]):
  1802. # python2.4 or python2.5 or python3.9 but not python.so
  1803. return dirName
  1804. return ''
  1805. def _isCIL(self, m):
  1806. return m and m.name == 'CIL'
  1807. def _isJava(self, m, contents=None):
  1808. return m and isinstance(m, (magic.jar, magic.java)) and self._hasContents(m, contents)
  1809. def _isPerlModule(self, path):
  1810. return (path.endswith('.pm') or
  1811. path.endswith('.pl') or
  1812. path.endswith('.ph'))
  1813. def _isPerl(self, path, m, f):
  1814. return self._isPerlModule(path) or (
  1815. f.inode.perms() & 0111 and m and m.name == 'script'
  1816. and 'interpreter' in m.contents
  1817. and '/bin/perl' in m.contents['interpreter'])
  1818. def _createELFDepSet(self, m, elfinfo, recipe=None, basedir=None,
  1819. soname=None, soflags=None,
  1820. libPathMap={}, getRPATH=None, path=None,
  1821. isProvides=None):
  1822. """
  1823. Add dependencies from ELF information.
  1824. @param m: magic.ELF object
  1825. @param elfinfo: requires or provides from magic.ELF.contents
  1826. @param recipe: recipe object for calling Requires if basedir is not None
  1827. @param basedir: directory to add into dependency
  1828. @param soname: alternative soname to use
  1829. @param libPathMap: mapping from base dependency name to new dependency name
  1830. @param isProvides: whether the dependency being created is a provides
  1831. """
  1832. abi = m.contents['abi']
  1833. elfClass = abi[0]
  1834. nameMap = {}
  1835. usesLinuxAbi = False
  1836. depSet = deps.DependencySet()
  1837. for depClass, main, flags in elfinfo:
  1838. if soflags:
  1839. flags = itertools.chain(*(flags, soflags))
  1840. flags = [ (x, deps.FLAG_SENSE_REQUIRED) for x in flags ]
  1841. if depClass == 'soname':
  1842. if '/' in main:
  1843. main = os.path.basename(main)
  1844. if getRPATH:
  1845. rpath = getRPATH(main)
  1846. if rpath:
  1847. # change the name to follow the rpath
  1848. main = '/'.join((rpath, main))
  1849. elif soname:
  1850. main = soname
  1851. if basedir:
  1852. oldname = os.path.normpath('/'.join((elfClass, main)))
  1853. main = '/'.join((basedir, main))
  1854. main = os.path.normpath('/'.join((elfClass, main)))
  1855. if basedir:
  1856. nameMap[main] = oldname
  1857. if libPathMap and main in libPathMap:
  1858. # if we have a mapping to a provided library that would be
  1859. # satisfied, then we modify the requirement to match the
  1860. # provision
  1861. provided = libPathMap[main]
  1862. requiredSet = set(x[0] for x in flags)
  1863. providedSet = set(provided.flags.keys())
  1864. if requiredSet.issubset(providedSet):
  1865. main = provided.getName()[0]
  1866. else:
  1867. pathString = ''
  1868. if path:
  1869. pathString = 'for path %s' %path
  1870. self.warn('Not replacing %s with %s because of missing %s%s',
  1871. main, provided.getName()[0],
  1872. sorted(list(requiredSet-providedSet)),
  1873. pathString)
  1874. curClass = deps.SonameDependencies
  1875. for flag in abi[1]:
  1876. if flag == 'Linux':
  1877. usesLinuxAbi = True
  1878. flags.append(('SysV', deps.FLAG_SENSE_REQUIRED))
  1879. else:
  1880. flags.append((flag, deps.FLAG_SENSE_REQUIRED))
  1881. dep = deps.Dependency(main, flags)
  1882. elif depClass == 'abi':
  1883. curClass = deps.AbiDependency
  1884. dep = deps.Dependency(main, flags)
  1885. else:
  1886. assert(0)
  1887. depSet.addDep(curClass, dep)
  1888. # This loop has to happen late so that the soname
  1889. # flag merging from multiple flag instances has happened
  1890. if nameMap:
  1891. for soDep in depSet.iterDepsByClass(deps.SonameDependencies):
  1892. newName = soDep.getName()[0]
  1893. if newName in nameMap:
  1894. oldName = nameMap[newName]
  1895. recipe.Requires(_privateDepMap=(oldname, soDep))
  1896. if usesLinuxAbi and not isProvides:
  1897. isnset = m.contents.get('isnset', None)
  1898. if elfClass == 'ELF32' and isnset == 'x86':
  1899. main = 'ELF32/ld-linux.so.2'
  1900. elif elfClass == 'ELF64' and isnset == 'x86_64':
  1901. main = 'ELF64/ld-linux-x86-64.so.2'
  1902. else:
  1903. self.error('%s: unknown ELF class %s or instruction set %s',
  1904. path, elfClass, isnset)
  1905. return depSet
  1906. flags = [('Linux', deps.FLAG_SENSE_REQUIRED),
  1907. ('SysV', deps.FLAG_SENSE_REQUIRED),
  1908. (isnset, deps.FLAG_SENSE_REQUIRED)]
  1909. dep = deps.Dependency(main, flags)
  1910. depSet.addDep(curClass, dep)
  1911. return depSet
  1912. def _addDepToMap(self, path, depMap, depType, dep):
  1913. "Add a single dependency to a map, regardless of whether path was listed before"
  1914. if path not in depMap:
  1915. depMap[path] = deps.DependencySet()
  1916. depMap[path].addDep(depType, dep)
  1917. def _addDepSetToMap(self, path, depMap, depSet):
  1918. "Add a dependency set to a map, regardless of whether path was listed before"
  1919. if path in depMap:
  1920. depMap[path].union(depSet)
  1921. else:
  1922. depMap[path] = depSet
  1923. @staticmethod
  1924. def _recurseSymlink(path, destdir, fullpath=None):
  1925. """
  1926. Recurse through symlinks in destdir and get the final path and fullpath.
  1927. If initial fullpath (or destdir+path if fullpath not specified)
  1928. does not exist, return path.
  1929. """
  1930. if fullpath is None:
  1931. fullpath = destdir + path
  1932. while os.path.islink(fullpath):
  1933. contents = os.readlink(fullpath)
  1934. if contents.startswith('/'):
  1935. fullpath = os.path.normpath(contents)
  1936. else:
  1937. fullpath = os.path.normpath(
  1938. os.path.dirname(fullpath)+'/'+contents)
  1939. return fullpath[len(destdir):], fullpath
  1940. def _symlinkMagic(self, path, fullpath, macros, m=None):
  1941. "Recurse through symlinks and get the final path and magic"
  1942. path, _ = self._recurseSymlink(path, macros.destdir, fullpath=fullpath)
  1943. m = self.recipe.magic[path]
  1944. return m, path
  1945. def _enforceProvidedPath(self, path, fileType='interpreter',
  1946. unmanagedError=False):
  1947. db = self._getDb()
  1948. troveNames = [ x.getName() for x in db.iterTrovesByPath(path) ]
  1949. if not troveNames:
  1950. talk = {True: self.error, False: self.warn}[bool(unmanagedError)]
  1951. talk('%s file %s not managed by conary' %(fileType, path))
  1952. return None
  1953. troveName = troveNames[0]
  1954. # prefer corresponding :devel to :devellib if it exists
  1955. package, component = troveName.split(':', 1)
  1956. if component in ('devellib', 'lib'):
  1957. for preferredComponent in ('devel', 'devellib'):
  1958. troveSpec = (
  1959. ':'.join((package, preferredComponent)),
  1960. None, None
  1961. )
  1962. results = db.findTroves(None, [troveSpec],
  1963. allowMissing = True)
  1964. if troveSpec in results:
  1965. troveName = results[troveSpec][0][0]
  1966. break
  1967. if troveName not in self.recipe._getTransitiveBuildRequiresNames():
  1968. self.recipe.reportMissingBuildRequires(troveName)
  1969. return troveName
  1970. def _getRuby(self, macros, path):
  1971. # For bootstrapping purposes, prefer the just-built version if
  1972. # it exists
  1973. # Returns tuple: (pathToRubyInterpreter, bootstrap)
  1974. ruby = '%(ruby)s' %macros
  1975. if os.access('%(destdir)s/%(ruby)s' %macros, os.X_OK):
  1976. return '%(destdir)s/%(ruby)s' %macros, True
  1977. elif os.access(ruby, os.X_OK):
  1978. # Enforce the build requirement, since it is not in the package
  1979. self._enforceProvidedPath(ruby)
  1980. return ruby, False
  1981. else:
  1982. self.warn('%s not available for Ruby dependency discovery'
  1983. ' for path %s' %(ruby, path))
  1984. return False, None
  1985. def _getRubyLoadPath(self, macros, rubyInvocation, bootstrap):
  1986. # Returns tuple of (invocationString, loadPathList)
  1987. destdir = macros.destdir
  1988. if bootstrap:
  1989. rubyInvocation = (('LD_LIBRARY_PATH=%(destdir)s%(libdir)s '
  1990. +rubyInvocation)%macros)
  1991. rubyLoadPath = util.popen("%s -e 'puts $:'" %rubyInvocation).readlines()
  1992. rubyLoadPath = [ x.strip() for x in rubyLoadPath if x.startswith('/') ]
  1993. loadPathList = rubyLoadPath[:]
  1994. if bootstrap:
  1995. rubyLoadPath = [ destdir+x for x in rubyLoadPath ]
  1996. rubyInvocation = ('LD_LIBRARY_PATH=%(destdir)s%(libdir)s'
  1997. ' RUBYLIB="'+':'.join(rubyLoadPath)+'"'
  1998. ' %(destdir)s/%(ruby)s') % macros
  1999. return (rubyInvocation, loadPathList)
  2000. def _getRubyVersion(self, macros):
  2001. cmd = self.rubyInvocation + (" -e 'puts RUBY_VERSION'" % macros)
  2002. rubyVersion = util.popen(cmd).read()
  2003. rubyVersion = '.'.join(rubyVersion.split('.')[0:2])
  2004. return rubyVersion
  2005. def _getRubyFlagsFromPath(self, pathName, rubyVersion):
  2006. pathList = pathName.split('/')
  2007. pathList = [ x for x in pathList if x ]
  2008. foundLib = False
  2009. foundVer = False
  2010. flags = set()
  2011. for dirName in pathList:
  2012. if not foundLib and dirName.startswith('lib'):
  2013. foundLib = True
  2014. flags.add(dirName)
  2015. elif not foundVer and dirName == rubyVersion:
  2016. foundVer = True
  2017. flags.add(dirName)
  2018. if foundLib and foundVer:
  2019. break
  2020. return flags
  2021. def _getmonodis(self, macros, path):
  2022. # For bootstrapping purposes, prefer the just-built version if
  2023. # it exists
  2024. monodis = '%(monodis)s' %macros
  2025. if os.access('%(destdir)s/%(monodis)s' %macros, os.X_OK):
  2026. return ('MONO_PATH=%(destdir)s%(prefix)s/lib'
  2027. ' LD_LIBRARY_PATH=%(destdir)s%(libdir)s'
  2028. ' %(destdir)s/%(monodis)s' %macros)
  2029. elif os.access(monodis, os.X_OK):
  2030. # Enforce the build requirement, since it is not in the package
  2031. self._enforceProvidedPath(monodis)
  2032. return monodis
  2033. else:
  2034. self.warn('%s not available for CIL dependency discovery'
  2035. ' for path %s' %(monodis, path))
  2036. return None
  2037. def _getperlincpath(self, perl, destdir):
  2038. """
  2039. Fetch the perl @INC path, falling back to bootstrapPerlIncPath
  2040. only if perl cannot be run. All elements of the search path
  2041. will be resolved against symlinks in destdir if they exist. (CNY-2949)
  2042. """
  2043. if not perl:
  2044. return []
  2045. p = util.popen(r"""%s -e 'print join("\n", @INC)'""" %perl)
  2046. perlIncPath = p.readlines()
  2047. # make sure that the command completed successfully
  2048. try:
  2049. rc = p.close()
  2050. perlIncPath = [x.strip() for x in perlIncPath if not x.startswith('.')]
  2051. return [self._recurseSymlink(x, destdir)[0] for x in perlIncPath]
  2052. except RuntimeError:
  2053. return [self._recurseSymlink(x, destdir)[0]
  2054. for x in self.bootstrapPerlIncPath]
  2055. def _getperl(self, macros, recipe):
  2056. """
  2057. Find the preferred instance of perl to use, including setting
  2058. any environment variables necessary to use that perl.
  2059. Returns string for running it, the C{@INC} path, and a separate
  2060. string, if necessary, for adding to @INC.
  2061. """
  2062. perlDestPath = '%(destdir)s%(bindir)s/perl' %macros
  2063. # not %(bindir)s so that package modifications do not affect
  2064. # the search for system perl
  2065. perlPath = '/usr/bin/perl'
  2066. destdir = macros.destdir
  2067. def _perlDestInc(destdir, perlDestInc):
  2068. return ' '.join(['-I' + destdir + x for x in perlDestInc])
  2069. if os.access(perlDestPath, os.X_OK):
  2070. # must use packaged perl if it exists
  2071. m = recipe.magic[perlDestPath[len(destdir):]] # not perlPath
  2072. if m and 'RPATH' in m.contents and m.contents['RPATH']:
  2073. # we need to prepend the destdir to each element of the RPATH
  2074. # in order to run perl in the destdir
  2075. perl = ''.join((
  2076. 'export LD_LIBRARY_PATH=',
  2077. '%s%s:' %(destdir, macros.libdir),
  2078. ':'.join([destdir+x
  2079. for x in m.contents['RPATH'].split(':')]),
  2080. ';',
  2081. perlDestPath
  2082. ))
  2083. perlIncPath = self._getperlincpath(perl, destdir)
  2084. perlDestInc = _perlDestInc(destdir, perlIncPath)
  2085. return [perl, perlIncPath, perlDestInc]
  2086. else:
  2087. # perl that does not use/need rpath
  2088. perl = 'LD_LIBRARY_PATH=%s%s %s' %(
  2089. destdir, macros.libdir, perlDestPath)
  2090. perlIncPath = self._getperlincpath(perl, destdir)
  2091. perlDestInc = _perlDestInc(destdir, perlIncPath)
  2092. return [perl, perlIncPath, perlDestInc]
  2093. elif os.access(perlPath, os.X_OK):
  2094. # system perl if no packaged perl, needs no @INC mangling
  2095. self._enforceProvidedPath(perlPath)
  2096. perlIncPath = self._getperlincpath(perlPath, destdir)
  2097. return [perlPath, perlIncPath, '']
  2098. # must be no perl at all
  2099. return ['', [], '']
  2100. def _getPython(self, macros, path):
  2101. """
  2102. Takes a path
  2103. Returns, for that path, a tuple of
  2104. - the preferred instance of python to use
  2105. - whether that instance is in the destdir
  2106. """
  2107. m = self.recipe.magic[path]
  2108. if m and m.name == 'script' and 'python' in m.contents['interpreter']:
  2109. pythonPath = [m.contents['interpreter']]
  2110. else:
  2111. pythonVersion = self._getPythonVersionFromPath(path, None)
  2112. # After PATH, fall back to %(bindir)s. If %(bindir)s should be
  2113. # preferred, it needs to be earlier in the PATH. Include
  2114. # unversioned python as a last resort for confusing cases.
  2115. shellPath = os.environ.get('PATH', '').split(':') + [ '%(bindir)s' ]
  2116. pythonPath = []
  2117. if pythonVersion:
  2118. pythonPath = [ os.path.join(x, pythonVersion) for x in shellPath ]
  2119. pythonPath.extend([ os.path.join(x, 'python') for x in shellPath ])
  2120. for pathElement in pythonPath:
  2121. pythonDestPath = ('%(destdir)s'+pathElement) %macros
  2122. if os.access(pythonDestPath, os.X_OK):
  2123. return (pythonDestPath, True)
  2124. for pathElement in pythonPath:
  2125. pythonDestPath = pathElement %macros
  2126. if os.access(pythonDestPath, os.X_OK):
  2127. self._enforceProvidedPath(pythonDestPath)
  2128. return (pythonDestPath, False)
  2129. # Specified python not found on system (usually because of
  2130. # bad interpreter path -- CNY-2050)
  2131. if len(pythonPath) == 1:
  2132. missingPythonPath = '%s ' % pythonPath[0]
  2133. else:
  2134. missingPythonPath = ''
  2135. self.warn('Python interpreter %snot found for %s',
  2136. missingPythonPath, path)
  2137. return (None, None)
  2138. def _stripDestDir(self, pathList, destdir):
  2139. destDirLen = len(destdir)
  2140. pathElementList = []
  2141. for pathElement in pathList:
  2142. if pathElement.startswith(destdir):
  2143. pathElementList.append(pathElement[destDirLen:])
  2144. else:
  2145. pathElementList.append(pathElement)
  2146. return pathElementList
  2147. class Provides(_dependency):
  2148. """
  2149. NAME
  2150. ====
  2151. B{C{r.Provides()}} - Creates dependency provision
  2152. SYNOPSIS
  2153. ========
  2154. C{r.Provides([I{provision}, I{filterexp}] || [I{exceptions=filterexp}])}
  2155. DESCRIPTION
  2156. ===========
  2157. The C{r.Provides()} policy marks files as providing certain features
  2158. or characteristics, and can be called to explicitly provide things
  2159. that cannot be automatically discovered. C{r.Provides} can also override
  2160. automatic discovery, and prevent marking a file as providing things, such
  2161. as for package-private plugin modules installed in system library
  2162. directories.
  2163. A C{I{provision}} may be C{'file'} to mark a file as providing its
  2164. filename, or a dependency type. You can create a file, soname or
  2165. ABI C{I{provision}} manually; all other types are only automatically
  2166. discovered. Provisions that begin with C{file} are files, those that
  2167. start with C{soname:} are sonames, and those that start with C{abi:}
  2168. are ABIs. Other prefixes are reserved.
  2169. Soname provisions are normally discovered automatically; they need
  2170. to be provided manually only in two cases:
  2171. - If a shared library was not built with a soname at all.
  2172. - If a symbolic link to a shared library needs to provide its name
  2173. as a soname.
  2174. Note: Use {Cr.ComponentProvides} rather than C{r.Provides} to add
  2175. capability flags to components.
  2176. For unusual cases where you want to remove a provision Conary
  2177. automatically finds, you can specify C{r.Provides(exceptDeps='regexp')}
  2178. to override all provisions matching a regular expression,
  2179. C{r.Provides(exceptDeps=('filterexp', 'regexp'))}
  2180. to override provisions matching a regular expression only for files
  2181. matching filterexp, or
  2182. C{r.Provides(exceptDeps=(('filterexp', 'regexp'), ...))} to specify
  2183. multiple overrides.
  2184. EXAMPLES
  2185. ========
  2186. C{r.Provides('file', '/usr/share/dict/words')}
  2187. Demonstrates using C{r.Provides} to specify the file provision
  2188. C{/usr/share/dict/words}, so that other files can now require that file.
  2189. C{r.Provides('soname: libperl.so', '%(libdir)s/perl5/.*/CORE/libperl.so')}
  2190. Demonstrates synthesizing a shared library provision for all the
  2191. libperl.so symlinks.
  2192. C{r.Provides(exceptDeps = 'java: .*')}
  2193. Demonstrates removing all java provisions.
  2194. """
  2195. bucket = policy.PACKAGE_CREATION
  2196. requires = (
  2197. ('PackageSpec', policy.REQUIRED_PRIOR),
  2198. ('SharedLibrary', policy.REQUIRED),
  2199. # _ELFPathProvide calls Requires to pass in discovered info
  2200. # _addCILPolicyProvides does likewise
  2201. ('Requires', policy.REQUIRED_SUBSEQUENT),
  2202. )
  2203. filetree = policy.PACKAGE
  2204. invariantexceptions = (
  2205. '%(docdir)s/',
  2206. )
  2207. dbDepCacheClass = _DatabaseDepCache
  2208. def __init__(self, *args, **keywords):
  2209. _dependency.__init__(self, *args, **keywords)
  2210. self.provisions = []
  2211. self.sonameSubtrees = set()
  2212. self.sysPath = None
  2213. self.monodisPath = None
  2214. self.rubyInterpreter = None
  2215. self.rubyVersion = None
  2216. self.rubyInvocation = None
  2217. self.rubyLoadPath = None
  2218. self.perlIncPath = None
  2219. self.pythonSysPathMap = {}
  2220. self.exceptDeps = []
  2221. policy.Policy.__init__(self, *args, **keywords)
  2222. self.depCache = self.dbDepCacheClass(self._getDb())
  2223. def updateArgs(self, *args, **keywords):
  2224. if args:
  2225. for filespec in args[1:]:
  2226. self.provisions.append((filespec, args[0]))
  2227. sonameSubtrees = keywords.pop('sonameSubtrees', None)
  2228. if sonameSubtrees:
  2229. if type(sonameSubtrees) in (list, tuple):
  2230. self.sonameSubtrees.update(set(sonameSubtrees))
  2231. else:
  2232. self.sonameSubtrees.add(sonameSubtrees)
  2233. exceptDeps = keywords.pop('exceptDeps', None)
  2234. if exceptDeps:
  2235. if type(exceptDeps) is str:
  2236. exceptDeps = ('.*', exceptDeps)
  2237. assert(type(exceptDeps) == tuple)
  2238. if type(exceptDeps[0]) is tuple:
  2239. self.exceptDeps.extend(exceptDeps)
  2240. else:
  2241. self.exceptDeps.append(exceptDeps)
  2242. # The next three are called only from Requires and should override
  2243. # completely to make sure the policies are in sync
  2244. pythonFlagNamespace = keywords.pop('_pythonFlagNamespace', None)
  2245. if pythonFlagNamespace is not None:
  2246. self.pythonFlagNamespace = pythonFlagNamespace
  2247. bootstrapPythonFlags = keywords.pop('_bootstrapPythonFlags', None)
  2248. if bootstrapPythonFlags is not None:
  2249. self.bootstrapPythonFlags = bootstrapPythonFlags
  2250. bootstrapSysPath = keywords.pop('_bootstrapSysPath', None)
  2251. if bootstrapSysPath is not None:
  2252. self.bootstrapSysPath = bootstrapSysPath
  2253. bootstrapPerlIncPath = keywords.pop('_bootstrapPerlIncPath', None)
  2254. if bootstrapPerlIncPath is not None:
  2255. self.bootstrapPerlIncPath = bootstrapPerlIncPath
  2256. if keywords.get('removeFlagsByDependencyClass', None):
  2257. self.error('removeFlagsByDependencyClass not currently implemented for Provides (CNY-3443)')
  2258. _dependency.updateArgs(self, **keywords)
  2259. def preProcess(self):
  2260. macros = self.macros
  2261. if self.bootstrapPythonFlags is not None:
  2262. self.bootstrapPythonFlags = set(x % macros
  2263. for x in self.bootstrapPythonFlags)
  2264. if self.bootstrapSysPath:
  2265. self.bootstrapSysPath = [x % macros for x in self.bootstrapSysPath]
  2266. if self.pythonFlagNamespace is not None:
  2267. self.pythonFlagNamespace = self.pythonFlagNamespace % macros
  2268. if self.bootstrapPerlIncPath:
  2269. self.bootstrapPerlIncPath = [x % macros for x in self.bootstrapPerlIncPath]
  2270. self.rootdir = self.rootdir % macros
  2271. self.fileFilters = []
  2272. self.binDirs = frozenset(
  2273. x % macros for x in [
  2274. '%(bindir)s', '%(sbindir)s',
  2275. '%(essentialbindir)s', '%(essentialsbindir)s',
  2276. '%(libexecdir)s', ])
  2277. self.noProvDirs = frozenset(
  2278. x % macros for x in [
  2279. '%(testdir)s',
  2280. '%(debuglibdir)s',
  2281. ]).union(self.binDirs)
  2282. exceptDeps = []
  2283. for fE, rE in self.exceptDeps:
  2284. try:
  2285. exceptDeps.append((filter.Filter(fE, macros),
  2286. re.compile(rE % self.macros)))
  2287. except sre_constants.error, e:
  2288. self.error('Bad regular expression %s for file spec %s: %s', rE, fE, e)
  2289. self.exceptDeps= exceptDeps
  2290. for filespec, provision in self.provisions:
  2291. self.fileFilters.append(
  2292. (filter.Filter(filespec, macros), provision % macros))
  2293. del self.provisions
  2294. _dependency.preProcess(self)
  2295. def doFile(self, path):
  2296. pkgs = self.recipe.autopkg.findComponents(path)
  2297. if not pkgs:
  2298. return
  2299. pkgFiles = [(x, x.getFile(path)) for x in pkgs]
  2300. macros = self.recipe.macros
  2301. m = self.recipe.magic[path]
  2302. fullpath = macros.destdir + path
  2303. basepath = os.path.basename(path)
  2304. dirpath = os.path.dirname(path)
  2305. if os.path.exists(fullpath):
  2306. mode = os.lstat(fullpath)[stat.ST_MODE]
  2307. # First, add in the manual provisions
  2308. self.addExplicitProvides(path, fullpath, pkgFiles, macros, m)
  2309. # Next, discover all automatically-discoverable provisions
  2310. if os.path.exists(fullpath):
  2311. if (self._isELF(m, 'abi')
  2312. and m.contents['Type'] != elf.ET_EXEC
  2313. and not [ x for x in self.noProvDirs if path.startswith(x) ]):
  2314. # we do not add elf provides for programs that won't be linked to
  2315. self._ELFAddProvide(path, m, pkgFiles, basedir=dirpath)
  2316. if dirpath in self.sonameSubtrees:
  2317. # only export filename as soname if is shlib
  2318. sm, finalpath = self._symlinkMagic(path, fullpath, macros, m)
  2319. if sm and self._isELF(sm, 'abi') and sm.contents['Type'] != elf.ET_EXEC:
  2320. # add the filename as a soname provision (CNY-699)
  2321. # note: no provides necessary
  2322. self._ELFAddProvide(path, sm, pkgFiles, soname=basepath, basedir=dirpath)
  2323. if self._isPythonModuleCandidate(path):
  2324. self._addPythonProvides(path, m, pkgFiles, macros)
  2325. rubyProv = self._isRubyModule(path, macros, fullpath)
  2326. if rubyProv:
  2327. self._addRubyProvides(path, m, pkgFiles, macros, rubyProv)
  2328. elif self._isCIL(m):
  2329. self._addCILProvides(path, m, pkgFiles, macros)
  2330. elif self.CILPolicyRE.match(path):
  2331. self._addCILPolicyProvides(path, pkgFiles, macros)
  2332. elif self._isJava(m, 'provides'):
  2333. # Cache the internal provides
  2334. if not hasattr(self.recipe, '_internalJavaDepMap'):
  2335. self.recipe._internalJavaDepMap = None
  2336. self._addJavaProvides(path, m, pkgFiles)
  2337. elif self._isPerlModule(path):
  2338. self._addPerlProvides(path, m, pkgFiles)
  2339. self.addPathDeps(path, dirpath, pkgFiles)
  2340. self.whiteOut(path, pkgFiles)
  2341. self.unionDeps(path, pkgFiles)
  2342. def whiteOut(self, path, pkgFiles):
  2343. # remove intentionally discarded provides
  2344. for pkg, f in pkgFiles:
  2345. if self.exceptDeps and path in pkg.providesMap:
  2346. depSet = deps.DependencySet()
  2347. for depClass, dep in pkg.providesMap[path].iterDeps():
  2348. for filt, exceptRe in self.exceptDeps:
  2349. if filt.match(path):
  2350. matchName = '%s: %s' %(depClass.tagName, str(dep))
  2351. if exceptRe.match(matchName):
  2352. # found one to not copy
  2353. dep = None
  2354. break
  2355. if dep is not None:
  2356. depSet.addDep(depClass, dep)
  2357. pkg.providesMap[path] = depSet
  2358. def addExplicitProvides(self, path, fullpath, pkgFiles, macros, m):
  2359. for (filter, provision) in self.fileFilters:
  2360. if filter.match(path):
  2361. self._markProvides(path, fullpath, provision, pkgFiles, macros, m)
  2362. def addPathDeps(self, path, dirpath, pkgFiles):
  2363. # Because paths can change, individual files do not provide their
  2364. # paths. However, within a trove, a file does provide its name.
  2365. # Furthermore, non-regular files can be path dependency targets
  2366. # Therefore, we have to handle this case a bit differently.
  2367. for pkg, f in pkgFiles:
  2368. if dirpath in self.binDirs and not isinstance(f, files.Directory):
  2369. # CNY-930: automatically export paths in bindirs
  2370. # CNY-1721: but not directories in bindirs
  2371. f.flags.isPathDependencyTarget(True)
  2372. if f.flags.isPathDependencyTarget():
  2373. pkg.provides.addDep(deps.FileDependencies, deps.Dependency(path))
  2374. def unionDeps(self, path, pkgFiles):
  2375. for pkg, f in pkgFiles:
  2376. if path in pkg.providesMap:
  2377. f.provides.set(pkg.providesMap[path])
  2378. pkg.provides.union(f.provides())
  2379. def _getELFinfo(self, m, soname):
  2380. if 'provides' in m.contents and m.contents['provides']:
  2381. return m.contents['provides']
  2382. else:
  2383. # we need to synthesize some provides information
  2384. return [('soname', soname, ())]
  2385. def _ELFAddProvide(self, path, m, pkgFiles, soname=None, soflags=None, basedir=None):
  2386. if basedir is None:
  2387. basedir = os.path.dirname(path)
  2388. if basedir in self.sonameSubtrees:
  2389. # do not record the basedir
  2390. basedir = None
  2391. else:
  2392. # path needs to be in the dependency, since the
  2393. # provides is too broad otherwise, so add it.
  2394. # We can only add characters from the path that are legal
  2395. # in a dependency name
  2396. basedir = ''.join(x for x in basedir if self.legalCharsRE.match(x))
  2397. elfinfo = self._getELFinfo(m, os.path.basename(path))
  2398. depSet = self._createELFDepSet(m, elfinfo,
  2399. recipe=self.recipe, basedir=basedir,
  2400. soname=soname, soflags=soflags,
  2401. path=path, isProvides=True)
  2402. for pkg, _ in pkgFiles:
  2403. self._addDepSetToMap(path, pkg.providesMap, depSet)
  2404. def _getPythonProvidesSysPath(self, path):
  2405. """Generate an ordered list of python paths for the target package.
  2406. This includes the current system path, plus any paths added by the new
  2407. package in the destdir through .pth files or a newly built python.
  2408. @return: (sysPath, pythonVersion)
  2409. """
  2410. pythonPath, bootstrapPython = self._getPython(self.macros, path)
  2411. if not pythonPath:
  2412. # Most likely bad interpreter path in a .py file
  2413. return (None, None)
  2414. if pythonPath in self.pythonSysPathMap:
  2415. return self.pythonSysPathMap[pythonPath]
  2416. destdir = self.macros.destdir
  2417. libdir = self.macros.libdir
  2418. pythonVersion = self._getPythonVersion(pythonPath, destdir, libdir)
  2419. # Get default sys.path from python interpreter, either the one just
  2420. # built (in the case of a python bootstrap) or from the system.
  2421. systemPaths = set(self._getPythonSysPath(pythonPath, destdir, libdir,
  2422. useDestDir=False))
  2423. # Now add paths from the destdir's site-packages, typically due to
  2424. # newly installed .pth files.
  2425. systemPaths.update(self._getPythonSysPath(pythonPath, destdir, libdir,
  2426. useDestDir=True))
  2427. # Sort in descending order so that the longest path matches first.
  2428. sysPath = sorted(self._stripDestDir(systemPaths, destdir), reverse=True)
  2429. self.pythonSysPathMap[pythonPath] = (sysPath, pythonVersion)
  2430. return self.pythonSysPathMap[pythonPath]
  2431. def _fetchPerlIncPath(self):
  2432. """
  2433. Cache the perl @INC path, sorted longest first
  2434. """
  2435. if self.perlIncPath is not None:
  2436. return
  2437. _, self.perlIncPath, _ = self._getperl(
  2438. self.recipe.macros, self.recipe)
  2439. self.perlIncPath.sort(key=len, reverse=True)
  2440. def _addPythonProvides(self, path, m, pkgFiles, macros):
  2441. if not self._isPythonModuleCandidate(path):
  2442. return
  2443. sysPath, pythonVersion = self._getPythonProvidesSysPath(path)
  2444. if not sysPath:
  2445. return
  2446. # Add provides for every match in sys.path. For example, PIL.Imaging
  2447. # and Imaging should both be provided since they are both reachable
  2448. # names.
  2449. for sysPathEntry in sysPath:
  2450. if not path.startswith(sysPathEntry):
  2451. continue
  2452. newDepPath = path[len(sysPathEntry)+1:]
  2453. if newDepPath.split('.')[0] == '__init__':
  2454. # we don't allow bare __init__ as a python import
  2455. # hopefully we'll find this init as a deeper import at some
  2456. # other point in the sysPath
  2457. continue
  2458. elif 'site-packages' in newDepPath:
  2459. # site-packages should be specifically excluded since both it
  2460. # and its parent are always in sys.path. However, invalid
  2461. # python package names in general are allowed due to certain
  2462. # cases where relative imports happen inside a hyphenated
  2463. # directory and the requires detector picks up on that.
  2464. continue
  2465. # Note that it's possible to have a false positive here. For
  2466. # example, in the PIL case if PIL/__init__.py did not exist,
  2467. # PIL.Imaging would still be provided. The odds of this causing
  2468. # problems are so small that it is not checked for here.
  2469. self._addPythonProvidesSingle(path, m, pkgFiles, macros,
  2470. newDepPath)
  2471. def _addPythonProvidesSingle(self, path, m, pkgFiles, macros, depPath):
  2472. # remove extension
  2473. depPath, extn = depPath.rsplit('.', 1)
  2474. if depPath == '__future__':
  2475. return
  2476. if depPath.endswith('/__init__'):
  2477. depPath = depPath.replace('/__init__', '')
  2478. depPath = depPath.replace('/', '.')
  2479. depPaths = [ depPath ]
  2480. if extn == 'so':
  2481. fname = util.joinPaths(macros.destdir, path)
  2482. try:
  2483. syms = elf.getDynSym(fname)
  2484. # Does this module have an init<blah> function?
  2485. initfuncs = [ x[4:] for x in syms if x.startswith('init') ]
  2486. # This is the equivalent of dirname()
  2487. comps = depPath.rsplit('.', 1)
  2488. dpPrefix = comps[0]
  2489. if len(comps) == 1:
  2490. # Top-level python module
  2491. depPaths.extend(initfuncs)
  2492. else:
  2493. for initfunc in initfuncs:
  2494. depPaths.append('.'.join([dpPrefix, initfunc]))
  2495. except elf.error:
  2496. pass
  2497. flags = self._getPythonFlagsFromPath(path)
  2498. flags = [(x, deps.FLAG_SENSE_REQUIRED) for x in sorted(list(flags))]
  2499. for dpath in depPaths:
  2500. dep = deps.Dependency(dpath, flags)
  2501. for pkg, _ in pkgFiles:
  2502. self._addDepToMap(path, pkg.providesMap, deps.PythonDependencies, dep)
  2503. def _addOneCILProvide(self, pkgFiles, path, name, ver):
  2504. for pkg, _ in pkgFiles:
  2505. self._addDepToMap(path, pkg.providesMap, deps.CILDependencies,
  2506. deps.Dependency(name, [(ver, deps.FLAG_SENSE_REQUIRED)]))
  2507. def _addCILPolicyProvides(self, path, pkgFiles, macros):
  2508. if ElementTree is None:
  2509. return
  2510. try:
  2511. keys = {'urn': '{urn:schemas-microsoft-com:asm.v1}'}
  2512. fullpath = macros.destdir + path
  2513. tree = ElementTree.parse(fullpath)
  2514. root = tree.getroot()
  2515. identity, redirect = root.find('runtime/%(urn)sassemblyBinding/%(urn)sdependentAssembly' % keys).getchildren()
  2516. assembly = identity.get('name')
  2517. self._addOneCILProvide(pkgFiles, path, assembly,
  2518. redirect.get('oldVersion'))
  2519. self.recipe.Requires(_CILPolicyProvides={
  2520. path: (assembly, redirect.get('newVersion'))})
  2521. except:
  2522. return
  2523. def _addCILProvides(self, path, m, pkgFiles, macros):
  2524. if not m or m.name != 'CIL':
  2525. return
  2526. fullpath = macros.destdir + path
  2527. if not self.monodisPath:
  2528. self.monodisPath = self._getmonodis(macros, path)
  2529. if not self.monodisPath:
  2530. return
  2531. p = util.popen('%s --assembly %s' %(
  2532. self.monodisPath, fullpath))
  2533. name = None
  2534. ver = None
  2535. for line in [ x.strip() for x in p.readlines() ]:
  2536. if 'Name:' in line:
  2537. name = line.split()[1]
  2538. elif 'Version:' in line:
  2539. ver = line.split()[1]
  2540. p.close()
  2541. # monodis did not give us any info
  2542. if not name or not ver:
  2543. return
  2544. self._addOneCILProvide(pkgFiles, path, name, ver)
  2545. def _isRubyModule(self, path, macros, fullpath):
  2546. if not util.isregular(fullpath) or os.path.islink(fullpath):
  2547. return False
  2548. if '/ruby/' in path:
  2549. # load up ruby opportunistically; this is our first chance
  2550. if self.rubyInterpreter is None:
  2551. self.rubyInterpreter, bootstrap = self._getRuby(macros, path)
  2552. if not self.rubyInterpreter:
  2553. return False
  2554. self.rubyInvocation, self.rubyLoadPath = self._getRubyLoadPath(
  2555. macros, self.rubyInterpreter, bootstrap)
  2556. self.rubyVersion = self._getRubyVersion(macros)
  2557. # we need to look deep first
  2558. self.rubyLoadPath = sorted(list(self.rubyLoadPath),
  2559. key=len, reverse=True)
  2560. elif self.rubyInterpreter is False:
  2561. return False
  2562. for pathElement in self.rubyLoadPath:
  2563. if path.startswith(pathElement) and '.' in os.path.basename(path):
  2564. return path[len(pathElement)+1:].rsplit('.', 1)[0]
  2565. return False
  2566. def _addRubyProvides(self, path, m, pkgFiles, macros, prov):
  2567. flags = self._getRubyFlagsFromPath(path, self.rubyVersion)
  2568. flags = [(x, deps.FLAG_SENSE_REQUIRED) for x in sorted(list(flags))]
  2569. dep = deps.Dependency(prov, flags)
  2570. for pkg, _ in pkgFiles:
  2571. self._addDepToMap(path, pkg.providesMap, deps.RubyDependencies, dep)
  2572. def _addJavaProvides(self, path, m, pkgFiles):
  2573. if 'provides' not in m.contents or not m.contents['provides']:
  2574. return
  2575. if not hasattr(self.recipe, '_reqExceptDeps'):
  2576. self.recipe._reqExceptDeps = []
  2577. # Compile requires exceptDeps (and persist them)
  2578. if not hasattr(self.recipe, '_compiledReqExceptDeps'):
  2579. self.recipe._compiledReqExceptDeps = exceptDeps = []
  2580. macros = self.recipe.macros
  2581. for fE, rE in self.recipe._reqExceptDeps:
  2582. try:
  2583. exceptDeps.append((filter.Filter(fE, macros),
  2584. re.compile(rE % macros)))
  2585. except sre_constants.error, e:
  2586. self.error('Bad regular expression %s for file spec %s: %s',
  2587. rE, fE, e)
  2588. # We will no longer need this, we have the compiled version now
  2589. self.recipe._reqExceptDeps = []
  2590. if self.recipe._internalJavaDepMap is None:
  2591. # Instantiate the dictionary of provides from this package
  2592. self.recipe._internalJavaDepMap = internalJavaDepMap = {}
  2593. componentMap = self.recipe.autopkg.componentMap
  2594. for opath in componentMap:
  2595. om = self.recipe.magic[opath]
  2596. if not self._isJava(om, 'provides'):
  2597. continue
  2598. # The file could be a .jar, in which case it contains multiple
  2599. # classes. contents['files'] is a dict, keyed on the file name
  2600. # within the jar and with a provide and a set of requires as
  2601. # value.
  2602. internalJavaDepMap.setdefault(opath, {}).update(
  2603. om.contents['files'])
  2604. else:
  2605. internalJavaDepMap = self.recipe._internalJavaDepMap
  2606. if hasattr(self.recipe, '_internalJavaProvides'):
  2607. internalProvides = self.recipe._internalJavaProvides
  2608. else:
  2609. # We need to cache the internal java provides, otherwise we do too
  2610. # much work for each file (CNY-3372)
  2611. self.recipe._internalJavaProvides = internalProvides = set()
  2612. for opath, ofiles in internalJavaDepMap.items():
  2613. internalProvides.update(x[0] for x in ofiles.values()
  2614. if x[0] is not None)
  2615. # Now drop internal provides from individual class requires
  2616. for opath, ofiles in internalJavaDepMap.items():
  2617. for oclassName, (oclassProv, oclassReqSet) in ofiles.items():
  2618. if oclassReqSet is None:
  2619. continue
  2620. oclassReqSet.difference_update(internalProvides)
  2621. reqs = set()
  2622. if self._isJava(m, 'requires'):
  2623. # Extract this file's requires
  2624. reqs.update(m.contents['requires'])
  2625. # Remove the ones that are satisfied internally
  2626. reqs.difference_update(internalProvides)
  2627. # For now, we are only trimming the provides (and requires) for
  2628. # classes for which the requires are not satisfied, neither internally
  2629. # nor from the system Conary database. In the future we may need to
  2630. # build a dependency tree between internal classes, such that we do
  2631. # the removal transitively (class A requires class B which doesn't
  2632. # have its deps satisfied should make class A unusable). This can come
  2633. # at a later time
  2634. # CNY-3362: we don't drop provides for classes which had requires on
  2635. # classes that had their dependencies pruned. (at least not yet)
  2636. if reqs:
  2637. # Try to resolve these deps against the Conary database
  2638. depSetList = []
  2639. depSetMap = {}
  2640. for req in reqs:
  2641. depSet = deps.DependencySet()
  2642. depSet.addDep(deps.JavaDependencies, deps.Dependency(req, []))
  2643. depSetList.append(depSet)
  2644. depSetMap[depSet] = req
  2645. troves = self.depCache.getProvides(depSetList)
  2646. missingDepSets = set(depSetList) - set(troves)
  2647. missingReqs = set(depSetMap[x] for x in missingDepSets)
  2648. # White out the missing requires if exceptDeps for them are found
  2649. rExceptDeps = self.recipe._compiledReqExceptDeps
  2650. if missingReqs and rExceptDeps:
  2651. depClass = deps.JavaDependencies
  2652. filteredMissingDeps = set()
  2653. for dep in list(missingReqs):
  2654. for filt, exceptRe in rExceptDeps:
  2655. if not filt.match(path):
  2656. continue
  2657. matchName = '%s: %s' %(depClass.tagName, str(dep))
  2658. if exceptRe.match(matchName):
  2659. # found one to not copy
  2660. missingReqs.remove(dep)
  2661. filteredMissingDeps.add(dep)
  2662. break
  2663. if filteredMissingDeps:
  2664. # We need to take them out of the per-file requires
  2665. ofiles = internalJavaDepMap[path]
  2666. for _, (oclassProv, oclassReqSet) in ofiles.items():
  2667. if oclassProv is not None:
  2668. oclassReqSet.difference_update(filteredMissingDeps)
  2669. if missingReqs:
  2670. fileDeps = internalJavaDepMap[path]
  2671. # This file has unsatisfied dependencies.
  2672. # Walk its list of classes to determine which ones are not
  2673. # satisfied.
  2674. satisfiedClasses = dict((fpath, (fprov, freqs))
  2675. for (fpath, (fprov, freqs)) in fileDeps.iteritems()
  2676. if freqs is not None
  2677. and not freqs.intersection(missingReqs))
  2678. internalJavaDepMap[path] = satisfiedClasses
  2679. self.warn('Provides and requirements for file %s are disabled '
  2680. 'because of unsatisfied dependencies. To re-enable '
  2681. 'them, add to the recipe\'s buildRequires the '
  2682. 'packages that provide the following '
  2683. 'requirements: %s' %
  2684. (path, " ".join(sorted(missingReqs))))
  2685. # Add the remaining provides
  2686. fileDeps = internalJavaDepMap[path]
  2687. provs = set(fprov for fpath, (fprov, freqs) in fileDeps.iteritems()
  2688. if fprov is not None)
  2689. for prov in provs:
  2690. dep = deps.Dependency(prov, [])
  2691. for pkg, _ in pkgFiles:
  2692. self._addDepToMap(path, pkg.providesMap, deps.JavaDependencies, dep)
  2693. def _addPerlProvides(self, path, m, pkgFiles):
  2694. # do not call perl to get @INC unless we have something to do for perl
  2695. self._fetchPerlIncPath()
  2696. # It is possible that we'll want to allow user-specified
  2697. # additions to the perl search path, but if so, we need
  2698. # to path-encode those files, so we can't just prepend
  2699. # those elements to perlIncPath. We would need to end up
  2700. # with something like "perl: /path/to/foo::bar" because
  2701. # for perl scripts that don't modify @INC, they could not
  2702. # find those scripts. It is not clear that we need this
  2703. # at all, because most if not all of those cases would be
  2704. # intra-package dependencies that we do not want to export.
  2705. depPath = None
  2706. for pathPrefix in self.perlIncPath:
  2707. if path.startswith(pathPrefix):
  2708. depPath = path[len(pathPrefix)+1:]
  2709. break
  2710. if depPath is None:
  2711. return
  2712. # foo/bar/baz.pm -> foo::bar::baz
  2713. prov = '::'.join(depPath.split('/')).rsplit('.', 1)[0]
  2714. dep = deps.Dependency(prov, [])
  2715. for pkg, _ in pkgFiles:
  2716. self._addDepToMap(path, pkg.providesMap, deps.PerlDependencies, dep)
  2717. def _markProvides(self, path, fullpath, provision, pkgFiles, macros, m):
  2718. if provision.startswith("file"):
  2719. # can't actually specify what to provide, just that it provides...
  2720. for _, f in pkgFiles:
  2721. f.flags.isPathDependencyTarget(True)
  2722. elif provision.startswith("abi:"):
  2723. abistring = provision[4:].strip()
  2724. op = abistring.index('(')
  2725. abi = abistring[:op]
  2726. flags = abistring[op+1:-1].split()
  2727. flags = [ (x, deps.FLAG_SENSE_REQUIRED) for x in flags ]
  2728. dep = deps.Dependency(abi, flags)
  2729. for pkg, _ in pkgFiles:
  2730. self._addDepToMap(path, pkg.providesMap, deps.AbiDependency, dep)
  2731. elif provision.startswith("soname:"):
  2732. sm, finalpath = self._symlinkMagic(path, fullpath, macros, m)
  2733. if self._isELF(sm, 'abi'):
  2734. # Only ELF files can provide sonames.
  2735. # This is for libraries that don't really include a soname,
  2736. # but programs linked against them require a soname.
  2737. # For this reason, we do not pass 'provides' to _isELF
  2738. soname = provision[7:].strip()
  2739. soflags = []
  2740. if '(' in soname:
  2741. # get list of arbitrary flags
  2742. soname, rest = soname.split('(')
  2743. soflags.extend(rest[:-1].split())
  2744. basedir = None
  2745. if '/' in soname:
  2746. basedir, soname = soname.rsplit('/', 1)
  2747. self._ELFAddProvide(path, sm, pkgFiles, soname=soname, soflags=soflags,
  2748. basedir=basedir)
  2749. else:
  2750. self.error('Provides %s for file %s does not start with one of'
  2751. ' "file", "abi:", or "soname"',
  2752. provision, path)
  2753. class Requires(_addInfo, _dependency):
  2754. """
  2755. NAME
  2756. ====
  2757. B{C{r.Requires()}} - Creates dependency requirements
  2758. SYNOPSIS
  2759. ========
  2760. C{r.Requires([I{/path/to/file}, I{filterexp}] || [I{packagename:component[(FLAGS)]}, I{filterexp}] || [I{exceptions=filterexp)}])}
  2761. DESCRIPTION
  2762. ===========
  2763. The C{r.Requires()} policy adds requirements for a file.
  2764. You can pass in exceptions that should not have automatic requirement
  2765. discovery done, such as example shell scripts outside of C{%(docdir)s}.
  2766. Note: Components are the only troves which can be required.
  2767. For executables executed only through wrappers that
  2768. use C{LD_LIBRARY_PATH} to find the libraries instead of
  2769. embedding an RPATH in the binary, you will need to provide
  2770. a synthetic RPATH using C{r.Requires(rpath='I{RPATH}')}
  2771. or C{r.Requires(rpath=('I{filterExp}', 'I{RPATH}'))} calls,
  2772. which are tested in the order provided.
  2773. The RPATH is a standard Unix-style path string containing one or more
  2774. directory names, separated only by colon characters, except for one
  2775. significant change: Each path component is interpreted using shell-style
  2776. globs, which are checked first in the C{%(destdir)s} and then on the
  2777. installed system. (The globs are useful for cases like perl where
  2778. statically determining the entire content of the path is difficult. Use
  2779. globs only for variable parts of paths; be as specific as you can without
  2780. using the glob feature any more than necessary.)
  2781. Executables that use C{dlopen()} to open a shared library will not
  2782. automatically have a dependency on that shared library. If the program
  2783. unconditionally requires that it be able to C{dlopen()} the shared
  2784. library, encode that requirement by manually creating the requirement
  2785. by calling C{r.Requires('soname: libfoo.so', 'filterexp')} or
  2786. C{r.Requires('soname: /path/to/libfoo.so', 'filterexp')} depending on
  2787. whether the library is in a system library directory or not. (It should be
  2788. the same as how the soname dependency is expressed by the providing
  2789. package.)
  2790. For unusual cases where a system library is not listed in C{ld.so.conf}
  2791. but is instead found through a search through special subdirectories with
  2792. architecture-specific names (such as C{i686} and C{tls}), you can pass in
  2793. a string or list of strings specifying the directory or list of
  2794. directories. with C{r.Requires(sonameSubtrees='/directoryname')}
  2795. or C{r.Requires(sonameSubtrees=['/list', '/of', '/dirs'])}
  2796. Note: These are B{not} regular expressions. They will have macro
  2797. expansion expansion performed on them.
  2798. For unusual cases where Conary finds a false or misleading dependency,
  2799. or in which you need to override a true dependency, you can specify
  2800. C{r.Requires(exceptDeps='regexp')} to override all dependencies matching
  2801. a regular expression, C{r.Requires(exceptDeps=('filterexp', 'regexp'))}
  2802. to override dependencies matching a regular expression only for files
  2803. matching filterexp, or
  2804. C{r.Requires(exceptDeps=(('filterexp', 'regexp'), ...))} to specify
  2805. multiple overrides.
  2806. EXAMPLES
  2807. ========
  2808. C{r.Requires('mailbase:runtime', '%(sbindir)s/sendmail')}
  2809. Demonstrates using C{r.Requires} to specify a manual requirement of the
  2810. file C{%(sbindir)s/sendmail} to the C{:runtime} component of package
  2811. C{mailbase}.
  2812. C{r.Requires('file: %(sbindir)s/sendmail', '%(datadir)s/squirrelmail/index.php')}
  2813. Specifies that conary should require the file C{%(sbindir)s/sendmail} to
  2814. be present when trying to install C{%(datadir)s/squirrelmail/index.php}.
  2815. C{r.Requires('soname: %(libdir)/kde3/kgreet_classic.so', '%(bindir)/kdm')}
  2816. Demonstrates using C{r.Requires} to specify a manual soname requirement
  2817. of the file C{%(bindir)s/kdm} to the soname
  2818. C{%(libdir)/kde3/kgreet_classic.so}.
  2819. C{r.Requires(exceptions='/usr/share/vim/.*/doc/')}
  2820. Demonstrates using C{r.Requires} to specify that files in the
  2821. subdirectory C{/usr/share/vim/.*/doc} are excepted from being marked as
  2822. requirements.
  2823. C{r.Requires(exceptDeps='trove:$trovename')}
  2824. Uses C{r.Requires} to specify that the trove C{trovename} is excluded
  2825. from the dependencies for the package.
  2826. """
  2827. bucket = policy.PACKAGE_CREATION
  2828. requires = (
  2829. ('PackageSpec', policy.REQUIRED_PRIOR),
  2830. ('SharedLibrary', policy.REQUIRED_PRIOR),
  2831. # Requires depends on ELF dep path discovery previously done in Provides
  2832. ('Provides', policy.REQUIRED_PRIOR),
  2833. )
  2834. filetree = policy.PACKAGE
  2835. invariantexceptions = (
  2836. '%(docdir)s/',
  2837. )
  2838. dbDepCacheClass = _DatabaseDepCache
  2839. def __init__(self, *args, **keywords):
  2840. _dependency.__init__(self, *args, **keywords)
  2841. self.bootstrapPythonFlags = set()
  2842. self.bootstrapSysPath = []
  2843. self.bootstrapPerlIncPath = []
  2844. self.pythonFlagNamespace = None
  2845. self.sonameSubtrees = set()
  2846. self._privateDepMap = {}
  2847. self.rpathFixup = []
  2848. self.exceptDeps = []
  2849. self.sysPath = None
  2850. self.monodisPath = None
  2851. self.rubyInterpreter = None
  2852. self.rubyVersion = None
  2853. self.rubyInvocation = None
  2854. self.rubyLoadPath = None
  2855. self.perlReqs = None
  2856. self.perlPath = None
  2857. self.perlIncArgs = None
  2858. self._CILPolicyProvides = {}
  2859. self.pythonSysPathMap = {}
  2860. self.pythonModuleFinderMap = {}
  2861. self.troveDeps = {}
  2862. policy.Policy.__init__(self, *args, **keywords)
  2863. self.depCache = self.dbDepCacheClass(self._getDb())
  2864. ISD = deps.InstructionSetDependency
  2865. TISD = deps.TargetInstructionSetDependency
  2866. instructionDeps = list(self.recipe._buildFlavor.iterDepsByClass(ISD))
  2867. instructionDeps += list(self.recipe._buildFlavor.iterDepsByClass(TISD))
  2868. self.allowableIsnSets = [ x.name for x in instructionDeps ]
  2869. def updateArgs(self, *args, **keywords):
  2870. # _privateDepMap is used only for Provides to talk to Requires
  2871. privateDepMap = keywords.pop('_privateDepMap', None)
  2872. if privateDepMap:
  2873. self._privateDepMap.update([privateDepMap])
  2874. sonameSubtrees = keywords.pop('sonameSubtrees', None)
  2875. if sonameSubtrees:
  2876. if type(sonameSubtrees) in (list, tuple):
  2877. self.sonameSubtrees.update(set(sonameSubtrees))
  2878. else:
  2879. self.sonameSubtrees.add(sonameSubtrees)
  2880. bootstrapPythonFlags = keywords.pop('bootstrapPythonFlags', None)
  2881. if bootstrapPythonFlags:
  2882. if type(bootstrapPythonFlags) in (list, tuple):
  2883. self.bootstrapPythonFlags.update(set(bootstrapPythonFlags))
  2884. else:
  2885. self.bootstrapPythonFlags.add(bootstrapPythonFlags)
  2886. # pass full set to Provides to share the exact same data
  2887. self.recipe.Provides(
  2888. _bootstrapPythonFlags=self.bootstrapPythonFlags)
  2889. bootstrapSysPath = keywords.pop('bootstrapSysPath', None)
  2890. if bootstrapSysPath:
  2891. if type(bootstrapSysPath) in (list, tuple):
  2892. self.bootstrapSysPath.extend(bootstrapSysPath)
  2893. else:
  2894. self.error('bootstrapSysPath must be list or tuple')
  2895. # pass full set to Provides to share the exact same data
  2896. self.recipe.Provides(
  2897. _bootstrapSysPath=self.bootstrapSysPath)
  2898. pythonFlagNamespace = keywords.pop('pythonFlagNamespace', None)
  2899. if pythonFlagNamespace is not None:
  2900. self.pythonFlagNamespace = pythonFlagNamespace
  2901. self.recipe.Provides(_pythonFlagNamespace=pythonFlagNamespace)
  2902. bootstrapPerlIncPath = keywords.pop('bootstrapPerlIncPath', None)
  2903. if bootstrapPerlIncPath:
  2904. if type(bootstrapPerlIncPath) in (list, tuple):
  2905. self.bootstrapPerlIncPath.extend(bootstrapPerlIncPath)
  2906. else:
  2907. self.error('bootstrapPerlIncPath must be list or tuple')
  2908. # pass full set to Provides to share the exact same data
  2909. self.recipe.Provides(
  2910. _bootstrapPerlIncPath=self.bootstrapPerlIncPath)
  2911. _CILPolicyProvides = keywords.pop('_CILPolicyProvides', None)
  2912. if _CILPolicyProvides:
  2913. self._CILPolicyProvides.update(_CILPolicyProvides)
  2914. rpath = keywords.pop('rpath', None)
  2915. if rpath:
  2916. if type(rpath) is str:
  2917. rpath = ('.*', rpath)
  2918. assert(type(rpath) == tuple)
  2919. self.rpathFixup.append(rpath)
  2920. exceptDeps = keywords.pop('exceptDeps', None)
  2921. if exceptDeps:
  2922. if type(exceptDeps) is str:
  2923. exceptDeps = ('.*', exceptDeps)
  2924. assert(type(exceptDeps) == tuple)
  2925. if type(exceptDeps[0]) is tuple:
  2926. self.exceptDeps.extend(exceptDeps)
  2927. else:
  2928. self.exceptDeps.append(exceptDeps)
  2929. if not hasattr(self.recipe, '_reqExceptDeps'):
  2930. self.recipe._reqExceptDeps = []
  2931. self.recipe._reqExceptDeps.extend(self.exceptDeps)
  2932. # Filter out trove deps that are not associated with a file.
  2933. if len(args) >= 2:
  2934. troves = []
  2935. component = re.compile('^[-a-zA-Z0-9]*:[a-zA-Z]+$')
  2936. for arg in args[1:]:
  2937. arg = arg % self.recipe.macros
  2938. # Make sure arg looks like a component
  2939. if not component.match(arg):
  2940. break
  2941. troves.append(arg.lstrip(':'))
  2942. else:
  2943. self.troveDeps[args[0]] = troves
  2944. args = ()
  2945. _dependency.updateArgs(self, *args, **keywords)
  2946. _addInfo.updateArgs(self, *args, **keywords)
  2947. def preProcess(self):
  2948. macros = self.macros
  2949. self.systemLibPaths = set(os.path.normpath(x % macros)
  2950. for x in self.sonameSubtrees)
  2951. self.bootstrapPythonFlags = set(x % macros
  2952. for x in self.bootstrapPythonFlags)
  2953. self.bootstrapSysPath = [x % macros for x in self.bootstrapSysPath]
  2954. if self.pythonFlagNamespace is not None:
  2955. self.pythonFlagNamespace = self.pythonFlagNamespace % macros
  2956. self.bootstrapPerlIncPath = [x % macros for x in self.bootstrapPerlIncPath]
  2957. # anything that any buildreqs have caused to go into ld.so.conf
  2958. # or ld.so.conf.d/*.conf is a system library by definition,
  2959. # but only look at paths, not (for example) "include" lines
  2960. self.systemLibPaths |= set(os.path.normpath(x.strip())
  2961. for x in file('/etc/ld.so.conf').readlines()
  2962. if x.startswith('/'))
  2963. for fileName in fixedglob.glob('/etc/ld.so.conf.d/*.conf'):
  2964. self.systemLibPaths |= set(os.path.normpath(x.strip())
  2965. for x in file(fileName).readlines()
  2966. if x.startswith('/'))
  2967. self.rpathFixup = [(filter.Filter(x, macros), y % macros)
  2968. for x, y in self.rpathFixup]
  2969. exceptDeps = []
  2970. for fE, rE in self.exceptDeps:
  2971. try:
  2972. exceptDeps.append((filter.Filter(fE, macros), re.compile(rE % macros)))
  2973. except sre_constants.error, e:
  2974. self.error('Bad regular expression %s for file spec %s: %s', rE, fE, e)
  2975. self.exceptDeps= exceptDeps
  2976. _dependency.preProcess(self)
  2977. def postProcess(self):
  2978. self._delPythonRequiresModuleFinder()
  2979. components = {}
  2980. for comp in self.recipe.autopkg.getComponents():
  2981. components[comp.getName()] = comp
  2982. shortName = comp.getName().split(':')[1]
  2983. # Mark copmonent names with duplicates
  2984. if shortName in components:
  2985. components[shortName] = None
  2986. else:
  2987. components[shortName] = comp
  2988. # r.Requires('foo:runtime', 'msi')
  2989. # r.Requires('foo:runtime', ':msi')
  2990. # r.Requires('foo:runtime', 'bar:msi')
  2991. depClass = deps.TroveDependencies
  2992. for info, troves in self.troveDeps.iteritems():
  2993. # Sanity check inputs.
  2994. if ':' not in info:
  2995. self.error('package dependency %s not allowed', info)
  2996. return
  2997. for trove in troves:
  2998. if trove not in components:
  2999. self.error('no component named %s', trove)
  3000. return
  3001. if components[trove] is None:
  3002. self.error('specified component name matches multiple '
  3003. 'components %s', trove)
  3004. return
  3005. # Add the trove dependency.
  3006. dep = deps.Dependency(info)
  3007. for trove in troves:
  3008. components[trove].requires.addDep(depClass, dep)
  3009. def doFile(self, path):
  3010. pkgs = self.recipe.autopkg.findComponents(path)
  3011. if not pkgs:
  3012. return
  3013. pkgFiles = [(x, x.getFile(path)) for x in pkgs]
  3014. # this file object used only for tests, not for doing packaging
  3015. f = pkgFiles[0][1]
  3016. macros = self.recipe.macros
  3017. fullpath = macros.destdir + path
  3018. m = self.recipe.magic[path]
  3019. if self._isELF(m, 'requires'):
  3020. isnset = m.contents['isnset']
  3021. if isnset in self.allowableIsnSets:
  3022. # only add requirements for architectures
  3023. # that we are actually building for (this may include
  3024. # major and minor architectures)
  3025. self._addELFRequirements(path, m, pkgFiles)
  3026. # now go through explicit requirements
  3027. for info in self.included:
  3028. for filt in self.included[info]:
  3029. if filt.match(path):
  3030. self._markManualRequirement(info, path, pkgFiles, m)
  3031. # now check for automatic dependencies besides ELF
  3032. if f.inode.perms() & 0111 and m and m.name == 'script':
  3033. interp = m.contents['interpreter']
  3034. if interp.strip().startswith('/') and self._checkInclusion(interp,
  3035. path):
  3036. # no interpreter string warning is in BadInterpreterPaths
  3037. if not (os.path.exists(interp) or
  3038. os.path.exists(macros.destdir+interp)):
  3039. # this interpreter not on system, warn
  3040. # cannot be an error to prevent buildReq loops
  3041. self.warn('interpreter "%s" (referenced in %s) missing',
  3042. interp, path)
  3043. # N.B. no special handling for /{,usr/}bin/env here;
  3044. # if there has been an exception to
  3045. # NormalizeInterpreterPaths, then it is a
  3046. # real dependency on the env binary
  3047. self._addRequirement(path, interp, [], pkgFiles,
  3048. deps.FileDependencies)
  3049. if (f.inode.perms() & 0111 and m and m.name == 'script' and
  3050. os.path.basename(m.contents['interpreter']).startswith('python')):
  3051. self._addPythonRequirements(path, fullpath, pkgFiles, script=True)
  3052. elif self._isPython(path):
  3053. self._addPythonRequirements(path, fullpath, pkgFiles, script=False)
  3054. if (f.inode.perms() & 0111 and m and m.name == 'script' and
  3055. os.path.basename(m.contents['interpreter']).startswith('ruby')):
  3056. self._addRubyRequirements(path, fullpath, pkgFiles, script=True)
  3057. elif '/ruby/' in path:
  3058. self._addRubyRequirements(path, fullpath, pkgFiles, script=False)
  3059. if self._isCIL(m):
  3060. if not self.monodisPath:
  3061. self.monodisPath = self._getmonodis(macros, path)
  3062. if not self.monodisPath:
  3063. return
  3064. p = util.popen('%s --assemblyref %s' %(
  3065. self.monodisPath, fullpath))
  3066. for line in [ x.strip() for x in p.readlines() ]:
  3067. if ': Version=' in line:
  3068. ver = line.split('=')[1]
  3069. elif 'Name=' in line:
  3070. name = line.split('=')[1]
  3071. self._addRequirement(path, name, [ver], pkgFiles,
  3072. deps.CILDependencies)
  3073. p.close()
  3074. elif self.CILPolicyRE.match(path):
  3075. name, ver = self._CILPolicyProvides[path]
  3076. self._addRequirement(path, name, [ver], pkgFiles, deps.CILDependencies)
  3077. if self._isJava(m, 'requires'):
  3078. self._addJavaRequirements(path, m, pkgFiles)
  3079. db = self._getDb()
  3080. if self._isPerl(path, m, f):
  3081. perlReqs = self._getPerlReqs(path, fullpath)
  3082. for req in perlReqs:
  3083. thisReq = deps.parseDep('perl: ' + req)
  3084. if db.getTrovesWithProvides([thisReq]) or [
  3085. x for x in self.recipe.autopkg.getComponents()
  3086. if x.provides.satisfies(thisReq)]:
  3087. self._addRequirement(path, req, [], pkgFiles,
  3088. deps.PerlDependencies)
  3089. self.whiteOut(path, pkgFiles)
  3090. self.unionDeps(path, pkgFiles)
  3091. def _addJavaRequirements(self, path, m, pkgFiles):
  3092. if not hasattr(self.recipe, '_internalJavaDepMap'):
  3093. self.recipe._internalJavaDepMap = {}
  3094. fileDeps = self.recipe._internalJavaDepMap.get(path, {})
  3095. reqs = set()
  3096. for fpath, (fprov, freq) in fileDeps.items():
  3097. if freq is not None:
  3098. reqs.update(freq)
  3099. for req in reqs:
  3100. self._addRequirement(path, req, [], pkgFiles,
  3101. deps.JavaDependencies)
  3102. def whiteOut(self, path, pkgFiles):
  3103. # remove intentionally discarded dependencies
  3104. for pkg, _ in pkgFiles:
  3105. if self.exceptDeps and path in pkg.requiresMap:
  3106. depSet = deps.DependencySet()
  3107. for depClass, dep in pkg.requiresMap[path].iterDeps():
  3108. for filt, exceptRe in self.exceptDeps:
  3109. if filt.match(path):
  3110. matchName = '%s: %s' %(depClass.tagName, str(dep))
  3111. if exceptRe.match(matchName):
  3112. # found one to not copy
  3113. dep = None
  3114. break
  3115. if dep is not None:
  3116. depSet.addDep(depClass, dep)
  3117. pkg.requiresMap[path] = depSet
  3118. def unionDeps(self, path, pkgFiles):
  3119. # finally, package the dependencies up
  3120. for pkg, f in pkgFiles:
  3121. if path in pkg.requiresMap:
  3122. # files should not require items they provide directly. CNY-2177
  3123. f.requires.set(pkg.requiresMap[path] - f.provides())
  3124. pkg.requires.union(f.requires())
  3125. def _addELFRequirements(self, path, m, pkgFiles):
  3126. """
  3127. Add ELF and abi dependencies, including paths when not shlibs
  3128. """
  3129. def appendUnique(ul, items):
  3130. for item in items:
  3131. if item not in ul:
  3132. ul.append(item)
  3133. def _canonicalRPATH(rpath, glob=False):
  3134. # normalize all elements of RPATH
  3135. l = [ util.normpath(x) for x in rpath.split(':') ] # CNY-3425
  3136. # prune system paths and relative paths from RPATH
  3137. l = [ x for x in l
  3138. if x not in self.systemLibPaths and x.startswith('/') ]
  3139. if glob:
  3140. destdir = self.macros.destdir
  3141. dlen = len(destdir)
  3142. gl = []
  3143. for item in l:
  3144. # prefer destdir elements
  3145. paths = util.braceGlob(destdir + item)
  3146. paths = [ os.path.normpath(x[dlen:]) for x in paths ]
  3147. appendUnique(gl, paths)
  3148. # then look on system
  3149. paths = util.braceGlob(item)
  3150. paths = [ os.path.normpath(x) for x in paths ]
  3151. appendUnique(gl, paths)
  3152. l = gl
  3153. return l
  3154. rpathList = []
  3155. def _findSonameInRpath(soname):
  3156. for rpath in rpathList:
  3157. destpath = '/'.join((self.macros.destdir, rpath, soname))
  3158. if os.path.exists(destpath):
  3159. return rpath
  3160. destpath = '/'.join((rpath, soname))
  3161. if os.path.exists(destpath):
  3162. return rpath
  3163. # didn't find anything
  3164. return None
  3165. # fixup should come first so that its path elements can override
  3166. # the included RPATH if necessary
  3167. if self.rpathFixup:
  3168. for f, rpath in self.rpathFixup:
  3169. if f.match(path):
  3170. # synthetic RPATH items are globbed
  3171. rpathList = _canonicalRPATH(rpath, glob=True)
  3172. break
  3173. if m and 'RPATH' in m.contents and m.contents['RPATH']:
  3174. rpathList += _canonicalRPATH(m.contents['RPATH'])
  3175. depSet = self._createELFDepSet(m, m.contents['requires'],
  3176. libPathMap=self._privateDepMap,
  3177. getRPATH=_findSonameInRpath,
  3178. path=path, isProvides=False)
  3179. for pkg, _ in pkgFiles:
  3180. self._addDepSetToMap(path, pkg.requiresMap, depSet)
  3181. def _getPythonRequiresSysPath(self, pathName):
  3182. # Generate the correct sys.path for finding the required modules.
  3183. # we use the built in site.py to generate a sys.path for the
  3184. # current system and another one where destdir is the root.
  3185. # note the below code is similar to code in Provides,
  3186. # but it creates an ordered path list with and without destdir prefix,
  3187. # while provides only needs a complete list without destdir prefix.
  3188. # Returns tuple:
  3189. # (sysPath, pythonModuleFinder, pythonVersion)
  3190. pythonPath, bootstrapPython = self._getPython(self.macros, pathName)
  3191. if not pythonPath:
  3192. return (None, None, None)
  3193. if pythonPath in self.pythonSysPathMap:
  3194. return self.pythonSysPathMap[pythonPath]
  3195. destdir = self.macros.destdir
  3196. libdir = self.macros.libdir
  3197. pythonVersion = self._getPythonVersion(pythonPath, destdir, libdir)
  3198. # Start with paths inside the destdir so that imports within a package
  3199. # are discovered correctly.
  3200. systemPaths = self._getPythonSysPath(pythonPath, destdir, libdir,
  3201. useDestDir=True)
  3202. # Now add paths from the system (or bootstrap python)
  3203. systemPaths += self._getPythonSysPath(pythonPath, destdir, libdir,
  3204. useDestDir=False)
  3205. if not bootstrapPython:
  3206. # update pythonTroveFlagCache to require correct flags
  3207. self._getPythonTroveFlags(pythonPath)
  3208. # Keep original order for use with the module finder.
  3209. sysPathForModuleFinder = list(systemPaths)
  3210. # Strip destdir and sort in descending order for converting paths to
  3211. # qualified python module names.
  3212. sysPath = sorted(set(self._stripDestDir(systemPaths, destdir)),
  3213. reverse=True)
  3214. # load module finder after sys.path is restored
  3215. # in case delayed importer is installed.
  3216. pythonModuleFinder = self._getPythonRequiresModuleFinder(
  3217. pythonPath, destdir, libdir, sysPathForModuleFinder,
  3218. bootstrapPython)
  3219. self.pythonSysPathMap[pythonPath] = (
  3220. sysPath, pythonModuleFinder, pythonVersion)
  3221. return self.pythonSysPathMap[pythonPath]
  3222. def _getPythonRequiresModuleFinder(self, pythonPath, destdir, libdir, sysPath, bootstrapPython):
  3223. if self.recipe.isCrossCompiling():
  3224. return None
  3225. if pythonPath not in self.pythonModuleFinderMap:
  3226. try:
  3227. self.pythonModuleFinderMap[pythonPath] = pydeps.moduleFinderProxy(pythonPath, destdir, libdir, sysPath, self.error)
  3228. except pydeps.ModuleFinderInitializationError, e:
  3229. if bootstrapPython:
  3230. # another case, like isCrossCompiling, where we cannot
  3231. # run pythonPath -- ModuleFinderInitializationError
  3232. # is raised before looking at any path, so should
  3233. # be consistent for any pythonPath
  3234. self.pythonModuleFinderMap[pythonPath] = None
  3235. else:
  3236. raise
  3237. return self.pythonModuleFinderMap[pythonPath]
  3238. def _delPythonRequiresModuleFinder(self):
  3239. for finder in self.pythonModuleFinderMap.values():
  3240. if finder is not None:
  3241. finder.close()
  3242. def _addPythonRequirements(self, path, fullpath, pkgFiles, script=False):
  3243. destdir = self.recipe.macros.destdir
  3244. destDirLen = len(destdir)
  3245. (sysPath, pythonModuleFinder, pythonVersion
  3246. )= self._getPythonRequiresSysPath(path)
  3247. if not sysPath:
  3248. # Probably a bad interpreter path
  3249. return
  3250. if not pythonModuleFinder:
  3251. # We cannot (reliably) determine runtime python requirements
  3252. # in the cross-compile case, so don't even try (for
  3253. # consistency).
  3254. return
  3255. try:
  3256. if script:
  3257. pythonModuleFinder.run_script(fullpath)
  3258. else:
  3259. pythonModuleFinder.load_file(fullpath)
  3260. except:
  3261. # not a valid python file
  3262. self.info('File %s is not a valid python file', path)
  3263. return
  3264. for depPath in pythonModuleFinder.getDepsForPath(fullpath):
  3265. if not depPath:
  3266. continue
  3267. flags = None
  3268. if depPath.startswith('///invalid'):
  3269. # same as exception handling above
  3270. self.info('File %s is not a valid python file', path)
  3271. return
  3272. absPath = None
  3273. if depPath.startswith(destdir):
  3274. depPath = depPath[destDirLen:]
  3275. flags = self._getPythonFlagsFromPath(depPath)
  3276. # The file providing this dependency is part of this package.
  3277. absPath = depPath
  3278. for sysPathEntry in sysPath:
  3279. if depPath.startswith(sysPathEntry):
  3280. newDepPath = depPath[len(sysPathEntry)+1:]
  3281. if newDepPath not in ('__init__', '__init__.py'):
  3282. # we don't allow bare __init__'s as dependencies.
  3283. # hopefully we'll find this at deeper level in
  3284. # in the sysPath
  3285. if flags is None:
  3286. # this is provided by the system, so we have
  3287. # to see with which flags it is provided with
  3288. flags = self._getPythonFlags(depPath,
  3289. self.bootstrapPythonFlags)
  3290. depPath = newDepPath
  3291. break
  3292. if depPath.startswith('/'):
  3293. # a python file not found in sys.path will not have been
  3294. # provided, so we must not depend on it either
  3295. return
  3296. if not (depPath.endswith('.py') or depPath.endswith('.pyc') or
  3297. depPath.endswith('.so')):
  3298. # Not something we provide, so not something we can
  3299. # require either. Drop it and go on. We have seen
  3300. # this when a script in /usr/bin has ended up in the
  3301. # requires list.
  3302. continue
  3303. if depPath.endswith('module.so'):
  3304. # Strip 'module.so' from the end, make it a candidate
  3305. cands = [ depPath[:-9] + '.so', depPath ]
  3306. cands = [ self._normalizePythonDep(x) for x in cands ]
  3307. if absPath:
  3308. depName = self._checkPackagePythonDeps(pkgFiles, absPath,
  3309. cands, flags)
  3310. else:
  3311. depName = self._checkSystemPythonDeps(cands, flags)
  3312. else:
  3313. depName = self._normalizePythonDep(depPath)
  3314. if depName == '__future__':
  3315. continue
  3316. self._addRequirement(path, depName, flags, pkgFiles,
  3317. deps.PythonDependencies)
  3318. def _checkPackagePythonDeps(self, pkgFiles, depPath, depNames, flags):
  3319. # Try to match depNames against all current packages
  3320. # Use the last value in depNames as the fault value
  3321. assert depNames, "No dependencies passed"
  3322. for pkg, _ in pkgFiles:
  3323. if depPath in pkg:
  3324. fileProvides = pkg[depPath][1].provides()
  3325. if flags:
  3326. flags = [ (x, deps.FLAG_SENSE_REQUIRED) for x in flags ]
  3327. # Walk the depNames list in order, pick the first dependency
  3328. # available.
  3329. for dp in depNames:
  3330. depSet = deps.DependencySet()
  3331. depSet.addDep(deps.PythonDependencies,
  3332. deps.Dependency(dp, flags))
  3333. if fileProvides.intersection(depSet):
  3334. # this dep is provided
  3335. return dp
  3336. # If we got here, the file doesn't provide this dep. Return the last
  3337. # candidate and hope for the best
  3338. return depNames[-1]
  3339. def _checkSystemPythonDeps(self, depNames, flags):
  3340. if flags:
  3341. flags = [ (x, deps.FLAG_SENSE_REQUIRED) for x in flags ]
  3342. for dp in depNames:
  3343. depSet = deps.DependencySet()
  3344. depSet.addDep(deps.PythonDependencies, deps.Dependency(dp, flags))
  3345. troves = self.depCache.getProvides([depSet])
  3346. if troves:
  3347. return dp
  3348. return depNames[-1]
  3349. def _normalizePythonDep(self, depName):
  3350. # remove extension
  3351. depName = depName.rsplit('.', 1)[0]
  3352. depName = depName.replace('/', '.')
  3353. depName = depName.replace('.__init__', '')
  3354. return depName
  3355. def _addRubyRequirements(self, path, fullpath, pkgFiles, script=False):
  3356. macros = self.recipe.macros
  3357. destdir = macros.destdir
  3358. destDirLen = len(destdir)
  3359. if self.rubyInterpreter is None:
  3360. self.rubyInterpreter, bootstrap = self._getRuby(macros, path)
  3361. if not self.rubyInterpreter:
  3362. return
  3363. self.rubyInvocation, self.rubyLoadPath = self._getRubyLoadPath(
  3364. macros, self.rubyInterpreter, bootstrap)
  3365. self.rubyVersion = self._getRubyVersion(macros)
  3366. elif self.rubyInterpreter is False:
  3367. return
  3368. if not script:
  3369. if not util.isregular(fullpath) or os.path.islink(fullpath):
  3370. return
  3371. foundInLoadPath = False
  3372. for pathElement in self.rubyLoadPath:
  3373. if path.startswith(pathElement):
  3374. foundInLoadPath = True
  3375. break
  3376. if not foundInLoadPath:
  3377. return
  3378. # This is a very limited hack, but will work for the 90% case
  3379. # better parsing may be written later
  3380. # Note that we only honor "require" at the beginning of
  3381. # the line and only requirements enclosed in single quotes
  3382. # to avoid conditional requirements and requirements that
  3383. # do any sort of substitution. Because most ruby packages
  3384. # contain multiple ruby modules, getting 90% of the ruby
  3385. # dependencies will find most of the required packages in
  3386. # practice
  3387. depEntries = [x.strip() for x in file(fullpath)
  3388. if x.startswith('require')]
  3389. depEntries = (x.split() for x in depEntries)
  3390. depEntries = (x[1].strip("\"'") for x in depEntries
  3391. if len(x) == 2 and x[1].startswith("'") and
  3392. x[1].endswith("'"))
  3393. depEntries = set(depEntries)
  3394. # I know of no way to ask ruby to report deps from scripts
  3395. # Unfortunately, so far it seems that there are too many
  3396. # Ruby modules which have code that runs in the body; this
  3397. # code runs slowly, has not been useful in practice for
  3398. # filtering out bogus dependencies, and has been hanging
  3399. # and causing other unintended side effects from modules
  3400. # that have code in the main body.
  3401. #if not script:
  3402. # depClosure = util.popen(r'''%s -e "require '%s'; puts $\""'''
  3403. # %(self.rubyInvocation%macros, fullpath)).readlines()
  3404. # depClosure = set([x.split('.')[0] for x in depClosure])
  3405. # # remove any entries from the guessed immediate requirements
  3406. # # that are not in the closure
  3407. # depEntries = set(x for x in depEntries if x in depClosure)
  3408. def _getDepEntryPath(depEntry):
  3409. for prefix in (destdir, ''):
  3410. for pathElement in self.rubyLoadPath:
  3411. for suffix in ('.rb', '.so'):
  3412. candidate = util.joinPaths(pathElement, depEntry+suffix)
  3413. if util.exists(prefix+candidate):
  3414. return candidate
  3415. return None
  3416. for depEntry in depEntries:
  3417. depEntryPath = _getDepEntryPath(depEntry)
  3418. if depEntryPath is None:
  3419. continue
  3420. if depEntryPath.startswith(destdir):
  3421. depPath = depEntryPath[destDirLen:]
  3422. else:
  3423. depPath = depEntryPath
  3424. flags = self._getRubyFlagsFromPath(depPath, self.rubyVersion)
  3425. self._addRequirement(path, depEntry, flags, pkgFiles,
  3426. deps.RubyDependencies)
  3427. def _fetchPerl(self):
  3428. """
  3429. Cache the perl path and @INC path with -I%(destdir)s prepended to
  3430. each element if necessary
  3431. """
  3432. if self.perlPath is not None:
  3433. return
  3434. macros = self.recipe.macros
  3435. self.perlPath, perlIncPath, perlDestInc = self._getperl(macros, self.recipe)
  3436. if perlDestInc:
  3437. self.perlIncArgs = perlDestInc
  3438. else:
  3439. self.perlIncArgs = ' '.join('-I'+x for x in perlIncPath)
  3440. def _getPerlReqs(self, path, fullpath):
  3441. if self.perlReqs is None:
  3442. self._fetchPerl()
  3443. if not self.perlPath:
  3444. # no perl == bootstrap, but print warning
  3445. self.info('Unable to find perl interpreter,'
  3446. ' disabling perl: requirements')
  3447. self.perlReqs = False
  3448. return []
  3449. # get the base directory where conary lives. In a checked
  3450. # out version, this would be .../conary/conary/build/package.py
  3451. # chop off the last 3 directories to find where
  3452. # .../conary/Scandeps and .../conary/scripts/perlreqs.pl live
  3453. basedir = '/'.join(sys.modules[__name__].__file__.split('/')[:-3])
  3454. scandeps = '/'.join((basedir, 'conary/ScanDeps'))
  3455. if (os.path.exists(scandeps) and
  3456. os.path.exists('%s/scripts/perlreqs.pl' % basedir)):
  3457. perlreqs = '%s/scripts/perlreqs.pl' % basedir
  3458. else:
  3459. # we assume that conary is installed in
  3460. # $prefix/$libdir/python?.?/site-packages. Use this
  3461. # assumption to find the prefix for
  3462. # /usr/lib/conary and /usr/libexec/conary
  3463. regexp = re.compile(r'(.*)/lib(64){0,1}/python[1-9].[0-9]/site-packages')
  3464. match = regexp.match(basedir)
  3465. if not match:
  3466. # our regexp didn't work. fall back to hardcoded
  3467. # paths
  3468. prefix = '/usr'
  3469. else:
  3470. prefix = match.group(1)
  3471. # ScanDeps is not architecture specific
  3472. scandeps = '%s/lib/conary/ScanDeps' %prefix
  3473. if not os.path.exists(scandeps):
  3474. # but it might have been moved to lib64 for multilib
  3475. scandeps = '%s/lib64/conary/ScanDeps' %prefix
  3476. perlreqs = '%s/libexec/conary/perlreqs.pl' %prefix
  3477. self.perlReqs = '%s -I%s %s %s' %(
  3478. self.perlPath, scandeps, self.perlIncArgs, perlreqs)
  3479. if self.perlReqs is False:
  3480. return []
  3481. cwd = os.getcwd()
  3482. os.chdir(os.path.dirname(fullpath))
  3483. try:
  3484. p = os.popen('%s %s' %(self.perlReqs, fullpath))
  3485. finally:
  3486. try:
  3487. os.chdir(cwd)
  3488. except:
  3489. pass
  3490. reqlist = [x.strip().split('//') for x in p.readlines()]
  3491. # make sure that the command completed successfully
  3492. rc = p.close()
  3493. if rc:
  3494. # make sure that perl didn't blow up
  3495. assert(os.WIFEXITED(rc))
  3496. # Apparantly ScanDeps could not handle this input
  3497. return []
  3498. # we care only about modules right now
  3499. # throwing away the filenames for now, but we might choose
  3500. # to change that later
  3501. reqlist = [x[2] for x in reqlist if x[0] == 'module']
  3502. # foo/bar/baz.pm -> foo::bar::baz
  3503. reqlist = ['::'.join(x.split('/')).rsplit('.', 1)[0] for x in reqlist]
  3504. return reqlist
  3505. def _markManualRequirement(self, info, path, pkgFiles, m):
  3506. flags = []
  3507. if self._checkInclusion(info, path):
  3508. if info[0] == '/':
  3509. depClass = deps.FileDependencies
  3510. elif info.startswith('file:') and info[5:].strip()[0] == '/':
  3511. info = info[5:].strip()
  3512. depClass = deps.FileDependencies
  3513. elif info.startswith('soname:'):
  3514. if not m or m.name != 'ELF':
  3515. # only an ELF file can have a soname requirement
  3516. return
  3517. # we need to synthesize a dependency that encodes the
  3518. # same ABI as this binary
  3519. depClass = deps.SonameDependencies
  3520. for depType, dep, f in m.contents['requires']:
  3521. if depType == 'abi':
  3522. flags = tuple(x == 'Linux' and 'SysV' or x
  3523. for x in f) # CNY-3604
  3524. info = '%s/%s' %(dep, info.split(None, 1)[1])
  3525. info = os.path.normpath(info)
  3526. else: # by process of elimination, must be a trove
  3527. if info.startswith('group-'):
  3528. self.error('group dependency %s not allowed', info)
  3529. return
  3530. if info.startswith('fileset-'):
  3531. self.error('fileset dependency %s not allowed', info)
  3532. return
  3533. if ':' not in info:
  3534. self.error('package dependency %s not allowed', info)
  3535. return
  3536. depClass = deps.TroveDependencies
  3537. self._addRequirement(path, info, flags, pkgFiles, depClass)
  3538. def _checkInclusion(self, info, path):
  3539. if info in self.excluded:
  3540. for filt in self.excluded[info]:
  3541. # exception handling is per-requirement,
  3542. # so handled specially
  3543. if filt.match(path):
  3544. self.info('ignoring requirement match for %s: %s',
  3545. path, info)
  3546. return False
  3547. return True
  3548. def _addRequirement(self, path, info, flags, pkgFiles, depClass):
  3549. if depClass == deps.FileDependencies:
  3550. pathMap = self.recipe.autopkg.pathMap
  3551. componentMap = self.recipe.autopkg.componentMap
  3552. if (info in pathMap and not
  3553. componentMap[info][info][1].flags.isPathDependencyTarget()):
  3554. # if a package requires a file, includes that file,
  3555. # and does not provide that file, it should error out
  3556. self.error('%s requires %s, which is included but not'
  3557. ' provided; use'
  3558. " r.Provides('file', '%s')", path, info, info)
  3559. return
  3560. # in some cases, we get literal "(flags)" from the recipe
  3561. if '(' in info:
  3562. flagindex = info.index('(')
  3563. flags = set(info[flagindex+1:-1].split() + list(flags))
  3564. info = info.split('(')[0]
  3565. # CNY-3443
  3566. if depClass in self.removeFlagsByDependencyClassMap:
  3567. flags = set(flags)
  3568. for ignoreItem in self.removeFlagsByDependencyClassMap[depClass]:
  3569. if isinstance(ignoreItem, set):
  3570. ignoreFlags = ignoreItem
  3571. else:
  3572. ignoreFlags = set(f for f in flags if ignoreItem.match(f))
  3573. flags -= ignoreFlags
  3574. if flags:
  3575. flags = [ (x, deps.FLAG_SENSE_REQUIRED) for x in flags ]
  3576. for pkg, _ in pkgFiles:
  3577. # we may need to create a few more DependencySets.
  3578. if path not in pkg.requiresMap:
  3579. pkg.requiresMap[path] = deps.DependencySet()
  3580. pkg.requiresMap[path].addDep(depClass, deps.Dependency(info, flags))
  3581. class _basePluggableRequires(Requires):
  3582. """
  3583. Base class for pluggable Requires policies.
  3584. """
  3585. # This set of policies get executed before the Requires policy,
  3586. # and inherits the Requires' ordering constraints
  3587. requires = list(Requires.requires) + [
  3588. ('Requires', policy.REQUIRED_SUBSEQUENT),
  3589. ]
  3590. def preProcess(self):
  3591. # We want to inherit the exceptions from the Requires class, so we
  3592. # need to peek into the Required policy object. We can still pass
  3593. # explicit exceptions into the pluggable sub-policies, and they will
  3594. # only apply to the sub-policy.
  3595. exceptions = self.recipe._policyMap['Requires'].exceptions
  3596. if exceptions:
  3597. Requires.updateArgs(self, exceptions=exceptions,
  3598. allowUnusedFilters = True)
  3599. Requires.preProcess(self)
  3600. def reportErrors(self, *args, **kwargs):
  3601. return self.recipe._policyMap['Requires'].reportErrors(*args, **kwargs)
  3602. def error(self, *args, **kwargs):
  3603. return self.recipe._policyMap['Requires'].error(*args, **kwargs)
  3604. def warn(self, *args, **kwargs):
  3605. return self.recipe._policyMap['Requires'].warn(*args, **kwargs)
  3606. def info(self, *args, **kwargs):
  3607. return self.recipe._policyMap['Requires'].info(*args, **kwargs)
  3608. def _addClassName(self, *args, **kwargs):
  3609. return self.recipe._policyMap['Requires']._addClassName(*args, **kwargs)
  3610. def doFile(self, path):
  3611. pkgs = self.recipe.autopkg.findComponents(path)
  3612. if not pkgs:
  3613. return
  3614. pkgFiles = [(x, x.getFile(path)) for x in pkgs]
  3615. macros = self.recipe.macros
  3616. fullpath = macros.destdir + path
  3617. self.addPluggableRequirements(path, fullpath, pkgFiles, macros)
  3618. self.whiteOut(path, pkgFiles)
  3619. self.unionDeps(path, pkgFiles)
  3620. def addPluggableRequirements(self, path, fullpath, pkgFiles, macros):
  3621. """Override in subclasses"""
  3622. pass
  3623. class RemoveSelfProvidedRequires(policy.Policy):
  3624. """
  3625. This policy is used to remove component requirements when they are provided
  3626. by the component itself.
  3627. Do not call it directly; it is for internal use only.
  3628. """
  3629. bucket = policy.PACKAGE_CREATION
  3630. requires = (
  3631. ('Requires', policy.REQUIRED_PRIOR),
  3632. )
  3633. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  3634. def do(self):
  3635. if use.Use.bootstrap._get():
  3636. return
  3637. for comp in self.recipe.autopkg.getComponents():
  3638. comp.requires -= comp.provides
  3639. class Flavor(policy.Policy):
  3640. """
  3641. NAME
  3642. ====
  3643. B{C{r.Flavor()}} - Controls the Flavor mechanism
  3644. SYNOPSIS
  3645. ========
  3646. C{r.Flavor([I{filterexp}] | [I{exceptions=filterexp}])}
  3647. DESCRIPTION
  3648. ===========
  3649. The C{r.Flavor} policy marks files with the appropriate Flavor.
  3650. To except a file's flavor from being marked, use:
  3651. C{r.Flavor(exceptions='I{filterexp}')}.
  3652. EXAMPLES
  3653. ========
  3654. C{r.Flavor(exceptions='%(crossprefix)s/lib/gcc-lib/.*')}
  3655. Files in the directory C{%(crossprefix)s/lib/gcc-lib} are being excepted
  3656. from having their Flavor marked, because they are not flavored for
  3657. the system on which the trove is being installed.
  3658. """
  3659. bucket = policy.PACKAGE_CREATION
  3660. requires = (
  3661. ('PackageSpec', policy.REQUIRED_PRIOR),
  3662. ('Requires', policy.REQUIRED_PRIOR),
  3663. # For example: :lib component contains only a single packaged empty
  3664. # directory, which must be artificially flavored for multilib
  3665. ('ExcludeDirectories', policy.REQUIRED_PRIOR),
  3666. )
  3667. filetree = policy.PACKAGE
  3668. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  3669. def preProcess(self):
  3670. self.libRe = re.compile(
  3671. '^(%(libdir)s'
  3672. '|/%(lib)s'
  3673. '|%(x11prefix)s/%(lib)s'
  3674. '|%(krbprefix)s/%(lib)s)(/|$)' %self.recipe.macros)
  3675. self.libReException = re.compile('^/usr/(lib|%(lib)s)/(python|ruby).*$')
  3676. self.baseIsnset = use.Arch.getCurrentArch()._name
  3677. self.baseArchFlavor = use.Arch.getCurrentArch()._toDependency()
  3678. self.archFlavor = use.createFlavor(None, use.Arch._iterUsed())
  3679. self.packageFlavor = deps.Flavor()
  3680. self.troveMarked = False
  3681. self.componentMap = self.recipe.autopkg.componentMap
  3682. ISD = deps.InstructionSetDependency
  3683. TISD = deps.TargetInstructionSetDependency
  3684. instructionDeps = list(self.recipe._buildFlavor.iterDepsByClass(ISD))
  3685. instructionDeps += list(self.recipe._buildFlavor.iterDepsByClass(TISD))
  3686. self.allowableIsnSets = [ x.name for x in instructionDeps ]
  3687. def postProcess(self):
  3688. # If this is a Windows package, include the flavor from the windows
  3689. # helper.
  3690. if (self._getTarget() == TARGET_WINDOWS and
  3691. hasattr(self.recipe, 'winHelper')):
  3692. flavorStr = self.recipe.winHelper.flavor
  3693. if flavorStr:
  3694. self.packageFlavor.union(deps.parseFlavor(flavorStr))
  3695. # all troves need to share the same flavor so that we can
  3696. # distinguish them later
  3697. for pkg in self.recipe.autopkg.components.values():
  3698. pkg.flavor.union(self.packageFlavor)
  3699. def hasLibInPath(self, path):
  3700. return self.libRe.match(path) and not self.libReException.match(path)
  3701. def hasLibInDependencyFlag(self, path, f):
  3702. for depType in (deps.PythonDependencies, deps.RubyDependencies):
  3703. for dep in ([x for x in f.requires.deps.iterDepsByClass(depType)] +
  3704. [x for x in f.provides.deps.iterDepsByClass(depType)]):
  3705. flagNames = [x[0] for x in dep.getFlags()[0]]
  3706. flagNames = [x for x in flagNames if x.startswith('lib')]
  3707. if flagNames:
  3708. return True
  3709. return False
  3710. def doFile(self, path):
  3711. autopkg = self.recipe.autopkg
  3712. pkg = autopkg.findComponent(path)
  3713. if pkg is None:
  3714. return
  3715. f = pkg.getFile(path)
  3716. m = self.recipe.magic[path]
  3717. if m and m.name == 'ELF' and 'isnset' in m.contents:
  3718. isnset = m.contents['isnset']
  3719. elif self.hasLibInPath(path) or self.hasLibInDependencyFlag(path, f):
  3720. # all possible paths in a %(lib)s-derived path get default
  3721. # instruction set assigned if they don't have one already
  3722. if f.hasContents:
  3723. isnset = self.baseIsnset
  3724. else:
  3725. # this file can't be marked by arch, but the troves
  3726. # and package must be. (e.g. symlinks and empty directories)
  3727. # we don't need to union in the base arch flavor more
  3728. # than once.
  3729. if self.troveMarked:
  3730. return
  3731. self.packageFlavor.union(self.baseArchFlavor)
  3732. self.troveMarked = True
  3733. return
  3734. else:
  3735. return
  3736. flv = deps.Flavor()
  3737. flv.addDep(deps.InstructionSetDependency, deps.Dependency(isnset, []))
  3738. # get the Arch.* dependencies
  3739. # set the flavor for the file to match that discovered in the
  3740. # magic - but do not let that propagate up to the flavor of
  3741. # the package - instead the package will have the flavor that
  3742. # it was cooked with. This is to avoid unnecessary or extra files
  3743. # causing the entire package from being flavored inappropriately.
  3744. # Such flavoring requires a bunch of Flavor exclusions to fix.
  3745. # Note that we need to set all shared paths between containers
  3746. # to share flavors and ensure that fileIds are the same
  3747. for pkg in autopkg.findComponents(path):
  3748. f = pkg.getFile(path)
  3749. f.flavor.set(flv)
  3750. # get the Arch.* dependencies
  3751. flv.union(self.archFlavor)
  3752. if isnset in self.allowableIsnSets:
  3753. self.packageFlavor.union(flv)
  3754. class _ProcessInfoPackage(policy.UserGroupBasePolicy):
  3755. bucket = policy.PACKAGE_CREATION
  3756. requires = (
  3757. ('PackageSpec', policy.REQUIRED_PRIOR),
  3758. ('ComponentSpec', policy.REQUIRED_PRIOR),
  3759. ('Provides', policy.CONDITIONAL_PRIOR),
  3760. ('Requires', policy.CONDITIONAL_PRIOR),
  3761. ('Config', policy.CONDITIONAL_PRIOR),
  3762. ('InitialContents', policy.CONDITIONAL_PRIOR)
  3763. )
  3764. def preProcess(self):
  3765. if self.exceptions:
  3766. self.error('%s does not honor exceptions' % self.__class__.__name__)
  3767. self.exceptions = None
  3768. if self.inclusions:
  3769. self.inclusions = None
  3770. def doFile(self, path):
  3771. expectedName = 'info-%s:%s' % (os.path.basename(path), self.component)
  3772. comp = self.recipe.autopkg.componentMap[path]
  3773. compName = comp.name
  3774. if not isinstance(comp.getFile(path), files.RegularFile):
  3775. self.error("Only regular files may appear in '%s'" % expectedName)
  3776. return
  3777. if len(comp) > 1:
  3778. badPaths = [x for x in comp if x != path]
  3779. self.error("The following files are not allowed in '%s': '%s'" % \
  3780. (compName, "', '".join(badPaths)))
  3781. else:
  3782. fileObj = comp[path][1]
  3783. for tag in fileObj.tags():
  3784. self.error("TagSpec '%s' is not allowed for %s" % \
  3785. (tag, expectedName))
  3786. fileObj.tags.set('%s-info' % self.component)
  3787. fileObj.flags.isTransient(True)
  3788. self.parseError = False
  3789. self.addProvides(path)
  3790. if not self.parseError:
  3791. self.addRequires(path)
  3792. def parseInfoFile(self, path):
  3793. infoname = "info-%s:%s" % (os.path.basename(path), self.component)
  3794. data = {}
  3795. try:
  3796. data = dict([x.strip().split('=', 1) \
  3797. for x in open(path).readlines()])
  3798. extraKeys = set(data.keys()).difference(self.legalKeys)
  3799. if extraKeys:
  3800. for key in extraKeys:
  3801. self.error("%s is not is not a valid value for %s" % \
  3802. (key, infoname))
  3803. self.parseError = True
  3804. except ValueError:
  3805. self.error("Unable to parse info file for '%s'" % infoname)
  3806. self.parseError = True
  3807. return data
  3808. def addProvides(self, path):
  3809. realpath, fileObj = self.recipe.autopkg.findComponent(path)[path]
  3810. data = self.parseInfoFile(realpath)
  3811. pkg = self.recipe.autopkg.componentMap[path]
  3812. infoname = os.path.basename(path)
  3813. if path in pkg.providesMap:
  3814. # only deps related to userinfo/troveinfo are allowed
  3815. self.error("Illegal provision for 'info-%s:%s': '%s'" % \
  3816. (infoname, self.component, str(pkg.providesMap[path])))
  3817. pkg.providesMap[path] = deps.DependencySet()
  3818. depSet = self.getProvides(infoname, data)
  3819. fileObj.provides.set(depSet)
  3820. pkg.providesMap[path].union(depSet)
  3821. pkg.provides.union(depSet)
  3822. def addRequires(self, path):
  3823. realpath, fileObj = self.recipe.autopkg.findComponent(path)[path]
  3824. data = self.parseInfoFile(realpath)
  3825. pkg = self.recipe.autopkg.componentMap[path]
  3826. infoname = os.path.basename(path)
  3827. if path in pkg.requiresMap:
  3828. # only deps related to userinfo/troveinfo are allowed
  3829. self.error("Illegal requirement on 'info-%s:%s': '%s'" % \
  3830. (infoname, self.component, str(pkg.requiresMap[path])))
  3831. pkg.requiresMap[path] = deps.DependencySet()
  3832. depSet = self.getRequires(infoname, data)
  3833. fileObj.requires.set(depSet)
  3834. pkg.requiresMap[path].union(depSet)
  3835. pkg.requires.union(depSet)
  3836. class ProcessUserInfoPackage(_ProcessInfoPackage):
  3837. """
  3838. NAME
  3839. ====
  3840. B{C{r.ProcessUserInfoPackage()}} - Set dependencies and tags for User
  3841. info packages
  3842. SYNOPSIS
  3843. ========
  3844. C{r.ProcessUserInfoPackage()}
  3845. DESCRIPTION
  3846. ===========
  3847. The C{r.ProcessUserInfoPackage} policy automatically sets up provides
  3848. and requries, as well as tags for user info files create by the
  3849. C{r.User} build action.
  3850. This policy is not intended to be invoked from recipes. Do not use it.
  3851. """
  3852. invariantsubtrees = ['%(userinfodir)s']
  3853. component = 'user'
  3854. legalKeys = ['PREFERRED_UID', 'GROUP', 'GROUPID', 'HOMEDIR', 'COMMENT',
  3855. 'SHELL', 'SUPPLEMENTAL', 'PASSWORD']
  3856. def parseInfoFile(self, path):
  3857. if self.recipe._getCapsulePathsForFile(path):
  3858. return {}
  3859. data = _ProcessInfoPackage.parseInfoFile(self, path)
  3860. if data:
  3861. supplemental = data.get('SUPPLEMENTAL')
  3862. if supplemental is not None:
  3863. data['SUPPLEMENTAL'] = supplemental.split(',')
  3864. return data
  3865. def getProvides(self, infoname, data):
  3866. depSet = deps.DependencySet()
  3867. groupname = data.get('GROUP', infoname)
  3868. depSet.addDep(deps.UserInfoDependencies,
  3869. deps.Dependency(infoname, []))
  3870. if self.recipe._provideGroup.get(infoname, True):
  3871. depSet.addDep(deps.GroupInfoDependencies,
  3872. deps.Dependency(groupname, []))
  3873. return depSet
  3874. def getRequires(self, infoname, data):
  3875. groupname = data.get('GROUP', infoname)
  3876. supp = data.get('SUPPLEMENTAL', [])
  3877. depSet = deps.DependencySet()
  3878. for grpDep in supp:
  3879. depSet.addDep(deps.GroupInfoDependencies,
  3880. deps.Dependency(grpDep, []))
  3881. if not self.recipe._provideGroup.get(infoname):
  3882. depSet.addDep(deps.GroupInfoDependencies,
  3883. deps.Dependency(groupname, []))
  3884. return depSet
  3885. class ProcessGroupInfoPackage(_ProcessInfoPackage):
  3886. """
  3887. NAME
  3888. ====
  3889. B{C{r.ProcessGroupInfoPackage()}} - Set dependencies and tags for Group
  3890. info packages
  3891. SYNOPSIS
  3892. ========
  3893. C{r.ProcessGroupInfoPackage()}
  3894. DESCRIPTION
  3895. ===========
  3896. The C{r.ProcessGroupInfoPackage} policy automatically sets up provides
  3897. and requries, as well as tags for group info files create by the
  3898. C{r.Group} and C{r.SupplementalGroup} build actions.
  3899. This policy is not intended to be invoked from recipes. Do not use it.
  3900. """
  3901. invariantsubtrees = ['%(groupinfodir)s']
  3902. component = 'group'
  3903. legalKeys = ['PREFERRED_GID', 'USER']
  3904. def getProvides(self, groupname, data):
  3905. depSet = deps.DependencySet()
  3906. depSet.addDep(deps.GroupInfoDependencies,
  3907. deps.Dependency(groupname, []))
  3908. return depSet
  3909. def getRequires(self, groupname, data):
  3910. infoname = data.get('USER')
  3911. depSet = deps.DependencySet()
  3912. if infoname:
  3913. depSet.addDep(deps.UserInfoDependencies,
  3914. deps.Dependency(infoname, []))
  3915. return depSet
  3916. class reportExcessBuildRequires(policy.Policy):
  3917. """
  3918. NAME
  3919. ====
  3920. B{C{r.reportExcessBuildRequires()}} - suggest items to remove from C{buildRequires} list
  3921. SYNOPSIS
  3922. ========
  3923. C{r.reportExcessBuildRequires('required:component')}
  3924. C{r.reportExcessBuildRequires(['list:of', 'required:components'])}
  3925. DESCRIPTION
  3926. ===========
  3927. The C{r.reportExcessBuildRequires()} policy is used to report
  3928. together all suggestions for possible items to remove from the
  3929. C{buildRequires} list.
  3930. The suggestions provided by this policy are build requirements
  3931. listed in the recipe's C{buildRequires} list for which Conary
  3932. has not specifically discovered a need. Build requirement
  3933. discovery is not perfect, which means that even though this
  3934. policy prints a warning that a build requirement might not be
  3935. necessary, Conary does not know that it is definitely not needed.
  3936. These are only hints. If you are not sure whether a component
  3937. should be removed from the C{buildRequires} list, it is safer
  3938. to leave it in the list. This is because an extra component
  3939. in the C{buildRequires} list is very unlikely to cause trouble,
  3940. but a truly missing component causes failure (by definition).
  3941. Because dependencies on C{:runtime} components are the least
  3942. likely dependencies to be discovered automatically, this policy
  3943. currently does not recommend removing any C{:runtime} components.
  3944. EXAMPLES
  3945. ========
  3946. This policy is normally called only internally by other Conary
  3947. policies. However, a recipe can report build requirements
  3948. that are known by the recipe maintainer to be required but
  3949. which Conary does not discover automatically by passing a
  3950. list of these components. For example, if this policy
  3951. says that C{foo:devel} and C{blah:perl} are possible extra
  3952. build requirements, but you know that they are required in
  3953. order to correctly build the included software, you can
  3954. turn off the warnings like this:
  3955. C{r.reportExcessBuildRequires(['foo:devel', 'blah:perl'])}
  3956. This will tell the C{reportExcessBuildRequires} policy that
  3957. C{foo:devel} and C{blah:perl} are known to be required to
  3958. build the package.
  3959. No regular expressions are honored.
  3960. """
  3961. bucket = policy.ERROR_REPORTING
  3962. processUnmodified = True
  3963. filetree = policy.NO_FILES
  3964. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  3965. def __init__(self, *args, **keywords):
  3966. self.found = set()
  3967. policy.Policy.__init__(self, *args, **keywords)
  3968. def updateArgs(self, *args, **keywords):
  3969. for arg in args:
  3970. if type(arg) in (list, tuple, set):
  3971. self.found.update(arg)
  3972. else:
  3973. self.found.add(arg)
  3974. def do(self):
  3975. # If absolutely no buildRequires were found automatically,
  3976. # assume that the buildRequires list has been carefully crafted
  3977. # for some reason that the buildRequires enforcement policy
  3978. # doesn't yet support, and don't warn that all of the listed
  3979. # buildRequires might be excessive.
  3980. if self.found and self.recipe._logFile:
  3981. r = self.recipe
  3982. def getReqNames(key):
  3983. return set(x.split('=')[0] for x in r._recipeRequirements[key])
  3984. recipeReqs = getReqNames('buildRequires')
  3985. superReqs = getReqNames('buildRequiresSuper')
  3986. foundPackages = set(x.split(':')[0] for x in self.found)
  3987. superClosure = r._getTransitiveDepClosure(superReqs)
  3988. foundClosure = r._getTransitiveDepClosure(self.found)
  3989. def removeCore(candidates):
  3990. # conary, python, and setup are always required; gcc
  3991. # is often an implicit requirement, and sqlite:lib is
  3992. # listed explicitly make bootstrapping easier
  3993. return set(x for x in candidates if
  3994. not x.startswith('conary')
  3995. and not x.startswith('python:')
  3996. and not x.startswith('gcc:')
  3997. and not x in ('libgcc:devellib',
  3998. 'setup:runtime',
  3999. 'sqlite:lib'))
  4000. def removeSome(candidates):
  4001. # at this point, we don't have good enough detection
  4002. # of :runtime in particular to recommend getting rid
  4003. # of it
  4004. return set(x for x in removeCore(candidates) if
  4005. not x.endswith(':runtime'))
  4006. def removeDupComponents(candidates):
  4007. # If any component is required, we don't really need
  4008. # to flag others as excessive in superclass excess
  4009. return set(x for x in candidates
  4010. if x.split(':')[0] not in foundPackages)
  4011. # for superclass reqs
  4012. excessSuperReqs = superReqs - foundClosure
  4013. if excessSuperReqs:
  4014. # note that as this is for debugging only, we do not
  4015. # remove runtime requirements
  4016. deDupedSuperReqs = sorted(list(
  4017. removeDupComponents(removeCore(excessSuperReqs))))
  4018. if deDupedSuperReqs:
  4019. self._reportExcessSuperclassBuildRequires(deDupedSuperReqs)
  4020. excessReqs = recipeReqs - self.found
  4021. redundantReqs = recipeReqs.intersection(superClosure)
  4022. if excessReqs or redundantReqs:
  4023. excessBuildRequires = sorted(list(
  4024. removeSome(excessReqs.union(redundantReqs))))
  4025. # all potential excess build requires might have
  4026. # been removed by removeSome
  4027. if excessBuildRequires:
  4028. self._reportExcessBuildRequires(excessBuildRequires)
  4029. def _reportExcessBuildRequires(self, reqList):
  4030. self.recipe._logFile.reportExcessBuildRequires(
  4031. sorted(list(reqList)))
  4032. def _reportExcessSuperclassBuildRequires(self, reqList):
  4033. self.recipe._logFile.reportExcessSuperclassBuildRequires(
  4034. sorted(list(reqList)))
  4035. class reportMissingBuildRequires(policy.Policy):
  4036. """
  4037. This policy is used to report together all suggestions for
  4038. additions to the C{buildRequires} list.
  4039. Do not call it directly; it is for internal use only.
  4040. """
  4041. bucket = policy.ERROR_REPORTING
  4042. processUnmodified = True
  4043. filetree = policy.NO_FILES
  4044. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  4045. def __init__(self, *args, **keywords):
  4046. self.errors = set()
  4047. policy.Policy.__init__(self, *args, **keywords)
  4048. def updateArgs(self, *args, **keywords):
  4049. for arg in args:
  4050. if type(arg) in (list, tuple, set):
  4051. self.errors.update(arg)
  4052. else:
  4053. self.errors.add(arg)
  4054. def do(self):
  4055. if self.errors and self.recipe._logFile:
  4056. self.recipe._logFile.reportMissingBuildRequires(
  4057. sorted(list(self.errors)))
  4058. class reportErrors(policy.Policy, policy.GroupPolicy):
  4059. """
  4060. This policy is used to report together all package errors.
  4061. Do not call it directly; it is for internal use only.
  4062. """
  4063. bucket = policy.ERROR_REPORTING
  4064. processUnmodified = True
  4065. filetree = policy.NO_FILES
  4066. groupError = False
  4067. supported_targets = (TARGET_LINUX, TARGET_WINDOWS)
  4068. def __init__(self, *args, **keywords):
  4069. self.errors = []
  4070. policy.Policy.__init__(self, *args, **keywords)
  4071. def updateArgs(self, *args, **keywords):
  4072. """
  4073. Called once, with printf-style arguments, for each warning.
  4074. """
  4075. self.errors.append(args[0] %tuple(args[1:]))
  4076. groupError = keywords.pop('groupError', None)
  4077. if groupError is not None:
  4078. self.groupError = groupError
  4079. def do(self):
  4080. if self.errors:
  4081. msg = self.groupError and 'Group' or 'Package'
  4082. raise policy.PolicyError, ('%s Policy errors found:\n%%s' % msg) \
  4083. % "\n".join(self.errors)
  4084. class _TroveScript(policy.PackagePolicy):
  4085. processUnmodified = False
  4086. keywords = { 'contents' : None }
  4087. _troveScriptName = None
  4088. def __init__(self, *args, **keywords):
  4089. policy.PackagePolicy.__init__(self, *args, **keywords)
  4090. def updateArgs(self, *args, **keywords):
  4091. if args:
  4092. troveNames = args
  4093. else:
  4094. troveNames = [ self.recipe.name ]
  4095. self.troveNames = troveNames
  4096. policy.PackagePolicy.updateArgs(self, **keywords)
  4097. def do(self):
  4098. if not self.contents:
  4099. return
  4100. # Build component map
  4101. availTroveNames = dict((x.name, None) for x in
  4102. self.recipe.autopkg.getComponents())
  4103. availTroveNames.update(self.recipe.packages)
  4104. troveNames = set(self.troveNames) & set(availTroveNames)
  4105. # We don't support compatibility classes for troves (yet)
  4106. self.recipe._addTroveScript(troveNames, self.contents,
  4107. self._troveScriptName, None)
  4108. class ScriptPreUpdate(_TroveScript):
  4109. _troveScriptName = 'preUpdate'
  4110. class ScriptPostUpdate(_TroveScript):
  4111. _troveScriptName = 'postUpdate'
  4112. class ScriptPreInstall(_TroveScript):
  4113. _troveScriptName = 'preInstall'
  4114. class ScriptPostInstall(_TroveScript):
  4115. _troveScriptName = 'postInstall'
  4116. class ScriptPreErase(_TroveScript):
  4117. _troveScriptName = 'preErase'
  4118. class ScriptPostErase(_TroveScript):
  4119. _troveScriptName = 'postErase'
  4120. class ScriptPreRollback(_TroveScript):
  4121. _troveScriptName = 'preRollback'
  4122. class ScriptPostRollback(_TroveScript):
  4123. _troveScriptName = 'postRollback'