buildhistory.bbclass 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  1. #
  2. # Records history of build output in order to detect regressions
  3. #
  4. # Based in part on testlab.bbclass and packagehistory.bbclass
  5. #
  6. # Copyright (C) 2013 Intel Corporation
  7. # Copyright (C) 2007-2011 Koen Kooi <koen@openembedded.org>
  8. #
  9. BUILDHISTORY_FEATURES ?= "image package sdk"
  10. BUILDHISTORY_DIR ?= "${TOPDIR}/buildhistory"
  11. BUILDHISTORY_DIR_IMAGE = "${BUILDHISTORY_DIR}/images/${MACHINE_ARCH}/${TCLIBC}/${IMAGE_BASENAME}"
  12. BUILDHISTORY_DIR_PACKAGE = "${BUILDHISTORY_DIR}/packages/${MULTIMACH_TARGET_SYS}/${PN}"
  13. BUILDHISTORY_DIR_SDK = "${BUILDHISTORY_DIR}/sdk/${SDK_NAME}/${IMAGE_BASENAME}"
  14. BUILDHISTORY_IMAGE_FILES ?= "/etc/passwd /etc/group"
  15. BUILDHISTORY_COMMIT ?= "0"
  16. BUILDHISTORY_COMMIT_AUTHOR ?= "buildhistory <buildhistory@${DISTRO}>"
  17. BUILDHISTORY_PUSH_REPO ?= ""
  18. BUILDHISTORY_CHECKVERBACKWARDS ?= "1"
  19. # Must inherit package first before changing PACKAGEFUNCS
  20. inherit package
  21. PACKAGEFUNCS += "buildhistory_emit_pkghistory"
  22. # We don't want to force a rerun of do_package for everything
  23. # if the buildhistory_emit_pkghistory function or any of the
  24. # variables it refers to changes
  25. do_package[vardepsexclude] += "buildhistory_emit_pkghistory"
  26. #
  27. # Called during do_package to write out metadata about this package
  28. # for comparision when writing future packages
  29. #
  30. python buildhistory_emit_pkghistory() {
  31. import re
  32. if not "package" in (d.getVar('BUILDHISTORY_FEATURES', True) or "").split():
  33. return 0
  34. pkghistdir = d.getVar('BUILDHISTORY_DIR_PACKAGE', True)
  35. class RecipeInfo:
  36. def __init__(self, name):
  37. self.name = name
  38. self.pe = "0"
  39. self.pv = "0"
  40. self.pr = "r0"
  41. self.depends = ""
  42. self.packages = ""
  43. self.bbfile = ""
  44. self.src_uri = ""
  45. self.srcrev = ""
  46. self.srcrev_autorev = ""
  47. class PackageInfo:
  48. def __init__(self, name):
  49. self.name = name
  50. self.pe = "0"
  51. self.pv = "0"
  52. self.pr = "r0"
  53. # pkg/pkge/pkgv/pkgr should be empty because we want to be able to default them
  54. self.pkg = ""
  55. self.pkge = ""
  56. self.pkgv = ""
  57. self.pkgr = ""
  58. self.size = 0
  59. self.depends = ""
  60. self.rprovides = ""
  61. self.rdepends = ""
  62. self.rrecommends = ""
  63. self.rsuggests = ""
  64. self.rreplaces = ""
  65. self.rconflicts = ""
  66. self.files = ""
  67. self.filelist = ""
  68. # Variables that need to be written to their own separate file
  69. self.filevars = dict.fromkeys(['pkg_preinst', 'pkg_postinst', 'pkg_prerm', 'pkg_postrm'])
  70. # Should check PACKAGES here to see if anything removed
  71. def getpkgvar(pkg, var):
  72. val = bb.data.getVar('%s_%s' % (var, pkg), d, 1)
  73. if val:
  74. return val
  75. val = bb.data.getVar('%s' % (var), d, 1)
  76. return val
  77. def readPackageInfo(pkg, histfile):
  78. pkginfo = PackageInfo(pkg)
  79. with open(histfile, "r") as f:
  80. for line in f:
  81. lns = line.split('=')
  82. name = lns[0].strip()
  83. value = lns[1].strip(" \t\r\n").strip('"')
  84. if name == "PE":
  85. pkginfo.pe = value
  86. elif name == "PV":
  87. pkginfo.pv = value
  88. elif name == "PR":
  89. pkginfo.pr = value
  90. elif name == "PKG":
  91. pkginfo.pkg = value
  92. elif name == "PKGE":
  93. pkginfo.pkge = value
  94. elif name == "PKGV":
  95. pkginfo.pkgv = value
  96. elif name == "PKGR":
  97. pkginfo.pkgr = value
  98. elif name == "RPROVIDES":
  99. pkginfo.rprovides = value
  100. elif name == "RDEPENDS":
  101. pkginfo.rdepends = value
  102. elif name == "RRECOMMENDS":
  103. pkginfo.rrecommends = value
  104. elif name == "RSUGGESTS":
  105. pkginfo.rsuggests = value
  106. elif name == "RREPLACES":
  107. pkginfo.rreplaces = value
  108. elif name == "RCONFLICTS":
  109. pkginfo.rconflicts = value
  110. elif name == "PKGSIZE":
  111. pkginfo.size = long(value)
  112. elif name == "FILES":
  113. pkginfo.files = value
  114. elif name == "FILELIST":
  115. pkginfo.filelist = value
  116. # Apply defaults
  117. if not pkginfo.pkg:
  118. pkginfo.pkg = pkginfo.name
  119. if not pkginfo.pkge:
  120. pkginfo.pkge = pkginfo.pe
  121. if not pkginfo.pkgv:
  122. pkginfo.pkgv = pkginfo.pv
  123. if not pkginfo.pkgr:
  124. pkginfo.pkgr = pkginfo.pr
  125. return pkginfo
  126. def getlastpkgversion(pkg):
  127. try:
  128. histfile = os.path.join(pkghistdir, pkg, "latest")
  129. return readPackageInfo(pkg, histfile)
  130. except EnvironmentError:
  131. return None
  132. def sortpkglist(string):
  133. pkgiter = re.finditer(r'[a-zA-Z0-9.+-]+( \([><=]+ [^ )]+\))?', string, 0)
  134. pkglist = [p.group(0) for p in pkgiter]
  135. pkglist.sort()
  136. return ' '.join(pkglist)
  137. def sortlist(string):
  138. items = string.split(' ')
  139. items.sort()
  140. return ' '.join(items)
  141. pn = d.getVar('PN', True)
  142. pe = d.getVar('PE', True) or "0"
  143. pv = d.getVar('PV', True)
  144. pr = d.getVar('PR', True)
  145. bbfile = d.getVar('BB_FILENAME', True)
  146. src_uri = d.getVar('SRC_URI', True)
  147. srcrev = d.getVar('SRCREV', True)
  148. srcrev_autorev = 'yes' if d.getVar('SRCREV', False) == 'AUTOINC' else 'no'
  149. packages = squashspaces(d.getVar('PACKAGES', True))
  150. packagelist = packages.split()
  151. if not os.path.exists(pkghistdir):
  152. os.makedirs(pkghistdir)
  153. else:
  154. # Remove files for packages that no longer exist
  155. for item in os.listdir(pkghistdir):
  156. if item != "latest" and item != "latest_srcrev":
  157. if item not in packagelist:
  158. subdir = os.path.join(pkghistdir, item)
  159. for subfile in os.listdir(subdir):
  160. os.unlink(os.path.join(subdir, subfile))
  161. os.rmdir(subdir)
  162. rcpinfo = RecipeInfo(pn)
  163. rcpinfo.pe = pe
  164. rcpinfo.pv = pv
  165. rcpinfo.pr = pr
  166. rcpinfo.depends = sortlist(squashspaces(d.getVar('DEPENDS', True) or ""))
  167. rcpinfo.bbfile = bbfile
  168. rcpinfo.src_uri = src_uri
  169. rcpinfo.srcrev = srcrev
  170. rcpinfo.srcrev_autorev = srcrev_autorev
  171. rcpinfo.packages = packages
  172. write_recipehistory(rcpinfo, d)
  173. pkgdest = d.getVar('PKGDEST', True)
  174. for pkg in packagelist:
  175. pkge = getpkgvar(pkg, 'PKGE') or "0"
  176. pkgv = getpkgvar(pkg, 'PKGV')
  177. pkgr = getpkgvar(pkg, 'PKGR')
  178. #
  179. # Find out what the last version was
  180. # Make sure the version did not decrease
  181. #
  182. if d.getVar("BUILDHISTORY_CHECKVERBACKWARDS", True) == "1":
  183. lastversion = getlastpkgversion(pkg)
  184. if lastversion:
  185. last_pkge = lastversion.pkge
  186. last_pkgv = lastversion.pkgv
  187. last_pkgr = lastversion.pkgr
  188. r = bb.utils.vercmp((pkge, pkgv, pkgr), (last_pkge, last_pkgv, last_pkgr))
  189. if r < 0:
  190. bb.error("Package version for package %s went backwards which would break package feeds from (%s:%s-%s to %s:%s-%s)" % (pkg, last_pkge, last_pkgv, last_pkgr, pkge, pkgv, pkgr))
  191. pkginfo = PackageInfo(pkg)
  192. # Apparently the version can be different on a per-package basis (see Python)
  193. pkginfo.pe = getpkgvar(pkg, 'PE') or "0"
  194. pkginfo.pv = getpkgvar(pkg, 'PV')
  195. pkginfo.pr = getpkgvar(pkg, 'PR')
  196. pkginfo.pkg = getpkgvar(pkg, 'PKG') or pkg
  197. pkginfo.pkge = pkge
  198. pkginfo.pkgv = pkgv
  199. pkginfo.pkgr = pkgr
  200. pkginfo.rprovides = sortpkglist(squashspaces(getpkgvar(pkg, 'RPROVIDES') or ""))
  201. pkginfo.rdepends = sortpkglist(squashspaces(getpkgvar(pkg, 'RDEPENDS') or ""))
  202. pkginfo.rrecommends = sortpkglist(squashspaces(getpkgvar(pkg, 'RRECOMMENDS') or ""))
  203. pkginfo.rsuggests = sortpkglist(squashspaces(getpkgvar(pkg, 'RSUGGESTS') or ""))
  204. pkginfo.rreplaces = sortpkglist(squashspaces(getpkgvar(pkg, 'RREPLACES') or ""))
  205. pkginfo.rconflicts = sortpkglist(squashspaces(getpkgvar(pkg, 'RCONFLICTS') or ""))
  206. pkginfo.files = squashspaces(getpkgvar(pkg, 'FILES') or "")
  207. for filevar in pkginfo.filevars:
  208. pkginfo.filevars[filevar] = getpkgvar(pkg, filevar)
  209. # Gather information about packaged files
  210. pkgdestpkg = os.path.join(pkgdest, pkg)
  211. filelist = []
  212. pkginfo.size = 0
  213. for f in pkgfiles[pkg]:
  214. relpth = os.path.relpath(f, pkgdestpkg)
  215. fstat = os.lstat(f)
  216. pkginfo.size += fstat.st_size
  217. filelist.append(os.sep + relpth)
  218. filelist.sort()
  219. pkginfo.filelist = " ".join(filelist)
  220. write_pkghistory(pkginfo, d)
  221. }
  222. def write_recipehistory(rcpinfo, d):
  223. bb.debug(2, "Writing recipe history")
  224. pkghistdir = d.getVar('BUILDHISTORY_DIR_PACKAGE', True)
  225. infofile = os.path.join(pkghistdir, "latest")
  226. with open(infofile, "w") as f:
  227. if rcpinfo.pe != "0":
  228. f.write("PE = %s\n" % rcpinfo.pe)
  229. f.write("PV = %s\n" % rcpinfo.pv)
  230. f.write("PR = %s\n" % rcpinfo.pr)
  231. f.write("DEPENDS = %s\n" % rcpinfo.depends)
  232. f.write("PACKAGES = %s\n" % rcpinfo.packages)
  233. def write_pkghistory(pkginfo, d):
  234. bb.debug(2, "Writing package history for package %s" % pkginfo.name)
  235. pkghistdir = d.getVar('BUILDHISTORY_DIR_PACKAGE', True)
  236. pkgpath = os.path.join(pkghistdir, pkginfo.name)
  237. if not os.path.exists(pkgpath):
  238. os.makedirs(pkgpath)
  239. infofile = os.path.join(pkgpath, "latest")
  240. with open(infofile, "w") as f:
  241. if pkginfo.pe != "0":
  242. f.write("PE = %s\n" % pkginfo.pe)
  243. f.write("PV = %s\n" % pkginfo.pv)
  244. f.write("PR = %s\n" % pkginfo.pr)
  245. pkgvars = {}
  246. pkgvars['PKG'] = pkginfo.pkg if pkginfo.pkg != pkginfo.name else ''
  247. pkgvars['PKGE'] = pkginfo.pkge if pkginfo.pkge != pkginfo.pe else ''
  248. pkgvars['PKGV'] = pkginfo.pkgv if pkginfo.pkgv != pkginfo.pv else ''
  249. pkgvars['PKGR'] = pkginfo.pkgr if pkginfo.pkgr != pkginfo.pr else ''
  250. for pkgvar in pkgvars:
  251. val = pkgvars[pkgvar]
  252. if val:
  253. f.write("%s = %s\n" % (pkgvar, val))
  254. f.write("RPROVIDES = %s\n" % pkginfo.rprovides)
  255. f.write("RDEPENDS = %s\n" % pkginfo.rdepends)
  256. f.write("RRECOMMENDS = %s\n" % pkginfo.rrecommends)
  257. if pkginfo.rsuggests:
  258. f.write("RSUGGESTS = %s\n" % pkginfo.rsuggests)
  259. if pkginfo.rreplaces:
  260. f.write("RREPLACES = %s\n" % pkginfo.rreplaces)
  261. if pkginfo.rconflicts:
  262. f.write("RCONFLICTS = %s\n" % pkginfo.rconflicts)
  263. f.write("PKGSIZE = %d\n" % pkginfo.size)
  264. f.write("FILES = %s\n" % pkginfo.files)
  265. f.write("FILELIST = %s\n" % pkginfo.filelist)
  266. for filevar in pkginfo.filevars:
  267. filevarpath = os.path.join(pkgpath, "latest.%s" % filevar)
  268. val = pkginfo.filevars[filevar]
  269. if val:
  270. with open(filevarpath, "w") as f:
  271. f.write(val)
  272. else:
  273. if os.path.exists(filevarpath):
  274. os.unlink(filevarpath)
  275. buildhistory_get_installed() {
  276. mkdir -p $1
  277. # Get list of installed packages
  278. pkgcache="$1/installed-packages.tmp"
  279. list_installed_packages file | sort > $pkgcache
  280. cat $pkgcache | awk '{ print $1 }' > $1/installed-package-names.txt
  281. cat $pkgcache | awk '{ print $2 }' | xargs -n1 basename > $1/installed-packages.txt
  282. # Produce dependency graph
  283. # First, filter out characters that cause issues for dot
  284. rootfs_list_installed_depends | sed -e 's:-:_:g' -e 's:\.:_:g' -e 's:+::g' > $1/depends.tmp
  285. # Change delimiter from pipe to -> and set style for recommend lines
  286. sed -i -e 's:|: -> :' -e 's:\[REC\]:[style=dotted]:' -e 's:$:;:' $1/depends.tmp
  287. # Add header, sorted and de-duped contents and footer and then delete the temp file
  288. printf "digraph depends {\n node [shape=plaintext]\n" > $1/depends.dot
  289. cat $1/depends.tmp | sort | uniq >> $1/depends.dot
  290. echo "}" >> $1/depends.dot
  291. rm $1/depends.tmp
  292. # Produce installed package sizes list
  293. printf "" > $1/installed-package-sizes.tmp
  294. cat $pkgcache | while read pkg pkgfile
  295. do
  296. if [ -f $pkgfile ] ; then
  297. pkgsize=`du -k $pkgfile | head -n1 | awk '{ print $1 }'`
  298. echo $pkgsize $pkg >> $1/installed-package-sizes.tmp
  299. fi
  300. done
  301. cat $1/installed-package-sizes.tmp | sort -n -r | awk '{print $1 "\tKiB " $2}' > $1/installed-package-sizes.txt
  302. rm $1/installed-package-sizes.tmp
  303. # We're now done with the cache, delete it
  304. rm $pkgcache
  305. if [ "$2" != "sdk" ] ; then
  306. # Produce some cut-down graphs (for readability)
  307. grep -v kernel_image $1/depends.dot | grep -v kernel_2 | grep -v kernel_3 > $1/depends-nokernel.dot
  308. grep -v libc6 $1/depends-nokernel.dot | grep -v libgcc > $1/depends-nokernel-nolibc.dot
  309. grep -v update_ $1/depends-nokernel-nolibc.dot > $1/depends-nokernel-nolibc-noupdate.dot
  310. grep -v kernel_module $1/depends-nokernel-nolibc-noupdate.dot > $1/depends-nokernel-nolibc-noupdate-nomodules.dot
  311. fi
  312. # add complementary package information
  313. if [ -e ${WORKDIR}/complementary_pkgs.txt ]; then
  314. cp ${WORKDIR}/complementary_pkgs.txt $1
  315. fi
  316. }
  317. buildhistory_get_image_installed() {
  318. # Anything requiring the use of the packaging system should be done in here
  319. # in case the packaging files are going to be removed for this image
  320. if [ "${@base_contains('BUILDHISTORY_FEATURES', 'image', '1', '0', d)}" = "0" ] ; then
  321. return
  322. fi
  323. buildhistory_get_installed ${BUILDHISTORY_DIR_IMAGE}
  324. }
  325. buildhistory_get_sdk_installed() {
  326. # Anything requiring the use of the packaging system should be done in here
  327. # in case the packaging files are going to be removed for this SDK
  328. if [ "${@base_contains('BUILDHISTORY_FEATURES', 'sdk', '1', '0', d)}" = "0" ] ; then
  329. return
  330. fi
  331. buildhistory_get_installed ${BUILDHISTORY_DIR_SDK}/$1 sdk
  332. }
  333. buildhistory_list_files() {
  334. # List the files in the specified directory, but exclude date/time etc.
  335. # This awk script is somewhat messy, but handles where the size is not printed for device files under pseudo
  336. ( cd $1 && find . -ls | awk '{ if ( $7 ~ /[0-9]/ ) printf "%s %10-s %10-s %10s %s %s %s\n", $3, $5, $6, $7, $11, $12, $13 ; else printf "%s %10-s %10-s %10s %s %s %s\n", $3, $5, $6, 0, $10, $11, $12 }' | sort -k5 | sed 's/ *$//' > $2 )
  337. }
  338. buildhistory_get_imageinfo() {
  339. if [ "${@base_contains('BUILDHISTORY_FEATURES', 'image', '1', '0', d)}" = "0" ] ; then
  340. return
  341. fi
  342. buildhistory_list_files ${IMAGE_ROOTFS} ${BUILDHISTORY_DIR_IMAGE}/files-in-image.txt
  343. # Collect files requested in BUILDHISTORY_IMAGE_FILES
  344. rm -rf ${BUILDHISTORY_DIR_IMAGE}/image-files
  345. for f in ${BUILDHISTORY_IMAGE_FILES}; do
  346. if [ -f ${IMAGE_ROOTFS}/$f ] ; then
  347. mkdir -p ${BUILDHISTORY_DIR_IMAGE}/image-files/`dirname $f`
  348. cp ${IMAGE_ROOTFS}/$f ${BUILDHISTORY_DIR_IMAGE}/image-files/$f
  349. fi
  350. done
  351. # Record some machine-readable meta-information about the image
  352. printf "" > ${BUILDHISTORY_DIR_IMAGE}/image-info.txt
  353. cat >> ${BUILDHISTORY_DIR_IMAGE}/image-info.txt <<END
  354. ${@buildhistory_get_imagevars(d)}
  355. END
  356. imagesize=`du -ks ${IMAGE_ROOTFS} | awk '{ print $1 }'`
  357. echo "IMAGESIZE = $imagesize" >> ${BUILDHISTORY_DIR_IMAGE}/image-info.txt
  358. # Add some configuration information
  359. echo "${MACHINE}: ${IMAGE_BASENAME} configured for ${DISTRO} ${DISTRO_VERSION}" > ${BUILDHISTORY_DIR_IMAGE}/build-id
  360. cat >> ${BUILDHISTORY_DIR_IMAGE}/build-id <<END
  361. ${@buildhistory_get_layers(d)}
  362. END
  363. }
  364. buildhistory_get_sdkinfo() {
  365. if [ "${@base_contains('BUILDHISTORY_FEATURES', 'sdk', '1', '0', d)}" = "0" ] ; then
  366. return
  367. fi
  368. buildhistory_list_files ${SDK_OUTPUT} ${BUILDHISTORY_DIR_SDK}/files-in-sdk.txt
  369. # Record some machine-readable meta-information about the SDK
  370. printf "" > ${BUILDHISTORY_DIR_SDK}/sdk-info.txt
  371. cat >> ${BUILDHISTORY_DIR_SDK}/sdk-info.txt <<END
  372. ${@buildhistory_get_sdkvars(d)}
  373. END
  374. sdksize=`du -ks ${SDK_OUTPUT} | awk '{ print $1 }'`
  375. echo "SDKSIZE = $sdksize" >> ${BUILDHISTORY_DIR_SDK}/sdk-info.txt
  376. }
  377. # By prepending we get in before the removal of packaging files
  378. ROOTFS_POSTPROCESS_COMMAND =+ "buildhistory_get_image_installed ; "
  379. IMAGE_POSTPROCESS_COMMAND += " buildhistory_get_imageinfo ; "
  380. # We want these to be the last run so that we get called after complementary package installation
  381. POPULATE_SDK_POST_TARGET_COMMAND_append = "buildhistory_get_sdk_installed target ; "
  382. POPULATE_SDK_POST_HOST_COMMAND_append = "buildhistory_get_sdk_installed host ; "
  383. SDK_POSTPROCESS_COMMAND += "buildhistory_get_sdkinfo ; "
  384. def buildhistory_get_layers(d):
  385. layertext = "Configured metadata layers:\n%s\n" % '\n'.join(get_layers_branch_rev(d))
  386. return layertext
  387. def buildhistory_get_metadata_revs(d):
  388. # We want an easily machine-readable format here, so get_layers_branch_rev isn't quite what we want
  389. layers = (d.getVar("BBLAYERS", True) or "").split()
  390. medadata_revs = ["%-17s = %s:%s" % (os.path.basename(i), \
  391. base_get_metadata_git_branch(i, None).strip(), \
  392. base_get_metadata_git_revision(i, None)) \
  393. for i in layers]
  394. return '\n'.join(medadata_revs)
  395. def squashspaces(string):
  396. import re
  397. return re.sub("\s+", " ", string).strip()
  398. def outputvars(vars, listvars, d):
  399. vars = vars.split()
  400. listvars = listvars.split()
  401. ret = ""
  402. for var in vars:
  403. value = d.getVar(var, True) or ""
  404. if var in listvars:
  405. # Squash out spaces
  406. value = squashspaces(value)
  407. ret += "%s = %s\n" % (var, value)
  408. return ret.rstrip('\n')
  409. def buildhistory_get_imagevars(d):
  410. imagevars = "DISTRO DISTRO_VERSION USER_CLASSES IMAGE_CLASSES IMAGE_FEATURES IMAGE_LINGUAS IMAGE_INSTALL BAD_RECOMMENDATIONS ROOTFS_POSTPROCESS_COMMAND IMAGE_POSTPROCESS_COMMAND"
  411. listvars = "USER_CLASSES IMAGE_CLASSES IMAGE_FEATURES IMAGE_LINGUAS IMAGE_INSTALL BAD_RECOMMENDATIONS"
  412. return outputvars(imagevars, listvars, d)
  413. def buildhistory_get_sdkvars(d):
  414. sdkvars = "DISTRO DISTRO_VERSION SDK_NAME SDK_VERSION SDKMACHINE SDKIMAGE_FEATURES BAD_RECOMMENDATIONS"
  415. listvars = "SDKIMAGE_FEATURES BAD_RECOMMENDATIONS"
  416. return outputvars(sdkvars, listvars, d)
  417. buildhistory_commit() {
  418. if [ ! -d ${BUILDHISTORY_DIR} ] ; then
  419. # Code above that creates this dir never executed, so there can't be anything to commit
  420. return
  421. fi
  422. # Create a machine-readable list of metadata revisions for each layer
  423. cat > ${BUILDHISTORY_DIR}/metadata-revs <<END
  424. ${@buildhistory_get_metadata_revs(d)}
  425. END
  426. ( cd ${BUILDHISTORY_DIR}/
  427. # Initialise the repo if necessary
  428. if [ ! -d .git ] ; then
  429. git init -q
  430. fi
  431. # Check if there are new/changed files to commit (other than metadata-revs)
  432. repostatus=`git status --porcelain | grep -v " metadata-revs$"`
  433. HOSTNAME=`hostname 2>/dev/null || echo unknown`
  434. if [ "$repostatus" != "" ] ; then
  435. git add -A .
  436. # porcelain output looks like "?? packages/foo/bar"
  437. # Ensure we commit metadata-revs with the first commit
  438. for entry in `echo "$repostatus" | awk '{print $2}' | awk -F/ '{print $1}' | sort | uniq` ; do
  439. git commit $entry metadata-revs -m "$entry: Build ${BUILDNAME} of ${DISTRO} ${DISTRO_VERSION} for machine ${MACHINE} on $HOSTNAME" --author "${BUILDHISTORY_COMMIT_AUTHOR}" > /dev/null
  440. done
  441. if [ "${BUILDHISTORY_PUSH_REPO}" != "" ] ; then
  442. git push -q ${BUILDHISTORY_PUSH_REPO}
  443. fi
  444. else
  445. git commit ${BUILDHISTORY_DIR}/ --allow-empty -m "No changes: Build ${BUILDNAME} of ${DISTRO} ${DISTRO_VERSION} for machine ${MACHINE} on $HOSTNAME" --author "${BUILDHISTORY_COMMIT_AUTHOR}" > /dev/null
  446. fi) || true
  447. }
  448. python buildhistory_eventhandler() {
  449. if e.data.getVar('BUILDHISTORY_FEATURES', True).strip():
  450. if e.data.getVar("BUILDHISTORY_COMMIT", True) == "1":
  451. bb.build.exec_func("buildhistory_commit", e.data)
  452. }
  453. addhandler buildhistory_eventhandler
  454. buildhistory_eventhandler[eventmask] = "bb.event.BuildCompleted"
  455. # FIXME this ought to be moved into the fetcher
  456. def _get_srcrev_values(d):
  457. """
  458. Return the version strings for the current recipe
  459. """
  460. scms = []
  461. fetcher = bb.fetch.Fetch(d.getVar('SRC_URI', True).split(), d)
  462. urldata = fetcher.ud
  463. for u in urldata:
  464. if urldata[u].method.supports_srcrev():
  465. scms.append(u)
  466. autoinc_templ = 'AUTOINC+'
  467. dict_srcrevs = {}
  468. dict_tag_srcrevs = {}
  469. for scm in scms:
  470. ud = urldata[scm]
  471. for name in ud.names:
  472. rev = ud.method.sortable_revision(scm, ud, d, name)
  473. # Clean this up when we next bump bitbake version
  474. if type(rev) != str:
  475. autoinc, rev = rev
  476. elif rev.startswith(autoinc_templ):
  477. rev = rev[len(autoinc_templ):]
  478. dict_srcrevs[name] = rev
  479. if 'tag' in ud.parm:
  480. tag = ud.parm['tag'];
  481. key = name+'_'+tag
  482. dict_tag_srcrevs[key] = rev
  483. return (dict_srcrevs, dict_tag_srcrevs)
  484. do_fetch[postfuncs] += "write_srcrev"
  485. python write_srcrev() {
  486. pkghistdir = d.getVar('BUILDHISTORY_DIR_PACKAGE', True)
  487. srcrevfile = os.path.join(pkghistdir, 'latest_srcrev')
  488. srcrevs, tag_srcrevs = _get_srcrev_values(d)
  489. if srcrevs:
  490. if not os.path.exists(pkghistdir):
  491. os.makedirs(pkghistdir)
  492. old_tag_srcrevs = {}
  493. if os.path.exists(srcrevfile):
  494. with open(srcrevfile) as f:
  495. for line in f:
  496. if line.startswith('# tag_'):
  497. key, value = line.split("=", 1)
  498. key = key.replace('# tag_', '').strip()
  499. value = value.replace('"', '').strip()
  500. old_tag_srcrevs[key] = value
  501. with open(srcrevfile, 'w') as f:
  502. orig_srcrev = d.getVar('SRCREV', False) or 'INVALID'
  503. if orig_srcrev != 'INVALID':
  504. f.write('# SRCREV = "%s"\n' % orig_srcrev)
  505. if len(srcrevs) > 1:
  506. for name, srcrev in srcrevs.items():
  507. orig_srcrev = d.getVar('SRCREV_%s' % name, False)
  508. if orig_srcrev:
  509. f.write('# SRCREV_%s = "%s"\n' % (name, orig_srcrev))
  510. f.write('SRCREV_%s = "%s"\n' % (name, srcrev))
  511. else:
  512. f.write('SRCREV = "%s"\n' % srcrevs.itervalues().next())
  513. if len(tag_srcrevs) > 0:
  514. for name, srcrev in tag_srcrevs.items():
  515. f.write('# tag_%s = "%s"\n' % (name, srcrev))
  516. if name in old_tag_srcrevs and old_tag_srcrevs[name] != srcrev:
  517. pkg = d.getVar('PN', True)
  518. bb.warn("Revision for tag %s in package %s was changed since last build (from %s to %s)" % (name, pkg, old_tag_srcrevs[name], srcrev))
  519. else:
  520. if os.path.exists(srcrevfile):
  521. os.remove(srcrevfile)
  522. }