3 # Generate the output tree into a specified directory.
6 import argparse
, sys
, os
, errno
, shutil
, re
, subprocess
8 from multiprocessing
import cpu_count
11 source_dir
= os
.path
.abspath(os
.path
.dirname(__file__
))
12 sys
.path
.append(source_dir
)
13 # and import libraries we have
14 from lib
import kconfig
, patch
, make
15 from lib
import bpgit
as git
16 from lib
import bpgpg
as gpg
17 from lib
import bpkup
as kup
18 from lib
.tempdir
import tempdir
19 from lib
import bpreqs
as reqs
20 from lib
import bpversion
as gen_version
22 class Bp_Identity(object):
24 folks considering multiple integrations may want to
25 consider stuffing versioning info here as well but
26 that will need thought/design on sharing compat and
29 Use the *_resafe when combining on regexps, although we currently
30 don't support regexps there perhaps later we will and this will
31 just make things safer for the output regardless. Once and if those
32 are added, how we actually use the others for regular printing will
33 need to be considered.
35 def __init__(self
, integrate
=False, kconfig_prefix
='CPTCFG_',
36 project_prefix
='', project_dir
='',
37 target_dir
='', target_dir_name
='',
38 kconfig_source_var
=None):
39 self
.integrate
= integrate
40 self
.kconfig_prefix
= kconfig_prefix
41 self
.kconfig_prefix_resafe
= re
.escape(kconfig_prefix
)
42 self
.project_prefix
= project_prefix
43 self
.project_prefix_resafe
= re
.escape(project_prefix
)
44 self
.full_prefix
= kconfig_prefix
+ project_prefix
45 self
.full_prefix_resafe
= re
.escape(self
.full_prefix
)
46 self
.project_dir
= project_dir
47 self
.target_dir
= target_dir
48 self
.target_dir_name
= target_dir_name
49 self
.kconfig_source_var
= kconfig_source_var
50 if self
.kconfig_source_var
:
51 self
.kconfig_source_var_resafe
= re
.escape(self
.kconfig_source_var
)
53 self
.kconfig_source_var_resafe
= None
55 def read_copy_list(copyfile
):
57 Read a copy-list file and return a list of (source, target)
58 tuples. The source and target are usually the same, but in
59 the copy-list file there may be a rename included.
63 # remove leading/trailing whitespace
66 if not item
or item
[0] == '#':
69 raise Exception("Input path '%s' is absolute path, this isn't allowed" % (item
, ))
71 srcitem
, dstitem
= item
.split(' -> ')
72 if (srcitem
[-1] == '/') != (dstitem
[-1] == '/'):
73 raise Exception("Cannot copy file/dir to dir/file")
75 srcitem
= dstitem
= item
76 ret
.append((srcitem
, dstitem
))
80 def read_dependencies(depfilename
):
82 Read a (the) dependency file and return the list of
83 dependencies as a dictionary, mapping a Kconfig symbol
84 to a list of kernel version dependencies.
86 If a backported feature that an upstream backported driver
87 depends on had kconfig limitations (ie, debugging feature not
88 available) a built constaint restriction can be expressed
89 by using a kconfig expression. The kconfig expressions can
90 be specified by using the "kconfig: " prefix.
92 While reading ignore blank or commented lines.
95 depfile
= open(depfilename
, 'r')
99 if not item
or item
[0] == '#':
101 if "kconfig:" in item
:
102 sym
, kconfig_exp
= item
.split(" ", 1)
104 ret
[sym
] = [kconfig_exp
, ]
106 ret
[sym
].append(kconfig_exp
)
108 sym
, dep
= item
.split()
116 def check_output_dir(d
, clean
):
118 Check that the output directory doesn't exist or is empty,
119 unless clean is True in which case it's nuked. This helps
120 sanity check the output when generating a tree, so usually
121 running with --clean isn't suggested.
124 shutil
.rmtree(d
, ignore_errors
=True)
128 if e
.errno
!= errno
.ENOENT
:
132 def copytree(src
, dst
, symlinks
=False, ignore
=None):
134 Copy a directory tree. This differs from shutil.copytree()
135 in that it allows destination directories to already exist.
137 names
= os
.listdir(src
)
138 if ignore
is not None:
139 ignored_names
= ignore(src
, names
)
141 ignored_names
= set()
143 if not os
.path
.isdir(dst
):
147 if name
in ignored_names
:
149 srcname
= os
.path
.join(src
, name
)
150 dstname
= os
.path
.join(dst
, name
)
152 if symlinks
and os
.path
.islink(srcname
):
153 linkto
= os
.readlink(srcname
)
154 os
.symlink(linkto
, dstname
)
155 elif os
.path
.isdir(srcname
):
156 copytree(srcname
, dstname
, symlinks
, ignore
)
158 shutil
.copy2(srcname
, dstname
)
159 except (IOError, os
.error
) as why
:
160 errors
.append((srcname
, dstname
, str(why
)))
161 # catch the Error from the recursive copytree so that we can
162 # continue with other files
163 except shutil
.Error
as err
:
164 errors
.extend(err
.args
[0])
166 shutil
.copystat(src
, dst
)
168 # can't copy file access times on Windows
170 except OSError as why
:
171 errors
.extend((src
, dst
, str(why
)))
173 raise shutil
.Error(errors
)
176 def copy_files(srcpath
, copy_list
, outdir
):
178 Copy the copy_list files and directories from the srcpath
179 to the outdir. The copy_list contains source and target
182 For now, it also ignores any *~ editor backup files, though
183 this should probably be generalized (maybe using .gitignore?)
184 Similarly the code that only copies some files (*.c, *.h,
185 *.awk, Kconfig, Makefile) to avoid any build remnants in the
186 kernel if they should exist.
188 for srcitem
, tgtitem
in copy_list
:
190 copytree(srcpath
, outdir
, ignore
=shutil
.ignore_patterns('*~'))
191 elif tgtitem
[-1] == '/':
192 def copy_ignore(dir, entries
):
195 if i
[-2:] == '.o' or i
[-1] == '~':
198 copytree(os
.path
.join(srcpath
, srcitem
),
199 os
.path
.join(outdir
, tgtitem
),
203 os
.makedirs(os
.path
.join(outdir
, os
.path
.dirname(tgtitem
)))
205 # ignore dirs we might have created just now
206 if e
.errno
!= errno
.EEXIST
:
208 shutil
.copy(os
.path
.join(srcpath
, srcitem
),
209 os
.path
.join(outdir
, tgtitem
))
212 def copy_git_files(srcpath
, copy_list
, rev
, outdir
):
214 "Copy" files from a git repository. This really means listing them with
215 ls-tree and then using git show to obtain all the blobs.
217 for srcitem
, tgtitem
in copy_list
:
218 for m
, t
, h
, f
in git
.ls_tree(rev
=rev
, files
=(srcitem
,), tree
=srcpath
):
220 f
= os
.path
.join(outdir
, f
.replace(srcitem
, tgtitem
))
221 d
= os
.path
.dirname(f
)
222 if not os
.path
.exists(d
):
225 git
.get_blob(h
, outf
, tree
=srcpath
)
227 os
.chmod(f
, int(m
, 8))
229 def automatic_backport_mangle_c_file(name
):
230 return name
.replace('/', '-')
233 def add_automatic_backports(args
):
235 export
= re
.compile(r
'^EXPORT_SYMBOL(_GPL)?\((?P<sym>[^\)]*)\)')
236 bpi
= kconfig
.get_backport_info(os
.path
.join(args
.bpid
.target_dir
, 'compat', 'Kconfig'))
237 configtree
= kconfig
.ConfigTree(os
.path
.join(args
.bpid
.target_dir
, 'Kconfig'), args
.bpid
)
238 ignore
=['Kconfig.kernel', 'Kconfig.versions']
239 configtree
.verify_sources(ignore
=ignore
)
240 git_debug_snapshot(args
, "verify sources for automatic backports")
241 all_selects
= configtree
.all_selects()
242 for sym
, vals
in bpi
.items():
243 if sym
.startswith('BPAUTO_BUILD_'):
244 if not sym
[13:] in all_selects
:
245 disable_list
.append(sym
)
247 symtype
, module_name
, c_files
, h_files
= vals
252 files
.append((f
, os
.path
.join('compat', automatic_backport_mangle_c_file(f
))))
254 files
.append((os
.path
.join('include', f
),
255 os
.path
.join('include', os
.path
.dirname(f
), 'backport-' + os
.path
.basename(f
))))
256 if args
.git_revision
:
257 copy_git_files(args
.kerneldir
, files
, args
.git_revision
, args
.bpid
.target_dir
)
259 copy_files(args
.kerneldir
, files
, args
.bpid
.target_dir
)
261 # now add the Makefile line
262 mf
= open(os
.path
.join(args
.bpid
.target_dir
, 'compat', 'Makefile'), 'a+')
263 o_files
= [automatic_backport_mangle_c_file(f
)[:-1] + 'o' for f
in c_files
]
264 if symtype
== 'tristate':
266 raise Exception('backporting a module requires a #module-name')
268 mf
.write('%s-objs += %s\n' % (module_name
, of
))
269 mf
.write('obj-$(%s%s) += %s.o\n' % (args
.bpid
.full_prefix
, sym
, module_name
))
270 elif symtype
== 'bool':
271 mf
.write('compat-$(%s%s) += %s\n' % (args
.bpid
.full_prefix
, sym
, ' '.join(o_files
)))
273 # finally create the include file
276 for l
in open(os
.path
.join(args
.bpid
.target_dir
, 'compat',
277 automatic_backport_mangle_c_file(f
)), 'r'):
280 syms
.append(m
.group('sym'))
282 outf
= open(os
.path
.join(args
.bpid
.target_dir
, 'include', f
), 'w')
283 outf
.write('/* Automatically created during backport process */\n')
284 outf
.write('#ifndef %s%s\n' % (args
.bpid
.full_prefix
, sym
))
285 outf
.write('#include_next <%s>\n' % f
)
286 outf
.write('#else\n');
288 outf
.write('#undef %s\n' % s
)
289 outf
.write('#define %s LINUX_BACKPORT(%s)\n' % (s
, s
))
290 outf
.write('#include <%s>\n' % (os
.path
.dirname(f
) + '/backport-' + os
.path
.basename(f
), ))
291 outf
.write('#endif /* %s%s */\n' % (args
.bpid
.full_prefix
, sym
))
294 def git_debug_init(args
):
296 Initialize a git repository in the output directory and commit the current
297 code in it. This is only used for debugging the transformations this code
298 will do to the output later.
300 if not args
.gitdebug
:
302 # Git supports re-initialization, although not well documented it can
303 # reset config stuff, lets avoid that if the tree already exists.
304 if not os
.path
.exists(os
.path
.join(args
.bpid
.project_dir
, '.git')):
305 git
.init(tree
=args
.bpid
.project_dir
)
306 git
.commit_all("Copied backport", tree
=args
.bpid
.project_dir
)
309 def git_debug_snapshot(args
, name
):
311 Take a git snapshot for the debugging.
313 if not args
.gitdebug
:
315 git
.commit_all(name
, tree
=args
.bpid
.project_dir
)
317 def get_rel_spec_stable(rel
):
319 Returns release specs for a linux-stable backports based release.
322 m
= re
.match(r
"(?P<VERSION>\d+)\.+" \
323 "(?P<PATCHLEVEL>\d+)[.]*" \
324 "(?P<SUBLEVEL>\d*)" \
325 "[-rc]+(?P<RC_VERSION>\d+)\-+" \
326 "(?P<RELMOD_UPDATE>\d+)[-]*" \
327 "(?P<RELMOD_TYPE>[usnpc]*)", \
330 m
= re
.match(r
"(?P<VERSION>\d+)\.+" \
331 "(?P<PATCHLEVEL>\d+)[.]*" \
332 "(?P<SUBLEVEL>\d*)\-+" \
333 "(?P<RELMOD_UPDATE>\d+)[-]*" \
334 "(?P<RELMOD_TYPE>[usnpc]*)", \
340 def get_rel_spec_next(rel
):
342 Returns release specs for a linux-next backports based release.
344 m
= re
.match(r
"(?P<DATE_VERSION>\d+)[-]*" \
345 "(?P<RELMOD_UPDATE>\d*)[-]*" \
346 "(?P<RELMOD_TYPE>[usnpc]*)", \
352 def get_rel_prep(rel
):
354 Returns a dict with prep work details we need prior to
355 uploading a backports release to kernel.org
357 rel_specs
= get_rel_spec_stable(rel
)
362 rel_specs
= get_rel_spec_next(rel
)
364 sys
.stdout
.write("rel: %s\n" % rel
)
366 if (rel_specs
['RELMOD_UPDATE'] == '0' or
367 rel_specs
['RELMOD_UPDATE'] == '1'):
370 date
= rel_specs
['DATE_VERSION']
375 if (len(month
) != 2):
383 rel_tag
= "backports-" + rel
.replace(rel_specs
['RELMOD_TYPE'], "")
386 if (not rel_specs
['RELMOD_UPDATE']):
388 if (rel_specs
['RELMOD_UPDATE'] == '0'):
390 ignore
+= rel_specs
['RELMOD_UPDATE']
391 if (rel_specs
['RELMOD_TYPE'] != ''):
392 ignore
+= rel_specs
['RELMOD_TYPE']
393 base_rel
= rel
.replace(ignore
, "")
394 paths
.append("v" + base_rel
)
395 rel_tag
= "v" + rel
.replace(rel_specs
['RELMOD_TYPE'], "")
397 rel_prep
= dict(stable
= is_stable
,
398 expected_tag
= rel_tag
,
399 paths_to_create
= paths
)
402 def create_tar_and_gz(tar_name
, dir_to_tar
):
404 We need both a tar file and gzip for kernel.org, the tar file
405 gets signed, then we upload the compressed version, kup-server
406 in the backend decompresses and verifies the tarball against
409 basename
= os
.path
.basename(dir_to_tar
)
410 tar
= tarfile
.open(tar_name
, "w")
411 tar
.add(dir_to_tar
, basename
)
414 tar_file
= open(tar_name
, "r")
416 gz_file
= gzip
.GzipFile(tar_name
+ ".gz", 'wb')
417 gz_file
.write(tar_file
.read())
420 def upload_release(args
, rel_prep
, logwrite
=lambda x
:None):
422 Given a path of a relase make tarball out of it, PGP sign it, and
423 then upload it to kernel.org using kup.
425 The linux-next based release do not require a RELMOD_UPDATE
426 given that typically only one release is made per day. Using
427 RELMOD_UPDATE for these releases is allowed though and if
428 present it must be > 1.
430 The linux-stable based releases require a RELMOD_UPDATE.
432 RELMOD_UPDATE must be numeric and > 0 just as the RC releases
435 The tree must also be tagged with the respective release, without
436 the RELMOD_TYPE. For linux-next based releases this consists of
437 backports- followed by DATE_VERSION and if RELMOD_TYPE is present.
438 For linux-stable releases this consists of v followed by the
439 full release version except the RELMOD_TYPE.
441 Uploads will not be allowed if these rules are not followed.
443 korg_path
= "/pub/linux/kernel/projects/backports"
445 if (rel_prep
['stable']):
446 korg_path
+= "/stable"
448 parent
= os
.path
.dirname(args
.bpid
.project_dir
)
449 release
= os
.path
.basename(args
.bpid
.project_dir
)
450 tar_name
= parent
+ '/' + release
+ ".tar"
451 gzip_name
= tar_name
+ ".gz"
453 create_tar_and_gz(tar_name
, args
.bpid
.project_dir
)
455 logwrite(gpg
.sign(tar_name
, extra_args
=['--armor', '--detach-sign']))
457 logwrite("------------------------------------------------------")
459 if (not args
.kup_test
):
460 logwrite("About to upload, current target path contents:")
462 logwrite("kup-test: current target path contents:")
464 logwrite(kup
.ls(path
=korg_path
))
466 for path
in rel_prep
['paths_to_create']:
467 korg_path
+= '/' + path
468 if (not args
.kup_test
):
469 logwrite("create directory: %s" % korg_path
)
470 logwrite(kup
.mkdir(korg_path
))
472 if (not args
.kup_test
):
473 logwrite("upload file %s to %s" % (gzip_name
, korg_path
))
474 logwrite(kup
.put(gzip_name
, tar_name
+ '.asc', korg_path
))
475 logwrite("\nFinished upload!\n")
476 logwrite("Target path contents:")
477 logwrite(kup
.ls(path
=korg_path
))
479 kup_cmd
= "kup put /\n\t\t%s /\n\t\t%s /\n\t\t%s" % (gzip_name
, tar_name
+ '.asc', korg_path
)
480 logwrite("kup-test: skipping cmd: %s" % kup_cmd
)
482 def apply_patches(args
, desc
, source_dir
, patch_src
, target_dir
, logwrite
=lambda x
:None):
484 Given a path of a directories of patches and SmPL patches apply
485 them on the target directory. If requested refresh patches, or test
486 a specific SmPL patch.
488 logwrite('Applying patches from %s to %s ...' % (patch_src
, target_dir
))
489 test_cocci
= args
.test_cocci
or args
.profile_cocci
490 test_cocci_found
= False
493 for root
, dirs
, files
in os
.walk(os
.path
.join(source_dir
, patch_src
)):
495 if not test_cocci
and f
.endswith('.patch'):
496 patches
.append(os
.path
.join(root
, f
))
497 if f
.endswith('.cocci'):
499 if f
not in test_cocci
:
501 test_cocci_found
= True
503 logwrite("Testing Coccinelle SmPL patch: %s" % test_cocci
)
504 elif args
.profile_cocci
:
505 logwrite("Profiling Coccinelle SmPL patch: %s" % test_cocci
)
506 sempatches
.append(os
.path
.join(root
, f
))
508 prefix_len
= len(os
.path
.join(source_dir
, patch_src
)) + 1
509 for pfile
in patches
:
510 print_name
= pfile
[prefix_len
:]
511 # read the patch file
512 p
= patch
.fromfile(pfile
)
513 # complain if it's not a patch
515 raise Exception('No patch content found in %s' % print_name
)
516 # leading / seems to be stripped?
517 if 'dev/null' in p
.items
[0].source
:
518 raise Exception('Patches creating files are not supported (in %s)' % print_name
)
519 # check if the first file the patch touches exists, if so
520 # assume the patch needs to be applied -- otherwise continue
521 patched_file
= '/'.join(p
.items
[0].source
.split('/')[1:])
522 fullfn
= os
.path
.join(target_dir
, patched_file
)
523 if not os
.path
.exists(fullfn
):
525 logwrite("Not applying %s, not needed" % print_name
)
528 logwrite("Applying patch %s" % print_name
)
531 # but for refresh, of course look at all files the patch touches
532 for patchitem
in p
.items
:
533 patched_file
= '/'.join(patchitem
.source
.split('/')[1:])
534 fullfn
= os
.path
.join(target_dir
, patched_file
)
535 shutil
.copyfile(fullfn
, fullfn
+ '.orig_file')
537 process
= subprocess
.Popen(['patch', '-p1'], stdout
=subprocess
.PIPE
,
538 stderr
=subprocess
.STDOUT
, stdin
=subprocess
.PIPE
,
539 close_fds
=True, universal_newlines
=True,
541 output
= process
.communicate(input=open(pfile
, 'r').read())[0]
542 output
= output
.split('\n')
547 logwrite('> %s' % line
)
548 if process
.returncode
!= 0:
550 logwrite("Failed to apply changes from %s" % print_name
)
552 logwrite('> %s' % line
)
556 pfilef
= open(pfile
+ '.tmp', 'a')
557 pfilef
.write(p
.top_header
)
559 for patchitem
in p
.items
:
560 patched_file
= '/'.join(patchitem
.source
.split('/')[1:])
561 fullfn
= os
.path
.join(target_dir
, patched_file
)
562 process
= subprocess
.Popen(['diff', '-p', '-u', patched_file
+ '.orig_file', patched_file
,
563 '--label', 'a/' + patched_file
,
564 '--label', 'b/' + patched_file
],
565 stdout
=pfilef
, close_fds
=True,
566 universal_newlines
=True, cwd
=target_dir
)
568 os
.unlink(fullfn
+ '.orig_file')
569 if not process
.returncode
in (0, 1):
570 logwrite("Failed to diff to refresh %s" % print_name
)
572 os
.unlink(pfile
+ '.tmp')
575 os
.rename(pfile
+ '.tmp', pfile
)
577 # remove orig/rej files that patch sometimes creates
578 for root
, dirs
, files
in os
.walk(target_dir
):
580 if f
[-5:] == '.orig' or f
[-4:] == '.rej':
581 os
.unlink(os
.path
.join(root
, f
))
582 git_debug_snapshot(args
, "apply %s patch %s" % (desc
, print_name
))
585 prefix_len
= len(os
.path
.join(source_dir
, patch_src
)) + 1
587 for cocci_file
in sempatches
:
589 '--sp-file', cocci_file
,
591 '--recursive-includes',
592 '--relax-include-path',
595 '-j', '%d' % cpu_count(),
596 '--dir', os
.path
.abspath(target_dir
) ]
597 extra_spatch_args
= []
598 if args
.profile_cocci
:
599 cmd
.append('--profile')
600 print_name
= cocci_file
[prefix_len
:]
602 logwrite("Applying SmPL patch %s" % print_name
)
603 sprocess
= subprocess
.Popen(cmd
,
604 stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
,
605 close_fds
=True, universal_newlines
=True,
607 output
= sprocess
.communicate()[0]
609 if sprocess
.returncode
!= 0:
610 logwrite("Failed to process SmPL patch %s with %i" % (print_name
, sprocess
.returncode
))
612 output
= output
.split('\n')
617 logwrite('> %s' % line
)
619 # remove cocci_backup files
620 for root
, dirs
, files
in os
.walk(target_dir
):
622 if f
.endswith('.cocci_backup'):
623 os
.unlink(os
.path
.join(root
, f
))
624 git_debug_snapshot(args
, "apply %s SmPL patch %s" % (desc
, print_name
))
626 if test_cocci
and test_cocci_found
:
631 # Our binary requirements go here
634 req
.coccinelle('1.0.0-rc24')
635 if not req
.reqs_match():
638 # set up and parse arguments
639 parser
= argparse
.ArgumentParser(description
='generate backport tree')
640 parser
.add_argument('kerneldir', metavar
='<kernel tree>', type=str,
641 help='Kernel tree to copy drivers from')
642 parser
.add_argument('outdir', metavar
='<output directory>', type=str,
643 help='Directory to write the generated tree to')
644 parser
.add_argument('--copy-list', metavar
='<listfile>', type=argparse
.FileType('r'),
646 help='File containing list of files/directories to copy, default "copy-list"')
647 parser
.add_argument('--git-revision', metavar
='<revision>', type=str,
648 help='git commit revision (see gitrevisions(7)) to take objects from.' +
649 'If this is specified, the kernel tree is used as git object storage ' +
650 'and we use git ls-tree to get the files.')
651 parser
.add_argument('--clean', const
=True, default
=False, action
="store_const",
652 help='Clean output directory instead of erroring if it isn\'t empty')
653 parser
.add_argument('--integrate', const
=True, default
=False, action
="store_const",
654 help='Integrate a future backported kernel solution into ' +
655 'an older kernel tree source directory.')
656 parser
.add_argument('--refresh', const
=True, default
=False, action
="store_const",
657 help='Refresh patches as they are applied, the source dir will be modified!')
658 parser
.add_argument('--base-name', metavar
='<name>', type=str, default
='Linux',
659 help='name of base tree, default just "Linux"')
660 parser
.add_argument('--gitdebug', const
=True, default
=False, action
="store_const",
661 help='Use git, in the output tree, to debug the various transformation steps ' +
662 'that the tree generation makes (apply patches, ...)')
663 parser
.add_argument('--verbose', const
=True, default
=False, action
="store_const",
664 help='Print more verbose information')
665 parser
.add_argument('--extra-driver', nargs
=2, metavar
=('<source dir>', '<copy-list>'), type=str,
666 action
='append', default
=[], help='Extra driver directory/copy-list.')
667 parser
.add_argument('--kup', const
=True, default
=False, action
="store_const",
668 help='For maintainers: upload a release to kernel.org')
669 parser
.add_argument('--kup-test', const
=True, default
=False, action
="store_const",
670 help='For maintainers: do all the work as if you were about to ' +
671 'upload to kernel.org but do not do the final `kup put` ' +
672 'and also do not run any `kup mkdir` commands. This will ' +
673 'however run `kup ls` on the target paths so ' +
674 'at the very least we test your kup configuration. ' +
675 'If this is your first time uploading use this first!')
676 parser
.add_argument('--test-cocci', metavar
='<sp_file>', type=str, default
=None,
677 help='Only use the cocci file passed for Coccinelle, don\'t do anything else, ' +
678 'also creates a git repo on the target directory for easy inspection ' +
679 'of changes done by Coccinelle.')
680 parser
.add_argument('--profile-cocci', metavar
='<sp_file>', type=str, default
=None,
681 help='Only use the cocci file passed and pass --profile to Coccinelle, ' +
682 'also creates a git repo on the target directory for easy inspection ' +
683 'of changes done by Coccinelle.')
684 args
= parser
.parse_args()
686 # When building a package we use CPTCFG as we can rely on the
687 # fact that kconfig treats CONFIG_ as an environment variable
688 # requring less changes on code. For kernel integration we use
689 # the longer CONFIG_BACKPORT given that we'll be sticking to
690 # the kernel symbol namespace, to address that we do a final
691 # search / replace. Technically its possible to rely on the
692 # same prefix for packaging as with kernel integration but
693 # there are already some users of the CPTCFG prefix.
696 bpid
= Bp_Identity(integrate
= args
.integrate
,
697 kconfig_prefix
= 'CONFIG_',
698 project_prefix
= 'BACKPORT_',
699 project_dir
= args
.outdir
,
700 target_dir
= os
.path
.join(args
.outdir
, 'backports/'),
701 target_dir_name
= 'backports/',
702 kconfig_source_var
= '$BACKPORT_DIR',
705 bpid
= Bp_Identity(integrate
= args
.integrate
,
706 kconfig_prefix
= 'CPTCFG_',
708 project_dir
= args
.outdir
,
709 target_dir
= args
.outdir
,
710 target_dir_name
= '',
711 kconfig_source_var
= '$BACKPORT_DIR',
715 sys
.stdout
.write(msg
)
716 sys
.stdout
.write('\n')
719 return process(args
.kerneldir
, args
.copy_list
,
720 git_revision
=args
.git_revision
,
723 refresh
=args
.refresh
, base_name
=args
.base_name
,
724 gitdebug
=args
.gitdebug
, verbose
=args
.verbose
,
725 extra_driver
=args
.extra_driver
,
727 kup_test
=args
.kup_test
,
728 test_cocci
=args
.test_cocci
,
729 profile_cocci
=args
.profile_cocci
,
732 def process(kerneldir
, copy_list_file
, git_revision
=None,
734 clean
=False, refresh
=False, base_name
="Linux", gitdebug
=False,
735 verbose
=False, extra_driver
=[], kup
=False,
739 logwrite
=lambda x
:None,
740 git_tracked_version
=False):
742 def __init__(self
, kerneldir
, copy_list_file
,
743 git_revision
, bpid
, clean
, refresh
, base_name
,
744 gitdebug
, verbose
, extra_driver
, kup
,
748 self
.kerneldir
= kerneldir
749 self
.copy_list
= copy_list_file
750 self
.git_revision
= git_revision
753 self
.refresh
= refresh
754 self
.base_name
= base_name
755 self
.gitdebug
= gitdebug
756 self
.verbose
= verbose
757 self
.extra_driver
= extra_driver
759 self
.kup_test
= kup_test
760 self
.test_cocci
= test_cocci
761 self
.profile_cocci
= profile_cocci
762 if self
.test_cocci
or self
.profile_cocci
:
764 def git_paranoia(tree
=None, logwrite
=lambda x
:None):
765 data
= git
.paranoia(tree
)
767 logwrite('Cannot use %s' % tree
)
768 logwrite('%s' % data
['output'])
771 logwrite('Validated tree: %s' % tree
)
773 args
= Args(kerneldir
, copy_list_file
,
774 git_revision
, bpid
, clean
, refresh
, base_name
,
775 gitdebug
, verbose
, extra_driver
, kup
, kup_test
,
776 test_cocci
, profile_cocci
)
780 if args
.kup_test
or args
.test_cocci
or args
.profile_cocci
or args
.refresh
:
781 logwrite('Cannot use integration with:\n\tkup_test\n\ttest_cocci\n\tprofile_cocci\n\trefresh\n');
784 # start processing ...
785 if (args
.kup
or args
.kup_test
):
786 git_paranoia(source_dir
, logwrite
)
787 git_paranoia(kerneldir
, logwrite
)
789 rel_describe
= git
.describe(rev
=None, tree
=source_dir
, extra_args
=['--dirty'])
790 release
= os
.path
.basename(bpid
.target_dir
)
791 version
= release
.replace("backports-", "")
793 rel_prep
= get_rel_prep(version
)
795 logwrite('Invalid backports release name: %s' % release
)
796 logwrite('For rules on the release name see upload_release()')
798 rel_type
= "linux-stable"
799 if (not rel_prep
['stable']):
800 rel_type
= "linux-next"
801 if (rel_prep
['expected_tag'] != rel_describe
):
802 logwrite('Unexpected %s based backports release tag on' % rel_type
)
803 logwrite('the backports tree tree: %s\n' % rel_describe
)
804 logwrite('You asked to make a release with this ')
805 logwrite('directory name: %s' % release
)
806 logwrite('The actual expected tag we should find on')
807 logwrite('the backports tree then is: %s\n' % rel_prep
['expected_tag'])
808 logwrite('For rules on the release name see upload_release()')
811 copy_list
= read_copy_list(args
.copy_list
)
812 deplist
= read_dependencies(os
.path
.join(source_dir
, 'dependencies'))
814 # validate output directory
815 check_output_dir(bpid
.target_dir
, args
.clean
)
818 backport_integrate_files
= [
819 ('Makefile.kernel', 'Makefile'),
820 ('Kconfig.integrate', 'Kconfig'),
822 backport_package_files
= [(x
, x
) for x
in [
827 'Kconfig.package.hacks',
832 backport_package_files
+= [
833 ('Kconfig.package', 'Kconfig'),
835 backport_files
= [(x
, x
) for x
in [
841 if not bpid
.integrate
:
842 backport_files
+= backport_package_files
844 backport_files
+= backport_integrate_files
846 if not args
.git_revision
:
847 logwrite('Copy original source files ...')
849 logwrite('Get original source files from git ...')
851 copy_files(os
.path
.join(source_dir
, 'backport'), backport_files
, bpid
.target_dir
)
855 if not args
.git_revision
:
856 copy_files(args
.kerneldir
, copy_list
, bpid
.target_dir
)
858 copy_git_files(args
.kerneldir
, copy_list
, args
.git_revision
, bpid
.target_dir
)
860 # FIXME: should we add a git version of this (e.g. --git-extra-driver)?
861 for src
, copy_list
in args
.extra_driver
:
862 if (args
.kup
or args
.kup_test
):
864 copy_files(src
, read_copy_list(open(copy_list
, 'r')), bpid
.target_dir
)
866 git_debug_snapshot(args
, 'Add driver sources')
868 disable_list
= add_automatic_backports(args
)
869 if git_tracked_version
:
870 backports_version
= "(see git)"
871 kernel_version
= "(see git)"
873 backports_version
= git
.describe(tree
=source_dir
, extra_args
=['--long'])
874 kernel_version
= git
.describe(rev
=args
.git_revision
or 'HEAD',
876 extra_args
=['--long'])
878 if not bpid
.integrate
:
879 f
= open(os
.path
.join(bpid
.target_dir
, 'versions'), 'w')
880 f
.write('BACKPORTS_VERSION="%s"\n' % backports_version
)
881 f
.write('BACKPORTED_KERNEL_VERSION="%s"\n' % kernel_version
)
882 f
.write('BACKPORTED_KERNEL_NAME="%s"\n' % args
.base_name
)
883 if git_tracked_version
:
884 f
.write('BACKPORTS_GIT_TRACKED="backport tracker ID: $(shell git rev-parse HEAD 2>/dev/null || echo \'not built in git tree\')"\n')
886 git_debug_snapshot(args
, "add versions files")
889 (re
.compile(r
'.*(?P<key>%%BACKPORT_DIR%%)'), '%%BACKPORT_DIR%%', 'backports/'),
890 (re
.compile(r
'.*(?P<key>%%BACKPORTS_VERSION%%).*'), '%%BACKPORTS_VERSION%%', backports_version
),
891 (re
.compile(r
'.*(?P<key>%%BACKPORTED_KERNEL_VERSION%%).*'), '%%BACKPORTED_KERNEL_VERSION%%', kernel_version
),
892 (re
.compile(r
'.*(?P<key>%%BACKPORTED_KERNEL_NAME%%).*'), '%%BACKPORTED_KERNEL_NAME%%', args
.base_name
),
895 for l
in open(os
.path
.join(bpid
.target_dir
, 'Kconfig'), 'r'):
896 for r
in kconf_regexes
:
899 l
= re
.sub(r
'(' + r
[1] + ')', r
'' + r
[2] + '', l
)
901 outf
= open(os
.path
.join(bpid
.target_dir
, 'Kconfig'), 'w')
904 git_debug_snapshot(args
, "modify top level backports/Kconfig with backports identity")
907 # No need to verify_sources() as compat's Kconfig has no 'source' call
908 bpcfg
= kconfig
.ConfigTree(os
.path
.join(bpid
.target_dir
, 'compat', 'Kconfig'), bpid
)
909 bpcfg
.disable_symbols(disable_list
)
910 git_debug_snapshot(args
, 'Add automatic backports')
912 failure
= apply_patches(args
, "backport", source_dir
, 'patches', bpid
.target_dir
, logwrite
)
916 # Kernel integration requires Kconfig.versions already generated for you,
917 # we cannot do this for a package as we have no idea what kernel folks
920 kver
= gen_version
.kernelversion(bpid
.project_dir
)
921 rel_specs
= gen_version
.get_rel_spec_stable(kver
)
923 logwrite('Cannot parse source kernel version, update parser')
925 data
= gen_version
.genkconfig_versions(rel_specs
)
926 fo
= open(os
.path
.join(bpid
.target_dir
, 'Kconfig.versions'), 'w')
929 git_debug_snapshot(args
, "generate kernel version requirement Kconfig file")
931 # some post-processing is required
932 configtree
= kconfig
.ConfigTree(os
.path
.join(bpid
.target_dir
, 'Kconfig'), bpid
)
933 ignore
=['Kconfig.kernel', 'Kconfig.versions', 'Kconfig.local']
935 configtree
.verify_sources(ignore
=ignore
)
936 git_debug_snapshot(args
, "verify sources on top level backports Kconfig")
938 orig_symbols
= configtree
.symbols()
940 logwrite('Modify Kconfig tree ...')
941 configtree
.prune_sources(ignore
=ignore
)
942 git_debug_snapshot(args
, "prune Kconfig tree")
944 if not bpid
.integrate
:
945 configtree
.force_tristate_modular()
946 git_debug_snapshot(args
, "force tristate options modular")
948 ignore
= [os
.path
.join(bpid
.target_dir
, x
) for x
in [
949 'Kconfig.package.hacks',
955 configtree
.adjust_backported_configs(ignore
=ignore
, orig_symbols
=orig_symbols
)
956 git_debug_snapshot(args
, "adjust backports config symbols we port")
958 configtree
.modify_selects()
959 git_debug_snapshot(args
, "convert select to depends on")
961 symbols
= configtree
.symbols()
963 # write local symbol list -- needed during packaging build
964 if not bpid
.integrate
:
965 f
= open(os
.path
.join(bpid
.target_dir
, 'local-symbols'), 'w')
967 f
.write('%s=\n' % sym
)
969 git_debug_snapshot(args
, "add symbols files")
970 # also write Kconfig.local, representing all local symbols
971 # with a BACKPORTED_ prefix
972 f
= open(os
.path
.join(bpid
.target_dir
, 'Kconfig.local'), 'w')
974 f
.write('config BACKPORTED_%s\n' % sym
)
975 f
.write('\ttristate\n')
976 f
.write('\tdefault %s\n' % sym
)
978 git_debug_snapshot(args
, "add Kconfig.local")
980 # add defconfigs that we want
981 defconfigs_dir
= os
.path
.join(source_dir
, 'backport', 'defconfigs')
982 os
.mkdir(os
.path
.join(bpid
.target_dir
, 'defconfigs'))
983 for dfbase
in os
.listdir(defconfigs_dir
):
984 copy_defconfig
= True
985 dfsrc
= os
.path
.join(defconfigs_dir
, dfbase
)
986 for line
in open(dfsrc
, 'r'):
991 if sym
+ '=' in line
:
995 copy_defconfig
= False
998 shutil
.copy(dfsrc
, os
.path
.join(bpid
.target_dir
, 'defconfigs', dfbase
))
1000 git_debug_snapshot(args
, "add (useful) defconfig files")
1002 logwrite('Rewrite Makefiles and Kconfig files ...')
1004 # rewrite Makefile and source symbols
1006 # symbols we know only we can provide under the backport project prefix
1007 # for which we need an exception.
1008 skip_orig_syms
= [ bpid
.project_prefix
+ x
for x
in [
1012 parse_orig_syms
= [x
for x
in orig_symbols
if x
not in skip_orig_syms
]
1014 for some_symbols
in [parse_orig_syms
[i
:i
+ 50] for i
in range(0, len(parse_orig_syms
), 50)]:
1015 r
= 'CONFIG_((' + '|'.join([s
+ '(_MODULE)?' for s
in some_symbols
]) + ')([^A-Za-z0-9_]|$))'
1016 regexes
.append(re
.compile(r
, re
.MULTILINE
))
1017 for root
, dirs
, files
in os
.walk(bpid
.target_dir
):
1018 # don't go into .git dir (possible debug thing)
1022 data
= open(os
.path
.join(root
, f
), 'r').read()
1024 data
= r
.sub(r
'' + bpid
.full_prefix
+ '\\1', data
)
1025 data
= re
.sub(r
'\$\(srctree\)', '$(backport_srctree)', data
)
1026 data
= re
.sub(r
'-Idrivers', '-I$(backport_srctree)/drivers', data
)
1028 data
= re
.sub(r
'CPTCFG_', bpid
.full_prefix
, data
)
1029 fo
= open(os
.path
.join(root
, f
), 'w')
1033 git_debug_snapshot(args
, "rename config symbol / srctree usage")
1035 # disable unbuildable Kconfig symbols and stuff Makefiles that doesn't exist
1037 maketree
= make
.MakeTree(os
.path
.join(bpid
.target_dir
, 'Makefile'))
1039 maketree
= make
.MakeTree(os
.path
.join(bpid
.target_dir
, 'Makefile.kernel'))
1040 disable_kconfig
= []
1041 disable_makefile
= []
1042 for sym
in maketree
.get_impossible_symbols():
1043 disable_kconfig
.append(sym
[7:])
1044 disable_makefile
.append(sym
[7:])
1046 configtree
.disable_symbols(disable_kconfig
)
1047 git_debug_snapshot(args
, "disable impossible kconfig symbols")
1049 # add kernel version dependencies to Kconfig, from the dependency list
1050 # we read previously
1051 for sym
in tuple(deplist
.keys()):
1053 for dep
in deplist
[sym
]:
1054 if "kconfig:" in dep
:
1055 kconfig_expr
= dep
.replace('kconfig: ', '')
1056 new
.append(kconfig_expr
)
1057 elif (dep
== "DISABLE"):
1058 new
.append('BACKPORT_DISABLED_KCONFIG_OPTION')
1060 new
.append('!KERNEL_%s' % dep
.replace('.', '_'))
1062 deplist
[sym
] = ["BACKPORT_" + x
for x
in new
]
1065 configtree
.add_dependencies(deplist
)
1066 git_debug_snapshot(args
, "add kernel version dependencies")
1068 # disable things in makefiles that can't be selected and that the
1069 # build shouldn't recurse into because they don't exist -- if we
1070 # don't do that then a symbol from the kernel could cause the build
1071 # to attempt to recurse and fail
1073 # Note that we split the regex after 50 symbols, this is because of a
1074 # limitation in the regex implementation (it only supports 100 nested
1075 # groups -- 50 seemed safer and is still fast)
1077 for some_symbols
in [disable_makefile
[i
:i
+ 50] for i
in range(0, len(disable_makefile
), 50)]:
1078 r
= '^(([^#].*((' + bpid
.full_prefix_resafe
+ '|CONFIG_)(' + '|'.join([s
for s
in some_symbols
]) + ')))\W)'
1079 regexes
.append(re
.compile(r
, re
.MULTILINE
))
1080 for f
in maketree
.get_makefiles():
1081 data
= open(f
, 'r').read()
1083 data
= r
.sub(r
'#\1', data
)
1087 git_debug_snapshot(args
, "disable unsatisfied Makefile parts")
1090 f
= open(os
.path
.join(bpid
.project_dir
, 'Kconfig'), 'a')
1091 f
.write('source "backports/Kconfig"\n')
1093 git_debug_snapshot(args
, "hooked backport to top level Kconfig")
1095 failure
= apply_patches(args
, "integration", source_dir
, 'integration-patches/',
1096 bpid
.project_dir
, logwrite
)
1100 if (args
.kup
or args
.kup_test
):
1103 if not req
.reqs_match():
1105 upload_release(args
, rel_prep
, logwrite
=logwrite
)
1110 if __name__
== '__main__':