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
17 from lib
.tempdir
import tempdir
18 from lib
import bpreqs
as reqs
20 def read_copy_list(copyfile
):
22 Read a copy-list file and return a list of (source, target)
23 tuples. The source and target are usually the same, but in
24 the copy-list file there may be a rename included.
28 # remove leading/trailing whitespace
31 if not item
or item
[0] == '#':
34 raise Exception("Input path '%s' is absolute path, this isn't allowed" % (item
, ))
36 srcitem
, dstitem
= item
.split(' -> ')
37 if (srcitem
[-1] == '/') != (dstitem
[-1] == '/'):
38 raise Exception("Cannot copy file/dir to dir/file")
40 srcitem
= dstitem
= item
41 ret
.append((srcitem
, dstitem
))
45 def read_dependencies(depfilename
):
47 Read a (the) dependency file and return the list of
48 dependencies as a dictionary, mapping a Kconfig symbol
49 to a list of kernel version dependencies.
51 If a backported feature that an upstream backported driver
52 depends on had kconfig limitations (ie, debugging feature not
53 available) a built constaint restriction can be expressed
54 by using a kconfig expression. The kconfig expressions can
55 be specified by using the "kconfig: " prefix.
57 While reading ignore blank or commented lines.
60 depfile
= open(depfilename
, 'r')
64 if not item
or item
[0] == '#':
66 if "kconfig:" in item
:
67 sym
, kconfig_exp
= item
.split(" ", 1)
69 ret
[sym
] = [kconfig_exp
, ]
71 ret
[sym
].append(kconfig_exp
)
73 sym
, dep
= item
.split()
81 def check_output_dir(d
, clean
):
83 Check that the output directory doesn't exist or is empty,
84 unless clean is True in which case it's nuked. This helps
85 sanity check the output when generating a tree, so usually
86 running with --clean isn't suggested.
89 shutil
.rmtree(d
, ignore_errors
=True)
93 if e
.errno
!= errno
.ENOENT
:
97 def copytree(src
, dst
, symlinks
=False, ignore
=None):
99 Copy a directory tree. This differs from shutil.copytree()
100 in that it allows destination directories to already exist.
102 names
= os
.listdir(src
)
103 if ignore
is not None:
104 ignored_names
= ignore(src
, names
)
106 ignored_names
= set()
108 if not os
.path
.isdir(dst
):
112 if name
in ignored_names
:
114 srcname
= os
.path
.join(src
, name
)
115 dstname
= os
.path
.join(dst
, name
)
117 if symlinks
and os
.path
.islink(srcname
):
118 linkto
= os
.readlink(srcname
)
119 os
.symlink(linkto
, dstname
)
120 elif os
.path
.isdir(srcname
):
121 copytree(srcname
, dstname
, symlinks
, ignore
)
123 shutil
.copy2(srcname
, dstname
)
124 except (IOError, os
.error
) as why
:
125 errors
.append((srcname
, dstname
, str(why
)))
126 # catch the Error from the recursive copytree so that we can
127 # continue with other files
128 except shutil
.Error
as err
:
129 errors
.extend(err
.args
[0])
131 shutil
.copystat(src
, dst
)
133 # can't copy file access times on Windows
135 except OSError as why
:
136 errors
.extend((src
, dst
, str(why
)))
138 raise shutil
.Error(errors
)
141 def copy_files(srcpath
, copy_list
, outdir
):
143 Copy the copy_list files and directories from the srcpath
144 to the outdir. The copy_list contains source and target
147 For now, it also ignores any *~ editor backup files, though
148 this should probably be generalized (maybe using .gitignore?)
149 Similarly the code that only copies some files (*.c, *.h,
150 *.awk, Kconfig, Makefile) to avoid any build remnants in the
151 kernel if they should exist.
153 for srcitem
, tgtitem
in copy_list
:
155 copytree(srcpath
, outdir
, ignore
=shutil
.ignore_patterns('*~'))
156 elif tgtitem
[-1] == '/':
157 def copy_ignore(dir, entries
):
160 if i
[-2:] == '.o' or i
[-1] == '~':
163 copytree(os
.path
.join(srcpath
, srcitem
),
164 os
.path
.join(outdir
, tgtitem
),
168 os
.makedirs(os
.path
.join(outdir
, os
.path
.dirname(tgtitem
)))
170 # ignore dirs we might have created just now
171 if e
.errno
!= errno
.EEXIST
:
173 shutil
.copy(os
.path
.join(srcpath
, srcitem
),
174 os
.path
.join(outdir
, tgtitem
))
177 def copy_git_files(srcpath
, copy_list
, rev
, outdir
):
179 "Copy" files from a git repository. This really means listing them with
180 ls-tree and then using git show to obtain all the blobs.
182 for srcitem
, tgtitem
in copy_list
:
183 for m
, t
, h
, f
in git
.ls_tree(rev
=rev
, files
=(srcitem
,), tree
=srcpath
):
185 f
= os
.path
.join(outdir
, f
.replace(srcitem
, tgtitem
))
186 d
= os
.path
.dirname(f
)
187 if not os
.path
.exists(d
):
190 git
.get_blob(h
, outf
, tree
=srcpath
)
192 os
.chmod(f
, int(m
, 8))
194 def automatic_backport_mangle_c_file(name
):
195 return name
.replace('/', '-')
198 def add_automatic_backports(args
):
200 export
= re
.compile(r
'^EXPORT_SYMBOL(_GPL)?\((?P<sym>[^\)]*)\)')
201 bpi
= kconfig
.get_backport_info(os
.path
.join(args
.outdir
, 'compat', 'Kconfig'))
202 configtree
= kconfig
.ConfigTree(os
.path
.join(args
.outdir
, 'Kconfig'))
203 all_selects
= configtree
.all_selects()
204 for sym
, vals
in bpi
.items():
205 if sym
.startswith('BACKPORT_BUILD_'):
206 if not sym
[15:] in all_selects
:
207 disable_list
.append(sym
)
209 symtype
, module_name
, c_files
, h_files
= vals
214 files
.append((f
, os
.path
.join('compat', automatic_backport_mangle_c_file(f
))))
216 files
.append((os
.path
.join('include', f
),
217 os
.path
.join('include', os
.path
.dirname(f
), 'backport-' + os
.path
.basename(f
))))
218 if args
.git_revision
:
219 copy_git_files(args
.kerneldir
, files
, args
.git_revision
, args
.outdir
)
221 copy_files(args
.kerneldir
, files
, args
.outdir
)
223 # now add the Makefile line
224 mf
= open(os
.path
.join(args
.outdir
, 'compat', 'Makefile'), 'a+')
225 o_files
= [automatic_backport_mangle_c_file(f
)[:-1] + 'o' for f
in c_files
]
226 if symtype
== 'tristate':
228 raise Exception('backporting a module requires a #module-name')
230 mf
.write('%s-objs += %s\n' % (module_name
, of
))
231 mf
.write('obj-$(CPTCFG_%s) += %s.o\n' % (sym
, module_name
))
232 elif symtype
== 'bool':
233 mf
.write('compat-$(CPTCFG_%s) += %s\n' % (sym
, ' '.join(o_files
)))
235 # finally create the include file
238 for l
in open(os
.path
.join(args
.outdir
, 'compat',
239 automatic_backport_mangle_c_file(f
)), 'r'):
242 syms
.append(m
.group('sym'))
244 outf
= open(os
.path
.join(args
.outdir
, 'include', f
), 'w')
245 outf
.write('/* Automatically created during backport process */\n')
246 outf
.write('#ifndef CPTCFG_%s\n' % sym
)
247 outf
.write('#include_next <%s>\n' % f
)
248 outf
.write('#else\n');
250 outf
.write('#undef %s\n' % s
)
251 outf
.write('#define %s LINUX_BACKPORT(%s)\n' % (s
, s
))
252 outf
.write('#include <%s>\n' % (os
.path
.dirname(f
) + '/backport-' + os
.path
.basename(f
), ))
253 outf
.write('#endif /* CPTCFG_%s */\n' % sym
)
256 def git_debug_init(args
):
258 Initialize a git repository in the output directory and commit the current
259 code in it. This is only used for debugging the transformations this code
260 will do to the output later.
262 if not args
.gitdebug
:
264 git
.init(tree
=args
.outdir
)
265 git
.commit_all("Copied backport", tree
=args
.outdir
)
268 def git_debug_snapshot(args
, name
):
270 Take a git snapshot for the debugging.
272 if not args
.gitdebug
:
274 git
.commit_all(name
, tree
=args
.outdir
)
276 def get_rel_spec_stable(rel
):
278 Returns release specs for a linux-stable backports based release.
281 m
= re
.match(r
"(?P<VERSION>\d+)\.+" \
282 "(?P<PATCHLEVEL>\d+)[.]*" \
283 "(?P<SUBLEVEL>\d*)" \
284 "[-rc]+(?P<RC_VERSION>\d+)\-+" \
285 "(?P<RELMOD_UPDATE>\d+)[-]*" \
286 "(?P<RELMOD_TYPE>[usnpc]*)", \
289 m
= re
.match(r
"(?P<VERSION>\d+)\.+" \
290 "(?P<PATCHLEVEL>\d+)[.]*" \
291 "(?P<SUBLEVEL>\d*)\-+" \
292 "(?P<RELMOD_UPDATE>\d+)[-]*" \
293 "(?P<RELMOD_TYPE>[usnpc]*)", \
299 def get_rel_spec_next(rel
):
301 Returns release specs for a linux-next backports based release.
303 m
= re
.match(r
"(?P<DATE_VERSION>\d+)[-]*" \
304 "(?P<RELMOD_UPDATE>\d*)[-]*" \
305 "(?P<RELMOD_TYPE>[usnpc]*)", \
311 def get_rel_prep(rel
):
313 Returns a dict with prep work details we need prior to
314 uploading a backports release to kernel.org
316 rel_specs
= get_rel_spec_stable(rel
)
321 rel_specs
= get_rel_spec_next(rel
)
323 sys
.stdout
.write("rel: %s\n" % rel
)
325 if (rel_specs
['RELMOD_UPDATE'] == '0' or
326 rel_specs
['RELMOD_UPDATE'] == '1'):
329 date
= rel_specs
['DATE_VERSION']
334 if (len(month
) != 2):
342 rel_tag
= "backports-" + rel
.replace(rel_specs
['RELMOD_TYPE'], "")
345 if (not rel_specs
['RELMOD_UPDATE']):
347 if (rel_specs
['RELMOD_UPDATE'] == '0'):
349 ignore
+= rel_specs
['RELMOD_UPDATE']
350 if (rel_specs
['RELMOD_TYPE'] != ''):
351 ignore
+= rel_specs
['RELMOD_TYPE']
352 base_rel
= rel
.replace(ignore
, "")
353 paths
.append("v" + base_rel
)
354 rel_tag
= "v" + rel
.replace(rel_specs
['RELMOD_TYPE'], "")
356 rel_prep
= dict(stable
= is_stable
,
357 expected_tag
= rel_tag
,
358 paths_to_create
= paths
)
361 def create_tar_and_gz(tar_name
, dir_to_tar
):
363 We need both a tar file and gzip for kernel.org, the tar file
364 gets signed, then we upload the compressed version, kup-server
365 in the backend decompresses and verifies the tarball against
368 basename
= os
.path
.basename(dir_to_tar
)
369 tar
= tarfile
.open(tar_name
, "w")
370 tar
.add(dir_to_tar
, basename
)
373 tar_file
= open(tar_name
, "r")
375 gz_file
= gzip
.GzipFile(tar_name
+ ".gz", 'wb')
376 gz_file
.write(tar_file
.read())
379 def upload_release(args
, rel_prep
, logwrite
=lambda x
:None):
381 Given a path of a relase make tarball out of it, PGP sign it, and
382 then upload it to kernel.org using kup.
384 The linux-next based release do not require a RELMOD_UPDATE
385 given that typically only one release is made per day. Using
386 RELMOD_UPDATE for these releases is allowed though and if
387 present it must be > 1.
389 The linux-stable based releases require a RELMOD_UPDATE.
391 RELMOD_UPDATE must be numeric and > 0 just as the RC releases
394 The tree must also be tagged with the respective release, without
395 the RELMOD_TYPE. For linux-next based releases this consists of
396 backports- followed by DATE_VERSION and if RELMOD_TYPE is present.
397 For linux-stable releases this consists of v followed by the
398 full release version except the RELMOD_TYPE.
400 Uploads will not be allowed if these rules are not followed.
402 korg_path
= "/pub/linux/kernel/projects/backports"
404 if (rel_prep
['stable']):
405 korg_path
+= "/stable"
407 parent
= os
.path
.dirname(args
.outdir
)
408 release
= os
.path
.basename(args
.outdir
)
409 tar_name
= parent
+ '/' + release
+ ".tar"
410 gzip_name
= tar_name
+ ".gz"
412 create_tar_and_gz(tar_name
, args
.outdir
)
414 logwrite(gpg
.sign(tar_name
, extra_args
=['--armor', '--detach-sign']))
416 logwrite("------------------------------------------------------")
418 if (not args
.kup_test
):
419 logwrite("About to upload, current target path contents:")
421 logwrite("kup-test: current target path contents:")
423 logwrite(kup
.ls(path
=korg_path
))
425 for path
in rel_prep
['paths_to_create']:
426 korg_path
+= '/' + path
427 if (not args
.kup_test
):
428 logwrite("create directory: %s" % korg_path
)
429 logwrite(kup
.mkdir(korg_path
))
431 if (not args
.kup_test
):
432 logwrite("upload file %s to %s" % (gzip_name
, korg_path
))
433 logwrite(kup
.put(gzip_name
, tar_name
+ '.asc', korg_path
))
434 logwrite("\nFinished upload!\n")
435 logwrite("Target path contents:")
436 logwrite(kup
.ls(path
=korg_path
))
438 kup_cmd
= "kup put /\n\t\t%s /\n\t\t%s /\n\t\t%s" % (gzip_name
, tar_name
+ '.asc', korg_path
)
439 logwrite("kup-test: skipping cmd: %s" % kup_cmd
)
442 # Our binary requirements go here
445 req
.coccinelle('1.0.0-rc21')
446 if not req
.reqs_match():
449 # set up and parse arguments
450 parser
= argparse
.ArgumentParser(description
='generate backport tree')
451 parser
.add_argument('kerneldir', metavar
='<kernel tree>', type=str,
452 help='Kernel tree to copy drivers from')
453 parser
.add_argument('outdir', metavar
='<output directory>', type=str,
454 help='Directory to write the generated tree to')
455 parser
.add_argument('--copy-list', metavar
='<listfile>', type=argparse
.FileType('r'),
457 help='File containing list of files/directories to copy, default "copy-list"')
458 parser
.add_argument('--git-revision', metavar
='<revision>', type=str,
459 help='git commit revision (see gitrevisions(7)) to take objects from.' +
460 'If this is specified, the kernel tree is used as git object storage ' +
461 'and we use git ls-tree to get the files.')
462 parser
.add_argument('--clean', const
=True, default
=False, action
="store_const",
463 help='Clean output directory instead of erroring if it isn\'t empty')
464 parser
.add_argument('--refresh', const
=True, default
=False, action
="store_const",
465 help='Refresh patches as they are applied, the source dir will be modified!')
466 parser
.add_argument('--base-name', metavar
='<name>', type=str, default
='Linux',
467 help='name of base tree, default just "Linux"')
468 parser
.add_argument('--gitdebug', const
=True, default
=False, action
="store_const",
469 help='Use git, in the output tree, to debug the various transformation steps ' +
470 'that the tree generation makes (apply patches, ...)')
471 parser
.add_argument('--verbose', const
=True, default
=False, action
="store_const",
472 help='Print more verbose information')
473 parser
.add_argument('--extra-driver', nargs
=2, metavar
=('<source dir>', '<copy-list>'), type=str,
474 action
='append', default
=[], help='Extra driver directory/copy-list.')
475 parser
.add_argument('--kup', const
=True, default
=False, action
="store_const",
476 help='For maintainers: upload a release to kernel.org')
477 parser
.add_argument('--kup-test', const
=True, default
=False, action
="store_const",
478 help='For maintainers: do all the work as if you were about to ' +
479 'upload to kernel.org but do not do the final `kup put` ' +
480 'and also do not run any `kup mkdir` commands. This will ' +
481 'however run `kup ls` on the target paths so ' +
482 'at the very least we test your kup configuration. ' +
483 'If this is your first time uploading use this first!')
484 parser
.add_argument('--test-cocci', metavar
='<sp_file>', type=str, default
=None,
485 help='Only use the cocci file passed for Coccinelle, don\'t do anything else, ' +
486 'also creates a git repo on the target directory for easy inspection ' +
487 'of changes done by Coccinelle.')
488 parser
.add_argument('--profile-cocci', metavar
='<sp_file>', type=str, default
=None,
489 help='Only use the cocci file passed and pass --profile to Coccinelle, ' +
490 'also creates a git repo on the target directory for easy inspection ' +
491 'of changes done by Coccinelle.')
492 args
= parser
.parse_args()
495 sys
.stdout
.write(msg
)
496 sys
.stdout
.write('\n')
499 return process(args
.kerneldir
, args
.outdir
, args
.copy_list
,
500 git_revision
=args
.git_revision
, clean
=args
.clean
,
501 refresh
=args
.refresh
, base_name
=args
.base_name
,
502 gitdebug
=args
.gitdebug
, verbose
=args
.verbose
,
503 extra_driver
=args
.extra_driver
,
505 kup_test
=args
.kup_test
,
506 test_cocci
=args
.test_cocci
,
507 profile_cocci
=args
.profile_cocci
,
510 def process(kerneldir
, outdir
, copy_list_file
, git_revision
=None,
511 clean
=False, refresh
=False, base_name
="Linux", gitdebug
=False,
512 verbose
=False, extra_driver
=[], kup
=False,
516 logwrite
=lambda x
:None,
517 git_tracked_version
=False):
519 def __init__(self
, kerneldir
, outdir
, copy_list_file
,
520 git_revision
, clean
, refresh
, base_name
,
521 gitdebug
, verbose
, extra_driver
, kup
,
525 self
.kerneldir
= kerneldir
527 self
.copy_list
= copy_list_file
528 self
.git_revision
= git_revision
530 self
.refresh
= refresh
531 self
.base_name
= base_name
532 self
.gitdebug
= gitdebug
533 self
.verbose
= verbose
534 self
.extra_driver
= extra_driver
536 self
.kup_test
= kup_test
537 self
.test_cocci
= test_cocci
538 self
.profile_cocci
= profile_cocci
539 if self
.test_cocci
or self
.profile_cocci
:
541 def git_paranoia(tree
=None, logwrite
=lambda x
:None):
542 data
= git
.paranoia(tree
)
544 logwrite('Cannot use %s' % tree
)
545 logwrite('%s' % data
['output'])
548 logwrite('Validated tree: %s' % tree
)
550 args
= Args(kerneldir
, outdir
, copy_list_file
,
551 git_revision
, clean
, refresh
, base_name
,
552 gitdebug
, verbose
, extra_driver
, kup
, kup_test
,
553 test_cocci
, profile_cocci
)
556 # start processing ...
557 if (args
.kup
or args
.kup_test
):
558 git_paranoia(source_dir
, logwrite
)
559 git_paranoia(kerneldir
, logwrite
)
561 rel_describe
= git
.describe(rev
=None, tree
=source_dir
, extra_args
=['--dirty'])
562 release
= os
.path
.basename(args
.outdir
)
563 version
= release
.replace("backports-", "")
565 rel_prep
= get_rel_prep(version
)
567 logwrite('Invalid backports release name: %s' % release
)
568 logwrite('For rules on the release name see upload_release()')
570 rel_type
= "linux-stable"
571 if (not rel_prep
['stable']):
572 rel_type
= "linux-next"
573 if (rel_prep
['expected_tag'] != rel_describe
):
574 logwrite('Unexpected %s based backports release tag on' % rel_type
)
575 logwrite('the backports tree tree: %s\n' % rel_describe
)
576 logwrite('You asked to make a release with this ')
577 logwrite('directory name: %s' % release
)
578 logwrite('The actual expected tag we should find on')
579 logwrite('the backports tree then is: %s\n' % rel_prep
['expected_tag'])
580 logwrite('For rules on the release name see upload_release()')
583 copy_list
= read_copy_list(args
.copy_list
)
584 deplist
= read_dependencies(os
.path
.join(source_dir
, 'dependencies'))
586 # validate output directory
587 check_output_dir(args
.outdir
, args
.clean
)
590 backport_files
= [(x
, x
) for x
in [
591 'Kconfig', 'Makefile', 'Makefile.build', 'Makefile.kernel', '.gitignore',
592 'Makefile.real', 'compat/', 'backport-include/', 'kconf/',
593 'scripts/', '.blacklist.map',
595 if not args
.git_revision
:
596 logwrite('Copy original source files ...')
598 logwrite('Get original source files from git ...')
600 copy_files(os
.path
.join(source_dir
, 'backport'), backport_files
, args
.outdir
)
604 if not args
.git_revision
:
605 copy_files(args
.kerneldir
, copy_list
, args
.outdir
)
607 copy_git_files(args
.kerneldir
, copy_list
, args
.git_revision
, args
.outdir
)
609 # FIXME: should we add a git version of this (e.g. --git-extra-driver)?
610 for src
, copy_list
in args
.extra_driver
:
611 if (args
.kup
or args
.kup_test
):
613 copy_files(src
, read_copy_list(open(copy_list
, 'r')), args
.outdir
)
615 git_debug_snapshot(args
, 'Add driver sources')
617 disable_list
= add_automatic_backports(args
)
619 bpcfg
= kconfig
.ConfigTree(os
.path
.join(args
.outdir
, 'compat', 'Kconfig'))
620 bpcfg
.disable_symbols(disable_list
)
621 git_debug_snapshot(args
, 'Add automatic backports')
623 test_cocci
= args
.test_cocci
or args
.profile_cocci
625 logwrite('Apply patches ...')
628 for root
, dirs
, files
in os
.walk(os
.path
.join(source_dir
, 'patches')):
630 if not test_cocci
and f
.endswith('.patch'):
631 patches
.append(os
.path
.join(root
, f
))
632 if f
.endswith('.cocci'):
634 if f
not in test_cocci
:
637 logwrite("Testing Coccinelle SmPL patch: %s" % test_cocci
)
638 elif args
.profile_cocci
:
639 logwrite("Profiling Coccinelle SmPL patch: %s" % test_cocci
)
640 sempatches
.append(os
.path
.join(root
, f
))
642 prefix_len
= len(os
.path
.join(source_dir
, 'patches')) + 1
643 for pfile
in patches
:
644 print_name
= pfile
[prefix_len
:]
645 # read the patch file
646 p
= patch
.fromfile(pfile
)
647 # complain if it's not a patch
649 raise Exception('No patch content found in %s' % print_name
)
650 # leading / seems to be stripped?
651 if 'dev/null' in p
.items
[0].source
:
652 raise Exception('Patches creating files are not supported (in %s)' % print_name
)
653 # check if the first file the patch touches exists, if so
654 # assume the patch needs to be applied -- otherwise continue
655 patched_file
= '/'.join(p
.items
[0].source
.split('/')[1:])
656 fullfn
= os
.path
.join(args
.outdir
, patched_file
)
657 if not os
.path
.exists(fullfn
):
659 logwrite("Not applying %s, not needed" % print_name
)
662 logwrite("Applying patch %s" % print_name
)
665 # but for refresh, of course look at all files the patch touches
666 for patchitem
in p
.items
:
667 patched_file
= '/'.join(patchitem
.source
.split('/')[1:])
668 fullfn
= os
.path
.join(args
.outdir
, patched_file
)
669 shutil
.copyfile(fullfn
, fullfn
+ '.orig_file')
671 process
= subprocess
.Popen(['patch', '-p1'], stdout
=subprocess
.PIPE
,
672 stderr
=subprocess
.STDOUT
, stdin
=subprocess
.PIPE
,
673 close_fds
=True, universal_newlines
=True,
675 output
= process
.communicate(input=open(pfile
, 'r').read())[0]
676 output
= output
.split('\n')
681 logwrite('> %s' % line
)
682 if process
.returncode
!= 0:
684 logwrite("Failed to apply changes from %s" % print_name
)
686 logwrite('> %s' % line
)
690 pfilef
= open(pfile
+ '.tmp', 'a')
691 pfilef
.write(p
.top_header
)
693 for patchitem
in p
.items
:
694 patched_file
= '/'.join(patchitem
.source
.split('/')[1:])
695 fullfn
= os
.path
.join(args
.outdir
, patched_file
)
696 process
= subprocess
.Popen(['diff', '-p', '-u', patched_file
+ '.orig_file', patched_file
,
697 '--label', 'a/' + patched_file
,
698 '--label', 'b/' + patched_file
],
699 stdout
=pfilef
, close_fds
=True,
700 universal_newlines
=True, cwd
=args
.outdir
)
702 os
.unlink(fullfn
+ '.orig_file')
703 if not process
.returncode
in (0, 1):
704 logwrite("Failed to diff to refresh %s" % print_name
)
706 os
.unlink(pfile
+ '.tmp')
709 os
.rename(pfile
+ '.tmp', pfile
)
711 # remove orig/rej files that patch sometimes creates
712 for root
, dirs
, files
in os
.walk(args
.outdir
):
714 if f
[-5:] == '.orig' or f
[-4:] == '.rej':
715 os
.unlink(os
.path
.join(root
, f
))
716 git_debug_snapshot(args
, "apply backport patch %s" % print_name
)
719 prefix_len
= len(os
.path
.join(source_dir
, 'patches')) + 1
721 for cocci_file
in sempatches
:
722 # Until Coccinelle picks this up
723 pycocci
= os
.path
.join(source_dir
, 'devel/pycocci')
724 cmd
= [pycocci
, cocci_file
]
725 extra_spatch_args
= []
726 if args
.profile_cocci
:
727 cmd
.append('--profile-cocci')
728 cmd
.append(args
.outdir
)
729 print_name
= cocci_file
[prefix_len
:]
731 logwrite("Applying SmPL patch %s" % print_name
)
732 sprocess
= subprocess
.Popen(cmd
,
733 stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
,
734 close_fds
=True, universal_newlines
=True,
736 output
= sprocess
.communicate()[0]
738 if sprocess
.returncode
!= 0:
739 logwrite("Failed to process SmPL patch %s" % print_name
)
741 output
= output
.split('\n')
746 logwrite('> %s' % line
)
748 # remove cocci_backup files
749 for root
, dirs
, files
in os
.walk(args
.outdir
):
751 if f
.endswith('.cocci_backup'):
752 os
.unlink(os
.path
.join(root
, f
))
753 git_debug_snapshot(args
, "apply backport SmPL patch %s" % print_name
)
759 # some post-processing is required
760 configtree
= kconfig
.ConfigTree(os
.path
.join(args
.outdir
, 'Kconfig'))
761 logwrite('Modify Kconfig tree ...')
762 configtree
.prune_sources(ignore
=['Kconfig.kernel', 'Kconfig.versions'])
763 git_debug_snapshot(args
, "prune Kconfig tree")
764 configtree
.force_tristate_modular()
765 git_debug_snapshot(args
, "force tristate options modular")
766 configtree
.modify_selects()
767 git_debug_snapshot(args
, "convert select to depends on")
769 # write the versioning file
770 if git_tracked_version
:
771 backports_version
= "(see git)"
772 kernel_version
= "(see git)"
774 backports_version
= git
.describe(tree
=source_dir
, extra_args
=['--long'])
775 kernel_version
= git
.describe(rev
=args
.git_revision
or 'HEAD',
777 extra_args
=['--long'])
778 f
= open(os
.path
.join(args
.outdir
, 'versions'), 'w')
779 f
.write('BACKPORTS_VERSION="%s"\n' % backports_version
)
780 f
.write('BACKPORTED_KERNEL_VERSION="%s"\n' % kernel_version
)
781 f
.write('BACKPORTED_KERNEL_NAME="%s"\n' % args
.base_name
)
782 if git_tracked_version
:
783 f
.write('BACKPORTS_GIT_TRACKED="backport tracker ID: $(shell git rev-parse HEAD 2>/dev/null || echo \'not built in git tree\')"\n')
786 symbols
= configtree
.symbols()
788 # write local symbol list -- needed during build
789 f
= open(os
.path
.join(args
.outdir
, '.local-symbols'), 'w')
791 f
.write('%s=\n' % sym
)
794 git_debug_snapshot(args
, "add versions/symbols files")
796 # add defconfigs that we want
797 defconfigs_dir
= os
.path
.join(source_dir
, 'backport', 'defconfigs')
798 os
.mkdir(os
.path
.join(args
.outdir
, 'defconfigs'))
799 for dfbase
in os
.listdir(defconfigs_dir
):
800 copy_defconfig
= True
801 dfsrc
= os
.path
.join(defconfigs_dir
, dfbase
)
802 for line
in open(dfsrc
, 'r'):
807 if sym
+ '=' in line
:
811 copy_defconfig
= False
814 shutil
.copy(dfsrc
, os
.path
.join(args
.outdir
, 'defconfigs', dfbase
))
816 git_debug_snapshot(args
, "add (useful) defconfig files")
818 logwrite('Rewrite Makefiles and Kconfig files ...')
820 # rewrite Makefile and source symbols
822 for some_symbols
in [symbols
[i
:i
+ 50] for i
in range(0, len(symbols
), 50)]:
823 r
= 'CONFIG_((' + '|'.join([s
+ '(_MODULE)?' for s
in some_symbols
]) + ')([^A-Za-z0-9_]|$))'
824 regexes
.append(re
.compile(r
, re
.MULTILINE
))
825 for root
, dirs
, files
in os
.walk(args
.outdir
):
826 # don't go into .git dir (possible debug thing)
830 data
= open(os
.path
.join(root
, f
), 'r').read()
832 data
= r
.sub(r
'CPTCFG_\1', data
)
833 data
= re
.sub(r
'\$\(srctree\)', '$(backport_srctree)', data
)
834 data
= re
.sub(r
'-Idrivers', '-I$(backport_srctree)/drivers', data
)
835 fo
= open(os
.path
.join(root
, f
), 'w')
839 git_debug_snapshot(args
, "rename config symbol / srctree usage")
841 # disable unbuildable Kconfig symbols and stuff Makefiles that doesn't exist
842 maketree
= make
.MakeTree(os
.path
.join(args
.outdir
, 'Makefile.kernel'))
844 disable_makefile
= []
845 for sym
in maketree
.get_impossible_symbols():
846 disable_kconfig
.append(sym
[7:])
847 disable_makefile
.append(sym
[7:])
849 configtree
.disable_symbols(disable_kconfig
)
850 git_debug_snapshot(args
, "disable impossible kconfig symbols")
852 # add kernel version dependencies to Kconfig, from the dependency list
854 for sym
in tuple(deplist
.keys()):
856 for dep
in deplist
[sym
]:
857 if "kconfig:" in dep
:
858 kconfig_expr
= dep
.replace('kconfig: ', '')
859 new
.append(kconfig_expr
)
860 elif (dep
== "DISABLE"):
861 new
.append('BACKPORT_DISABLED_KCONFIG_OPTION')
863 new
.append('!BACKPORT_KERNEL_%s' % dep
.replace('.', '_'))
865 configtree
.add_dependencies(deplist
)
866 git_debug_snapshot(args
, "add kernel version dependencies")
868 # disable things in makefiles that can't be selected and that the
869 # build shouldn't recurse into because they don't exist -- if we
870 # don't do that then a symbol from the kernel could cause the build
871 # to attempt to recurse and fail
873 # Note that we split the regex after 50 symbols, this is because of a
874 # limitation in the regex implementation (it only supports 100 nested
875 # groups -- 50 seemed safer and is still fast)
877 for some_symbols
in [disable_makefile
[i
:i
+ 50] for i
in range(0, len(disable_makefile
), 50)]:
878 r
= '^([^#].*((CPTCFG|CONFIG)_(' + '|'.join([s
for s
in some_symbols
]) + ')))'
879 regexes
.append(re
.compile(r
, re
.MULTILINE
))
880 for f
in maketree
.get_makefiles():
881 data
= open(f
, 'r').read()
883 data
= r
.sub(r
'#\1', data
)
887 git_debug_snapshot(args
, "disable unsatisfied Makefile parts")
889 if (args
.kup
or args
.kup_test
):
892 if not req
.reqs_match():
894 upload_release(args
, rel_prep
, logwrite
=logwrite
)
899 if __name__
== '__main__':