PageRenderTime 41ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 1ms

/conary/build/packagepolicy.py

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