3 # Generate the output tree into a specified directory.
6 import argparse
, sys
, os
, errno
, shutil
, re
, subprocess
10 source_dir
= os
.path
.abspath(os
.path
.dirname(__file__
))
11 sys
.path
.append(source_dir
)
12 # and import libraries we have
13 from lib
import kconfig
, patch
, make
14 from lib
import bpgit
as git
15 from lib
import bpgpg
as gpg
16 from lib
import bpkup
as kup
18 def read_copy_list(copyfile
):
20 Read a copy-list file and return a list of (source, target)
21 tuples. The source and target are usually the same, but in
22 the copy-list file there may be a rename included.
26 # remove leading/trailing whitespace
29 if not item
or item
[0] == '#':
32 raise Exception("Input path '%s' is absolute path, this isn't allowed" % (item
, ))
34 srcitem
, dstitem
= item
.split(' -> ')
35 if (srcitem
[-1] == '/') != (dstitem
[-1] == '/'):
36 raise Exception("Cannot copy file/dir to dir/file")
38 srcitem
= dstitem
= item
39 ret
.append((srcitem
, dstitem
))
43 def read_dependencies(depfilename
):
45 Read a (the) dependency file and return the list of
46 dependencies as a dictionary, mapping a Kconfig symbol
47 to a list of kernel version dependencies.
49 If a backported feature that an upstream backported driver
50 depends on had kconfig limitations (ie, debugging feature not
51 available) a built constaint restriction can be expressed
52 by using a kconfig expression. The kconfig expressions can
53 be specified by using the "kconfig: " prefix.
55 While reading ignore blank or commented lines.
58 depfile
= open(depfilename
, 'r')
62 if not item
or item
[0] == '#':
64 if "kconfig:" in item
:
65 sym
, kconfig_exp
= item
.split(" ", 1)
67 ret
[sym
] = [kconfig_exp
, ]
69 ret
[sym
].append(kconfig_exp
)
71 sym
, dep
= item
.split()
79 def check_output_dir(d
, clean
):
81 Check that the output directory doesn't exist or is empty,
82 unless clean is True in which case it's nuked. This helps
83 sanity check the output when generating a tree, so usually
84 running with --clean isn't suggested.
87 shutil
.rmtree(d
, ignore_errors
=True)
91 if e
.errno
!= errno
.ENOENT
:
95 def copytree(src
, dst
, symlinks
=False, ignore
=None):
97 Copy a directory tree. This differs from shutil.copytree()
98 in that it allows destination directories to already exist.
100 names
= os
.listdir(src
)
101 if ignore
is not None:
102 ignored_names
= ignore(src
, names
)
104 ignored_names
= set()
106 if not os
.path
.isdir(dst
):
110 if name
in ignored_names
:
112 srcname
= os
.path
.join(src
, name
)
113 dstname
= os
.path
.join(dst
, name
)
115 if symlinks
and os
.path
.islink(srcname
):
116 linkto
= os
.readlink(srcname
)
117 os
.symlink(linkto
, dstname
)
118 elif os
.path
.isdir(srcname
):
119 copytree(srcname
, dstname
, symlinks
, ignore
)
121 shutil
.copy2(srcname
, dstname
)
122 except (IOError, os
.error
) as why
:
123 errors
.append((srcname
, dstname
, str(why
)))
124 # catch the Error from the recursive copytree so that we can
125 # continue with other files
126 except shutil
.Error
as err
:
127 errors
.extend(err
.args
[0])
129 shutil
.copystat(src
, dst
)
131 # can't copy file access times on Windows
133 except OSError as why
:
134 errors
.extend((src
, dst
, str(why
)))
136 raise shutil
.Error(errors
)
139 def copy_files(srcpath
, copy_list
, outdir
):
141 Copy the copy_list files and directories from the srcpath
142 to the outdir. The copy_list contains source and target
145 For now, it also ignores any *~ editor backup files, though
146 this should probably be generalized (maybe using .gitignore?)
147 Similarly the code that only copies some files (*.c, *.h,
148 *.awk, Kconfig, Makefile) to avoid any build remnants in the
149 kernel if they should exist.
151 for srcitem
, tgtitem
in copy_list
:
153 copytree(srcpath
, outdir
, ignore
=shutil
.ignore_patterns('*~'))
154 elif tgtitem
[-1] == '/':
155 def copy_ignore(dir, entries
):
158 if i
[-2:] == '.o' or i
[-1] == '~':
161 copytree(os
.path
.join(srcpath
, srcitem
),
162 os
.path
.join(outdir
, tgtitem
),
166 os
.makedirs(os
.path
.join(outdir
, os
.path
.dirname(tgtitem
)))
168 # ignore dirs we might have created just now
169 if e
.errno
!= errno
.EEXIST
:
171 shutil
.copy(os
.path
.join(srcpath
, srcitem
),
172 os
.path
.join(outdir
, tgtitem
))
175 def copy_git_files(srcpath
, copy_list
, rev
, outdir
):
177 "Copy" files from a git repository. This really means listing them with
178 ls-tree and then using git show to obtain all the blobs.
180 for srcitem
, tgtitem
in copy_list
:
181 for m
, t
, h
, f
in git
.ls_tree(rev
=rev
, files
=(srcitem
,), tree
=srcpath
):
183 f
= os
.path
.join(outdir
, f
.replace(srcitem
, tgtitem
))
184 d
= os
.path
.dirname(f
)
185 if not os
.path
.exists(d
):
188 git
.get_blob(h
, outf
, tree
=srcpath
)
190 os
.chmod(f
, int(m
, 8))
192 def automatic_backport_mangle_c_file(name
):
193 return name
.replace('/', '-')
196 def add_automatic_backports(args
):
198 export
= re
.compile(r
'^EXPORT_SYMBOL(_GPL)?\((?P<sym>[^\)]*)\)')
199 bpi
= kconfig
.get_backport_info(os
.path
.join(args
.outdir
, 'compat', 'Kconfig'))
200 configtree
= kconfig
.ConfigTree(os
.path
.join(args
.outdir
, 'Kconfig'))
201 all_selects
= configtree
.all_selects()
202 for sym
, vals
in bpi
.items():
203 if sym
.startswith('BACKPORT_BUILD_'):
204 if not sym
[15:] in all_selects
:
205 disable_list
.append(sym
)
207 symtype
, module_name
, c_files
, h_files
= vals
212 files
.append((f
, os
.path
.join('compat', automatic_backport_mangle_c_file(f
))))
214 files
.append((os
.path
.join('include', f
),
215 os
.path
.join('include', os
.path
.dirname(f
), 'backport-' + os
.path
.basename(f
))))
216 if args
.git_revision
:
217 copy_git_files(args
.kerneldir
, files
, args
.git_revision
, args
.outdir
)
219 copy_files(args
.kerneldir
, files
, args
.outdir
)
221 # now add the Makefile line
222 mf
= open(os
.path
.join(args
.outdir
, 'compat', 'Makefile'), 'a+')
223 o_files
= [automatic_backport_mangle_c_file(f
)[:-1] + 'o' for f
in c_files
]
224 if symtype
== 'tristate':
226 raise Exception('backporting a module requires a #module-name')
228 mf
.write('%s-objs += %s\n' % (module_name
, of
))
229 mf
.write('obj-$(CPTCFG_%s) += %s.o\n' % (sym
, module_name
))
230 elif symtype
== 'bool':
231 mf
.write('compat-$(CPTCFG_%s) += %s\n' % (sym
, ' '.join(o_files
)))
233 # finally create the include file
236 for l
in open(os
.path
.join(args
.outdir
, 'compat',
237 automatic_backport_mangle_c_file(f
)), 'r'):
240 syms
.append(m
.group('sym'))
242 outf
= open(os
.path
.join(args
.outdir
, 'include', f
), 'w')
243 outf
.write('/* Automatically created during backport process */\n')
244 outf
.write('#ifndef CPTCFG_%s\n' % sym
)
245 outf
.write('#include_next <%s>\n' % f
)
246 outf
.write('#else\n');
248 outf
.write('#undef %s\n' % s
)
249 outf
.write('#define %s LINUX_BACKPORT(%s)\n' % (s
, s
))
250 outf
.write('#include <%s>\n' % (os
.path
.dirname(f
) + '/backport-' + os
.path
.basename(f
), ))
251 outf
.write('#endif /* CPTCFG_%s */\n' % sym
)
254 def git_debug_init(args
):
256 Initialize a git repository in the output directory and commit the current
257 code in it. This is only used for debugging the transformations this code
258 will do to the output later.
260 if not args
.gitdebug
:
262 git
.init(tree
=args
.outdir
)
263 git
.commit_all("Copied backport", tree
=args
.outdir
)
266 def git_debug_snapshot(args
, name
):
268 Take a git snapshot for the debugging.
270 if not args
.gitdebug
:
272 git
.commit_all(name
, tree
=args
.outdir
)
274 def get_rel_spec_stable(rel
):
276 Returns release specs for a linux-stable backports based release.
279 m
= re
.match(r
"(?P<VERSION>\d+)\.+" \
280 "(?P<PATCHLEVEL>\d+)[.]*" \
281 "(?P<SUBLEVEL>\d*)" \
282 "[-rc]+(?P<RC_VERSION>\d+)\-+" \
283 "(?P<RELMOD_UPDATE>\d+)[-]*" \
284 "(?P<RELMOD_TYPE>[usnpc]*)", \
287 m
= re
.match(r
"(?P<VERSION>\d+)\.+" \
288 "(?P<PATCHLEVEL>\d+)[.]*" \
289 "(?P<SUBLEVEL>\d*)\-+" \
290 "(?P<RELMOD_UPDATE>\d+)[-]*" \
291 "(?P<RELMOD_TYPE>[usnpc]*)", \
297 def get_rel_spec_next(rel
):
299 Returns release specs for a linux-next backports based release.
301 m
= re
.match(r
"(?P<DATE_VERSION>\d+)[-]*" \
302 "(?P<RELMOD_UPDATE>\d*)[-]*" \
303 "(?P<RELMOD_TYPE>[usnpc]*)", \
309 def get_rel_prep(rel
):
311 Returns a dict with prep work details we need prior to
312 uploading a backports release to kernel.org
314 rel_specs
= get_rel_spec_stable(rel
)
319 rel_specs
= get_rel_spec_next(rel
)
321 sys
.stdout
.write("rel: %s\n" % rel
)
323 if (rel_specs
['RELMOD_UPDATE'] == '0' or
324 rel_specs
['RELMOD_UPDATE'] == '1'):
327 date
= rel_specs
['DATE_VERSION']
332 if (len(month
) != 2):
340 rel_tag
= "backports-" + rel
.replace(rel_specs
['RELMOD_TYPE'], "")
343 if (not rel_specs
['RELMOD_UPDATE']):
345 if (rel_specs
['RELMOD_UPDATE'] == '0'):
347 ignore
+= rel_specs
['RELMOD_UPDATE']
348 if (rel_specs
['RELMOD_TYPE'] != ''):
349 ignore
+= rel_specs
['RELMOD_TYPE']
350 base_rel
= rel
.replace(ignore
, "")
351 paths
.append("v" + base_rel
)
352 rel_tag
= "v" + rel
.replace(rel_specs
['RELMOD_TYPE'], "")
354 rel_prep
= dict(stable
= is_stable
,
355 expected_tag
= rel_tag
,
356 paths_to_create
= paths
)
359 def create_tar_and_bz2(tar_name
, dir_to_tar
):
361 We need both a tar file and bzip2 for kernel.org, the tar file
362 gets signed, then we upload the compressed version, kup-server
363 in the backend decompresses and verifies the tarball against
366 basename
= os
.path
.basename(dir_to_tar
)
367 tar
= tarfile
.open(tar_name
, "w")
368 tar
.add(dir_to_tar
, basename
)
371 tar_file
= open(tar_name
, "r")
373 bz2_file
= bz2
.BZ2File(tar_name
+ ".bz2", 'wb', compresslevel
=9)
374 bz2_file
.write(tar_file
.read())
377 def upload_release(args
, rel_prep
, logwrite
=lambda x
:None):
379 Given a path of a relase make tarball out of it, PGP sign it, and
380 then upload it to kernel.org using kup.
382 The linux-next based release do not require a RELMOD_UPDATE
383 given that typically only one release is made per day. Using
384 RELMOD_UPDATE for these releases is allowed though and if
385 present it must be > 1.
387 The linux-stable based releases require a RELMOD_UPDATE.
389 RELMOD_UPDATE must be numeric and > 0 just as the RC releases
392 The tree must also be tagged with the respective release, without
393 the RELMOD_TYPE. For linux-next based releases this consists of
394 backports- followed by DATE_VERSION and if RELMOD_TYPE is present.
395 For linux-stable releases this consists of v followed by the
396 full release version except the RELMOD_TYPE.
398 Uploads will not be allowed if these rules are not followed.
400 korg_path
= "/pub/linux/kernel/projects/backports"
402 if (rel_prep
['stable']):
403 korg_path
+= "/stable"
405 parent
= os
.path
.dirname(args
.outdir
)
406 release
= os
.path
.basename(args
.outdir
)
407 tar_name
= parent
+ '/' + release
+ ".tar"
408 bzip2_name
= tar_name
+ ".bz2"
410 create_tar_and_bz2(tar_name
, args
.outdir
)
412 logwrite(gpg
.sign(tar_name
, extra_args
=['--armor', '--detach-sign']))
414 logwrite("------------------------------------------------------")
416 if (not args
.kup_test
):
417 logwrite("About to upload, current target path contents:")
419 logwrite("kup-test: current target path contents:")
421 logwrite(kup
.ls(path
=korg_path
))
423 for path
in rel_prep
['paths_to_create']:
424 korg_path
+= '/' + path
425 if (not args
.kup_test
):
426 logwrite("create directory: %s" % korg_path
)
427 logwrite(kup
.mkdir(korg_path
))
429 if (not args
.kup_test
):
430 logwrite("upload file %s to %s" % (bzip2_name
, korg_path
))
431 logwrite(kup
.put(bzip2_name
, tar_name
+ '.asc', korg_path
))
432 logwrite("\nFinished upload!\n")
433 logwrite("Target path contents:")
434 logwrite(kup
.ls(path
=korg_path
))
436 kup_cmd
= "kup put /\n\t\t%s /\n\t\t%s /\n\t\t%s" % (bzip2_name
, tar_name
+ '.asc', korg_path
)
437 logwrite("kup-test: skipping cmd: %s" % kup_cmd
)
440 # set up and parse arguments
441 parser
= argparse
.ArgumentParser(description
='generate backport tree')
442 parser
.add_argument('kerneldir', metavar
='<kernel tree>', type=str,
443 help='Kernel tree to copy drivers from')
444 parser
.add_argument('outdir', metavar
='<output directory>', type=str,
445 help='Directory to write the generated tree to')
446 parser
.add_argument('--copy-list', metavar
='<listfile>', type=argparse
.FileType('r'),
448 help='File containing list of files/directories to copy, default "copy-list"')
449 parser
.add_argument('--git-revision', metavar
='<revision>', type=str,
450 help='git commit revision (see gitrevisions(7)) to take objects from.' +
451 'If this is specified, the kernel tree is used as git object storage ' +
452 'and we use git ls-tree to get the files.')
453 parser
.add_argument('--clean', const
=True, default
=False, action
="store_const",
454 help='Clean output directory instead of erroring if it isn\'t empty')
455 parser
.add_argument('--refresh', const
=True, default
=False, action
="store_const",
456 help='Refresh patches as they are applied, the source dir will be modified!')
457 parser
.add_argument('--base-name', metavar
='<name>', type=str, default
='Linux',
458 help='name of base tree, default just "Linux"')
459 parser
.add_argument('--gitdebug', const
=True, default
=False, action
="store_const",
460 help='Use git, in the output tree, to debug the various transformation steps ' +
461 'that the tree generation makes (apply patches, ...)')
462 parser
.add_argument('--verbose', const
=True, default
=False, action
="store_const",
463 help='Print more verbose information')
464 parser
.add_argument('--extra-driver', nargs
=2, metavar
=('<source dir>', '<copy-list>'), type=str,
465 action
='append', default
=[], help='Extra driver directory/copy-list.')
466 parser
.add_argument('--kup', const
=True, default
=False, action
="store_const",
467 help='For maintainers: upload a release to kernel.org')
468 parser
.add_argument('--kup-test', const
=True, default
=False, action
="store_const",
469 help='For maintainers: do all the work as if you were about to ' +
470 'upload to kernel.org but do not do the final `kup put` ' +
471 'and also do not run any `kup mkdir` commands. This will ' +
472 'however run `kup ls` on the target paths so ' +
473 'at the very least we test your kup configuration. ' +
474 'If this is your first time uploading use this first!')
475 args
= parser
.parse_args()
478 sys
.stdout
.write(msg
)
479 sys
.stdout
.write('\n')
482 return process(args
.kerneldir
, args
.outdir
, args
.copy_list
,
483 git_revision
=args
.git_revision
, clean
=args
.clean
,
484 refresh
=args
.refresh
, base_name
=args
.base_name
,
485 gitdebug
=args
.gitdebug
, verbose
=args
.verbose
,
486 extra_driver
=args
.extra_driver
,
488 kup_test
=args
.kup_test
,
491 def process(kerneldir
, outdir
, copy_list_file
, git_revision
=None,
492 clean
=False, refresh
=False, base_name
="Linux", gitdebug
=False,
493 verbose
=False, extra_driver
=[], kup
=False,
495 logwrite
=lambda x
:None,
496 git_tracked_version
=False):
498 def __init__(self
, kerneldir
, outdir
, copy_list_file
,
499 git_revision
, clean
, refresh
, base_name
,
500 gitdebug
, verbose
, extra_driver
, kup
,
502 self
.kerneldir
= kerneldir
504 self
.copy_list
= copy_list_file
505 self
.git_revision
= git_revision
507 self
.refresh
= refresh
508 self
.base_name
= base_name
509 self
.gitdebug
= gitdebug
510 self
.verbose
= verbose
511 self
.extra_driver
= extra_driver
513 self
.kup_test
= kup_test
514 def git_paranoia(tree
=None, logwrite
=lambda x
:None):
515 data
= git
.paranoia(tree
)
517 logwrite('Cannot use %s' % tree
)
518 logwrite('%s' % data
['output'])
521 logwrite('Validated tree: %s' % tree
)
523 args
= Args(kerneldir
, outdir
, copy_list_file
,
524 git_revision
, clean
, refresh
, base_name
,
525 gitdebug
, verbose
, extra_driver
, kup
, kup_test
)
528 # start processing ...
529 if (args
.kup
or args
.kup_test
):
530 git_paranoia(source_dir
, logwrite
)
531 git_paranoia(kerneldir
, logwrite
)
533 rel_describe
= git
.describe(rev
=None, tree
=source_dir
, extra_args
=['--dirty'])
534 release
= os
.path
.basename(args
.outdir
)
535 version
= release
.replace("backports-", "")
537 rel_prep
= get_rel_prep(version
)
539 logwrite('Invalid backports release name: %s' % release
)
540 logwrite('For rules on the release name see upload_release()')
542 rel_type
= "linux-stable"
543 if (not rel_prep
['stable']):
544 rel_type
= "linux-next"
545 if (rel_prep
['expected_tag'] != rel_describe
):
546 logwrite('Unexpected %s based backports release tag on' % rel_type
)
547 logwrite('the backports tree tree: %s\n' % rel_describe
)
548 logwrite('You asked to make a release with this ')
549 logwrite('directory name: %s' % release
)
550 logwrite('The actual expected tag we should find on')
551 logwrite('the backports tree then is: %s\n' % rel_prep
['expected_tag'])
552 logwrite('For rules on the release name see upload_release()')
555 copy_list
= read_copy_list(args
.copy_list
)
556 deplist
= read_dependencies(os
.path
.join(source_dir
, 'dependencies'))
558 # validate output directory
559 check_output_dir(args
.outdir
, args
.clean
)
562 backport_files
= [(x
, x
) for x
in [
563 'Kconfig', 'Makefile', 'Makefile.build', 'Makefile.kernel', '.gitignore',
564 'Makefile.real', 'compat/', 'backport-include/', 'kconf/', 'defconfigs/',
565 'scripts/', '.blacklist.map', 'udev/',
567 if not args
.git_revision
:
568 logwrite('Copy original source files ...')
570 logwrite('Get original source files from git ...')
572 copy_files(os
.path
.join(source_dir
, 'backport'), backport_files
, args
.outdir
)
576 if not args
.git_revision
:
577 copy_files(args
.kerneldir
, copy_list
, args
.outdir
)
579 copy_git_files(args
.kerneldir
, copy_list
, args
.git_revision
, args
.outdir
)
581 # FIXME: should we add a git version of this (e.g. --git-extra-driver)?
582 for src
, copy_list
in args
.extra_driver
:
583 if (args
.kup
or args
.kup_test
):
585 copy_files(src
, read_copy_list(open(copy_list
, 'r')), args
.outdir
)
587 git_debug_snapshot(args
, 'Add driver sources')
589 disable_list
= add_automatic_backports(args
)
591 bpcfg
= kconfig
.ConfigTree(os
.path
.join(args
.outdir
, 'compat', 'Kconfig'))
592 bpcfg
.disable_symbols(disable_list
)
593 git_debug_snapshot(args
, 'Add automatic backports')
595 logwrite('Apply patches ...')
598 for root
, dirs
, files
in os
.walk(os
.path
.join(source_dir
, 'patches')):
600 if f
.endswith('.patch'):
601 patches
.append(os
.path
.join(root
, f
))
602 if f
.endswith('.cocci'):
603 sempatches
.append(os
.path
.join(root
, f
))
605 prefix_len
= len(os
.path
.join(source_dir
, 'patches')) + 1
606 for pfile
in patches
:
607 print_name
= pfile
[prefix_len
:]
608 # read the patch file
609 p
= patch
.fromfile(pfile
)
610 # complain if it's not a patch
612 raise Exception('No patch content found in %s' % print_name
)
613 # leading / seems to be stripped?
614 if 'dev/null' in p
.items
[0].source
:
615 raise Exception('Patches creating files are not supported (in %s)' % print_name
)
616 # check if the first file the patch touches exists, if so
617 # assume the patch needs to be applied -- otherwise continue
618 patched_file
= '/'.join(p
.items
[0].source
.split('/')[1:])
619 fullfn
= os
.path
.join(args
.outdir
, patched_file
)
620 if not os
.path
.exists(fullfn
):
622 logwrite("Not applying %s, not needed" % print_name
)
625 logwrite("Applying patch %s" % print_name
)
628 # but for refresh, of course look at all files the patch touches
629 for patchitem
in p
.items
:
630 patched_file
= '/'.join(patchitem
.source
.split('/')[1:])
631 fullfn
= os
.path
.join(args
.outdir
, patched_file
)
632 shutil
.copyfile(fullfn
, fullfn
+ '.orig_file')
634 process
= subprocess
.Popen(['patch', '-p1'], stdout
=subprocess
.PIPE
,
635 stderr
=subprocess
.STDOUT
, stdin
=subprocess
.PIPE
,
636 close_fds
=True, universal_newlines
=True,
638 output
= process
.communicate(input=open(pfile
, 'r').read())[0]
639 output
= output
.split('\n')
644 logwrite('> %s' % line
)
645 if process
.returncode
!= 0:
647 logwrite("Failed to apply changes from %s" % print_name
)
649 logwrite('> %s' % line
)
653 pfilef
= open(pfile
+ '.tmp', 'a')
654 pfilef
.write(p
.top_header
)
656 for patchitem
in p
.items
:
657 patched_file
= '/'.join(patchitem
.source
.split('/')[1:])
658 fullfn
= os
.path
.join(args
.outdir
, patched_file
)
659 process
= subprocess
.Popen(['diff', '-p', '-u', patched_file
+ '.orig_file', patched_file
,
660 '--label', 'a/' + patched_file
,
661 '--label', 'b/' + patched_file
],
662 stdout
=pfilef
, close_fds
=True,
663 universal_newlines
=True, cwd
=args
.outdir
)
665 os
.unlink(fullfn
+ '.orig_file')
666 if not process
.returncode
in (0, 1):
667 logwrite("Failed to diff to refresh %s" % print_name
)
669 os
.unlink(pfile
+ '.tmp')
672 os
.rename(pfile
+ '.tmp', pfile
)
674 # remove orig/rej files that patch sometimes creates
675 for root
, dirs
, files
in os
.walk(args
.outdir
):
677 if f
[-5:] == '.orig' or f
[-4:] == '.rej':
678 os
.unlink(os
.path
.join(root
, f
))
679 git_debug_snapshot(args
, "apply backport patch %s" % print_name
)
682 prefix_len
= len(os
.path
.join(source_dir
, 'patches')) + 1
683 for cocci_file
in sempatches
:
684 print_name
= cocci_file
[prefix_len
:]
686 logwrite("Applying patch %s" % print_name
)
688 process
= subprocess
.Popen(['spatch', '--sp-file', cocci_file
, '--in-place',
689 '--backup-suffix', '.cocci_backup', '--dir', '.'],
690 stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
,
691 close_fds
=True, universal_newlines
=True,
693 output
= process
.communicate()[0]
694 output
= output
.split('\n')
699 logwrite('> %s' % line
)
700 if process
.returncode
!= 0:
702 logwrite("Failed to apply changes from %s" % print_name
)
704 logwrite('> %s' % line
)
707 # remove cocci_backup files
708 for root
, dirs
, files
in os
.walk(args
.outdir
):
710 if f
.endswith('.cocci_backup'):
711 os
.unlink(os
.path
.join(root
, f
))
712 git_debug_snapshot(args
, "apply backport patch %s" % print_name
)
714 # some post-processing is required
715 configtree
= kconfig
.ConfigTree(os
.path
.join(args
.outdir
, 'Kconfig'))
716 logwrite('Modify Kconfig tree ...')
717 configtree
.prune_sources(ignore
=['Kconfig.kernel', 'Kconfig.versions'])
718 git_debug_snapshot(args
, "prune Kconfig tree")
719 configtree
.force_tristate_modular()
720 git_debug_snapshot(args
, "force tristate options modular")
721 configtree
.modify_selects()
722 git_debug_snapshot(args
, "convert select to depends on")
724 # write the versioning file
725 if git_tracked_version
:
726 backports_version
= "(see git)"
727 kernel_version
= "(see git)"
729 backports_version
= git
.describe(tree
=source_dir
, extra_args
=['--long'])
730 kernel_version
= git
.describe(rev
=args
.git_revision
or 'HEAD',
732 extra_args
=['--long'])
733 f
= open(os
.path
.join(args
.outdir
, 'versions'), 'w')
734 f
.write('BACKPORTS_VERSION="%s"\n' % backports_version
)
735 f
.write('BACKPORTED_KERNEL_VERSION="%s"\n' % kernel_version
)
736 f
.write('BACKPORTED_KERNEL_NAME="%s"\n' % args
.base_name
)
737 if git_tracked_version
:
738 f
.write('BACKPORTS_GIT_TRACKED="backport tracker ID: $(shell git rev-parse HEAD 2>/dev/null || echo \'not built in git tree\')"\n')
741 symbols
= configtree
.symbols()
743 # write local symbol list -- needed during build
744 f
= open(os
.path
.join(args
.outdir
, '.local-symbols'), 'w')
746 f
.write('%s=\n' % sym
)
749 git_debug_snapshot(args
, "add versions/symbols files")
751 logwrite('Rewrite Makefiles and Kconfig files ...')
753 # rewrite Makefile and source symbols
755 for some_symbols
in [symbols
[i
:i
+ 50] for i
in range(0, len(symbols
), 50)]:
756 r
= 'CONFIG_((' + '|'.join([s
+ '(_MODULE)?' for s
in some_symbols
]) + ')([^A-Za-z0-9_]|$))'
757 regexes
.append(re
.compile(r
, re
.MULTILINE
))
758 for root
, dirs
, files
in os
.walk(args
.outdir
):
759 # don't go into .git dir (possible debug thing)
763 data
= open(os
.path
.join(root
, f
), 'r').read()
765 data
= r
.sub(r
'CPTCFG_\1', data
)
766 data
= re
.sub(r
'\$\(srctree\)', '$(backport_srctree)', data
)
767 data
= re
.sub(r
'-Idrivers', '-I$(backport_srctree)/drivers', data
)
768 fo
= open(os
.path
.join(root
, f
), 'w')
772 git_debug_snapshot(args
, "rename config symbol / srctree usage")
774 # disable unbuildable Kconfig symbols and stuff Makefiles that doesn't exist
775 maketree
= make
.MakeTree(os
.path
.join(args
.outdir
, 'Makefile.kernel'))
777 disable_makefile
= []
778 for sym
in maketree
.get_impossible_symbols():
779 disable_kconfig
.append(sym
[7:])
780 disable_makefile
.append(sym
[7:])
782 configtree
.disable_symbols(disable_kconfig
)
783 git_debug_snapshot(args
, "disable impossible kconfig symbols")
785 # add kernel version dependencies to Kconfig, from the dependency list
787 for sym
in tuple(deplist
.keys()):
789 for dep
in deplist
[sym
]:
790 if "kconfig:" in dep
:
791 kconfig_expr
= dep
.replace('kconfig: ', '')
792 new
.append(kconfig_expr
)
793 elif (dep
== "DISABLE"):
794 new
.append('BACKPORT_DISABLED_KCONFIG_OPTION')
796 new
.append('!BACKPORT_KERNEL_%s' % dep
.replace('.', '_'))
798 configtree
.add_dependencies(deplist
)
799 git_debug_snapshot(args
, "add kernel version dependencies")
801 # disable things in makefiles that can't be selected and that the
802 # build shouldn't recurse into because they don't exist -- if we
803 # don't do that then a symbol from the kernel could cause the build
804 # to attempt to recurse and fail
806 # Note that we split the regex after 50 symbols, this is because of a
807 # limitation in the regex implementation (it only supports 100 nested
808 # groups -- 50 seemed safer and is still fast)
810 for some_symbols
in [disable_makefile
[i
:i
+ 50] for i
in range(0, len(disable_makefile
), 50)]:
811 r
= '^([^#].*((CPTCFG|CONFIG)_(' + '|'.join([s
for s
in some_symbols
]) + ')))'
812 regexes
.append(re
.compile(r
, re
.MULTILINE
))
813 for f
in maketree
.get_makefiles():
814 data
= open(f
, 'r').read()
816 data
= r
.sub(r
'#\1', data
)
820 git_debug_snapshot(args
, "disable unsatisfied Makefile parts")
822 if (args
.kup
or args
.kup_test
):
823 upload_release(args
, rel_prep
, logwrite
=logwrite
)
828 if __name__
== '__main__':