diff options
Diffstat (limited to 'meta')
-rw-r--r-- | meta/classes/packagefeed-stability.bbclass | 252 |
1 files changed, 0 insertions, 252 deletions
diff --git a/meta/classes/packagefeed-stability.bbclass b/meta/classes/packagefeed-stability.bbclass deleted file mode 100644 index 5648602564..0000000000 --- a/meta/classes/packagefeed-stability.bbclass +++ /dev/null | |||
@@ -1,252 +0,0 @@ | |||
1 | # Class to avoid copying packages into the feed if they haven't materially changed | ||
2 | # | ||
3 | # Copyright (C) 2015 Intel Corporation | ||
4 | # Released under the MIT license (see COPYING.MIT for details) | ||
5 | # | ||
6 | # This class effectively intercepts packages as they are written out by | ||
7 | # do_package_write_*, causing them to be written into a different | ||
8 | # directory where we can compare them to whatever older packages might | ||
9 | # be in the "real" package feed directory, and avoid copying the new | ||
10 | # package to the feed if it has not materially changed. The idea is to | ||
11 | # avoid unnecessary churn in the packages when dependencies trigger task | ||
12 | # reexecution (and thus repackaging). Enabling the class is simple: | ||
13 | # | ||
14 | # INHERIT += "packagefeed-stability" | ||
15 | # | ||
16 | # Caveats: | ||
17 | # 1) Latest PR values in the build system may not match those in packages | ||
18 | # seen on the target (naturally) | ||
19 | # 2) If you rebuild from sstate without the existing package feed present, | ||
20 | # you will lose the "state" of the package feed i.e. the preserved old | ||
21 | # package versions. Not the end of the world, but would negate the | ||
22 | # entire purpose of this class. | ||
23 | # | ||
24 | # Note that running -c cleanall on a recipe will purposely delete the old | ||
25 | # package files so they will definitely be copied the next time. | ||
26 | |||
27 | python() { | ||
28 | if bb.data.inherits_class('native', d) or bb.data.inherits_class('cross', d): | ||
29 | return | ||
30 | # Package backend agnostic intercept | ||
31 | # This assumes that the package_write task is called package_write_<pkgtype> | ||
32 | # and that the directory in which packages should be written is | ||
33 | # pointed to by the variable DEPLOY_DIR_<PKGTYPE> | ||
34 | for pkgclass in (d.getVar('PACKAGE_CLASSES') or '').split(): | ||
35 | if pkgclass.startswith('package_'): | ||
36 | pkgtype = pkgclass.split('_', 1)[1] | ||
37 | pkgwritefunc = 'do_package_write_%s' % pkgtype | ||
38 | sstate_outputdirs = d.getVarFlag(pkgwritefunc, 'sstate-outputdirs', False) | ||
39 | deploydirvar = 'DEPLOY_DIR_%s' % pkgtype.upper() | ||
40 | deploydirvarref = '${' + deploydirvar + '}' | ||
41 | pkgcomparefunc = 'do_package_compare_%s' % pkgtype | ||
42 | |||
43 | if bb.data.inherits_class('image', d): | ||
44 | d.appendVarFlag('do_rootfs', 'recrdeptask', ' ' + pkgcomparefunc) | ||
45 | |||
46 | if bb.data.inherits_class('populate_sdk_base', d): | ||
47 | d.appendVarFlag('do_populate_sdk', 'recrdeptask', ' ' + pkgcomparefunc) | ||
48 | |||
49 | if bb.data.inherits_class('populate_sdk_ext', d): | ||
50 | d.appendVarFlag('do_populate_sdk_ext', 'recrdeptask', ' ' + pkgcomparefunc) | ||
51 | |||
52 | d.appendVarFlag('do_build', 'recrdeptask', ' ' + pkgcomparefunc) | ||
53 | |||
54 | if d.getVarFlag(pkgwritefunc, 'noexec') or not d.getVarFlag(pkgwritefunc, 'task'): | ||
55 | # Packaging is disabled for this recipe, we shouldn't do anything | ||
56 | continue | ||
57 | |||
58 | if deploydirvarref in sstate_outputdirs: | ||
59 | deplor_dir_pkgtype = d.expand(deploydirvarref + '-prediff') | ||
60 | # Set intermediate output directory | ||
61 | d.setVarFlag(pkgwritefunc, 'sstate-outputdirs', sstate_outputdirs.replace(deploydirvarref, deplor_dir_pkgtype)) | ||
62 | # Update SSTATE_DUPWHITELIST to avoid shared location conflicted error | ||
63 | d.appendVar('SSTATE_DUPWHITELIST', ' %s' % deplor_dir_pkgtype) | ||
64 | |||
65 | d.setVar(pkgcomparefunc, d.getVar('do_package_compare', False)) | ||
66 | d.setVarFlags(pkgcomparefunc, d.getVarFlags('do_package_compare', False)) | ||
67 | d.appendVarFlag(pkgcomparefunc, 'depends', ' build-compare-native:do_populate_sysroot') | ||
68 | bb.build.addtask(pkgcomparefunc, 'do_build', 'do_packagedata ' + pkgwritefunc, d) | ||
69 | } | ||
70 | |||
71 | # This isn't the real task function - it's a template that we use in the | ||
72 | # anonymous python code above | ||
73 | fakeroot python do_package_compare () { | ||
74 | currenttask = d.getVar('BB_CURRENTTASK') | ||
75 | pkgtype = currenttask.rsplit('_', 1)[1] | ||
76 | package_compare_impl(pkgtype, d) | ||
77 | } | ||
78 | |||
79 | def package_compare_impl(pkgtype, d): | ||
80 | import errno | ||
81 | import fnmatch | ||
82 | import glob | ||
83 | import subprocess | ||
84 | import oe.sstatesig | ||
85 | |||
86 | pn = d.getVar('PN') | ||
87 | deploydir = d.getVar('DEPLOY_DIR_%s' % pkgtype.upper()) | ||
88 | prepath = deploydir + '-prediff/' | ||
89 | |||
90 | # Find out PKGR values are | ||
91 | pkgdatadir = d.getVar('PKGDATA_DIR') | ||
92 | packages = [] | ||
93 | try: | ||
94 | with open(os.path.join(pkgdatadir, pn), 'r') as f: | ||
95 | for line in f: | ||
96 | if line.startswith('PACKAGES:'): | ||
97 | packages = line.split(':', 1)[1].split() | ||
98 | break | ||
99 | except IOError as e: | ||
100 | if e.errno == errno.ENOENT: | ||
101 | pass | ||
102 | |||
103 | if not packages: | ||
104 | bb.debug(2, '%s: no packages, nothing to do' % pn) | ||
105 | return | ||
106 | |||
107 | pkgrvalues = {} | ||
108 | rpkgnames = {} | ||
109 | rdepends = {} | ||
110 | pkgvvalues = {} | ||
111 | for pkg in packages: | ||
112 | with open(os.path.join(pkgdatadir, 'runtime', pkg), 'r') as f: | ||
113 | for line in f: | ||
114 | if line.startswith('PKGR:'): | ||
115 | pkgrvalues[pkg] = line.split(':', 1)[1].strip() | ||
116 | if line.startswith('PKGV:'): | ||
117 | pkgvvalues[pkg] = line.split(':', 1)[1].strip() | ||
118 | elif line.startswith('PKG_%s:' % pkg): | ||
119 | rpkgnames[pkg] = line.split(':', 1)[1].strip() | ||
120 | elif line.startswith('RDEPENDS_%s:' % pkg): | ||
121 | rdepends[pkg] = line.split(':', 1)[1].strip() | ||
122 | |||
123 | # Prepare a list of the runtime package names for packages that were | ||
124 | # actually produced | ||
125 | rpkglist = [] | ||
126 | for pkg, rpkg in rpkgnames.items(): | ||
127 | if os.path.exists(os.path.join(pkgdatadir, 'runtime', pkg + '.packaged')): | ||
128 | rpkglist.append((rpkg, pkg)) | ||
129 | rpkglist.sort(key=lambda x: len(x[0]), reverse=True) | ||
130 | |||
131 | pvu = d.getVar('PV', False) | ||
132 | if '$' + '{SRCPV}' in pvu: | ||
133 | pvprefix = pvu.split('$' + '{SRCPV}', 1)[0] | ||
134 | else: | ||
135 | pvprefix = None | ||
136 | |||
137 | pkgwritetask = 'package_write_%s' % pkgtype | ||
138 | files = [] | ||
139 | docopy = False | ||
140 | manifest, _ = oe.sstatesig.sstate_get_manifest_filename(pkgwritetask, d) | ||
141 | mlprefix = d.getVar('MLPREFIX') | ||
142 | # Copy recipe's all packages if one of the packages are different to make | ||
143 | # they have the same PR. | ||
144 | with open(manifest, 'r') as f: | ||
145 | for line in f: | ||
146 | if line.startswith(prepath): | ||
147 | srcpath = line.rstrip() | ||
148 | if os.path.isfile(srcpath): | ||
149 | destpath = os.path.join(deploydir, os.path.relpath(srcpath, prepath)) | ||
150 | |||
151 | # This is crude but should work assuming the output | ||
152 | # package file name starts with the package name | ||
153 | # and rpkglist is sorted by length (descending) | ||
154 | pkgbasename = os.path.basename(destpath) | ||
155 | pkgname = None | ||
156 | for rpkg, pkg in rpkglist: | ||
157 | if mlprefix and pkgtype == 'rpm' and rpkg.startswith(mlprefix): | ||
158 | rpkg = rpkg[len(mlprefix):] | ||
159 | if pkgbasename.startswith(rpkg): | ||
160 | pkgr = pkgrvalues[pkg] | ||
161 | destpathspec = destpath.replace(pkgr, '*') | ||
162 | if pvprefix: | ||
163 | pkgv = pkgvvalues[pkg] | ||
164 | if pkgv.startswith(pvprefix): | ||
165 | pkgvsuffix = pkgv[len(pvprefix):] | ||
166 | if '+' in pkgvsuffix: | ||
167 | newpkgv = pvprefix + '*+' + pkgvsuffix.split('+', 1)[1] | ||
168 | destpathspec = destpathspec.replace(pkgv, newpkgv) | ||
169 | pkgname = pkg | ||
170 | break | ||
171 | else: | ||
172 | bb.warn('Unable to map %s back to package' % pkgbasename) | ||
173 | destpathspec = destpath | ||
174 | |||
175 | oldfile = None | ||
176 | if not docopy: | ||
177 | oldfiles = glob.glob(destpathspec) | ||
178 | if oldfiles: | ||
179 | oldfile = oldfiles[-1] | ||
180 | result = subprocess.call(['pkg-diff.sh', oldfile, srcpath]) | ||
181 | if result != 0: | ||
182 | docopy = True | ||
183 | bb.note("%s and %s are different, will copy packages" % (oldfile, srcpath)) | ||
184 | else: | ||
185 | docopy = True | ||
186 | bb.note("No old packages found for %s, will copy packages" % pkgname) | ||
187 | |||
188 | files.append((pkgname, pkgbasename, srcpath, destpath)) | ||
189 | |||
190 | # Remove all the old files and copy again if docopy | ||
191 | if docopy: | ||
192 | bb.note('Copying packages for recipe %s' % pn) | ||
193 | pcmanifest = os.path.join(prepath, d.expand('pkg-compare-manifest-${MULTIMACH_TARGET_SYS}-${PN}')) | ||
194 | try: | ||
195 | with open(pcmanifest, 'r') as f: | ||
196 | for line in f: | ||
197 | fn = line.rstrip() | ||
198 | if fn: | ||
199 | try: | ||
200 | os.remove(fn) | ||
201 | bb.note('Removed old package %s' % fn) | ||
202 | except OSError as e: | ||
203 | if e.errno == errno.ENOENT: | ||
204 | pass | ||
205 | except IOError as e: | ||
206 | if e.errno == errno.ENOENT: | ||
207 | pass | ||
208 | |||
209 | # Create new manifest | ||
210 | with open(pcmanifest, 'w') as f: | ||
211 | for pkgname, pkgbasename, srcpath, destpath in files: | ||
212 | destdir = os.path.dirname(destpath) | ||
213 | bb.utils.mkdirhier(destdir) | ||
214 | # Remove allarch rpm pkg if it is already existed (for | ||
215 | # multilib), they're identical in theory, but sstate.bbclass | ||
216 | # copies it again, so keep align with that. | ||
217 | if os.path.exists(destpath) and pkgtype == 'rpm' \ | ||
218 | and d.getVar('PACKAGE_ARCH') == 'all': | ||
219 | os.unlink(destpath) | ||
220 | if (os.stat(srcpath).st_dev == os.stat(destdir).st_dev): | ||
221 | # Use a hard link to save space | ||
222 | os.link(srcpath, destpath) | ||
223 | else: | ||
224 | shutil.copyfile(srcpath, destpath) | ||
225 | f.write('%s\n' % destpath) | ||
226 | else: | ||
227 | bb.note('Not copying packages for recipe %s' % pn) | ||
228 | |||
229 | do_cleansstate[postfuncs] += "pfs_cleanpkgs" | ||
230 | python pfs_cleanpkgs () { | ||
231 | import errno | ||
232 | for pkgclass in (d.getVar('PACKAGE_CLASSES') or '').split(): | ||
233 | if pkgclass.startswith('package_'): | ||
234 | pkgtype = pkgclass.split('_', 1)[1] | ||
235 | deploydir = d.getVar('DEPLOY_DIR_%s' % pkgtype.upper()) | ||
236 | prepath = deploydir + '-prediff' | ||
237 | pcmanifest = os.path.join(prepath, d.expand('pkg-compare-manifest-${MULTIMACH_TARGET_SYS}-${PN}')) | ||
238 | try: | ||
239 | with open(pcmanifest, 'r') as f: | ||
240 | for line in f: | ||
241 | fn = line.rstrip() | ||
242 | if fn: | ||
243 | try: | ||
244 | os.remove(fn) | ||
245 | except OSError as e: | ||
246 | if e.errno == errno.ENOENT: | ||
247 | pass | ||
248 | os.remove(pcmanifest) | ||
249 | except IOError as e: | ||
250 | if e.errno == errno.ENOENT: | ||
251 | pass | ||
252 | } | ||