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
19 from lib
import bpversion
as gen_version
21 class Bp_Identity(object):
23 folks considering multiple integrations may want to
24 consider stuffing versioning info here as well but
25 that will need thought/design on sharing compat and
28 Use the *_resafe when combining on regexps, although we currently
29 don't support regexps there perhaps later we will and this will
30 just make things safer for the output regardless. Once and if those
31 are added, how we actually use the others for regular printing will
32 need to be considered.
34 def __init__(self
, integrate
=False, kconfig_prefix
='CPTCFG_',
35 project_prefix
='', project_dir
='',
36 target_dir
='', target_dir_name
='',
37 kconfig_source_var
=None):
38 self
.integrate
= integrate
39 self
.kconfig_prefix
= kconfig_prefix
40 self
.kconfig_prefix_resafe
= re
.escape(kconfig_prefix
)
41 self
.project_prefix
= project_prefix
42 self
.project_prefix_resafe
= re
.escape(project_prefix
)
43 self
.full_prefix
= kconfig_prefix
+ project_prefix
44 self
.full_prefix_resafe
= re
.escape(self
.full_prefix
)
45 self
.project_dir
= project_dir
46 self
.target_dir
= target_dir
47 self
.target_dir_name
= target_dir_name
48 self
.kconfig_source_var
= kconfig_source_var
49 if self
.kconfig_source_var
:
50 self
.kconfig_source_var_resafe
= re
.escape(self
.kconfig_source_var
)
52 self
.kconfig_source_var_resafe
= None
54 def read_copy_list(copyfile
):
56 Read a copy-list file and return a list of (source, target)
57 tuples. The source and target are usually the same, but in
58 the copy-list file there may be a rename included.
62 # remove leading/trailing whitespace
65 if not item
or item
[0] == '#':
68 raise Exception("Input path '%s' is absolute path, this isn't allowed" % (item
, ))
70 srcitem
, dstitem
= item
.split(' -> ')
71 if (srcitem
[-1] == '/') != (dstitem
[-1] == '/'):
72 raise Exception("Cannot copy file/dir to dir/file")
74 srcitem
= dstitem
= item
75 ret
.append((srcitem
, dstitem
))
79 def read_dependencies(depfilename
):
81 Read a (the) dependency file and return the list of
82 dependencies as a dictionary, mapping a Kconfig symbol
83 to a list of kernel version dependencies.
85 If a backported feature that an upstream backported driver
86 depends on had kconfig limitations (ie, debugging feature not
87 available) a built constaint restriction can be expressed
88 by using a kconfig expression. The kconfig expressions can
89 be specified by using the "kconfig: " prefix.
91 While reading ignore blank or commented lines.
94 depfile
= open(depfilename
, 'r')
98 if not item
or item
[0] == '#':
100 if "kconfig:" in item
:
101 sym
, kconfig_exp
= item
.split(" ", 1)
103 ret
[sym
] = [kconfig_exp
, ]
105 ret
[sym
].append(kconfig_exp
)
107 sym
, dep
= item
.split()
115 def check_output_dir(d
, clean
):
117 Check that the output directory doesn't exist or is empty,
118 unless clean is True in which case it's nuked. This helps
119 sanity check the output when generating a tree, so usually
120 running with --clean isn't suggested.
123 shutil
.rmtree(d
, ignore_errors
=True)
127 if e
.errno
!= errno
.ENOENT
:
131 def copytree(src
, dst
, symlinks
=False, ignore
=None):
133 Copy a directory tree. This differs from shutil.copytree()
134 in that it allows destination directories to already exist.
136 names
= os
.listdir(src
)
137 if ignore
is not None:
138 ignored_names
= ignore(src
, names
)
140 ignored_names
= set()
142 if not os
.path
.isdir(dst
):
146 if name
in ignored_names
:
148 srcname
= os
.path
.join(src
, name
)
149 dstname
= os
.path
.join(dst
, name
)
151 if symlinks
and os
.path
.islink(srcname
):
152 linkto
= os
.readlink(srcname
)
153 os
.symlink(linkto
, dstname
)
154 elif os
.path
.isdir(srcname
):
155 copytree(srcname
, dstname
, symlinks
, ignore
)
157 shutil
.copy2(srcname
, dstname
)
158 except (IOError, os
.error
) as why
:
159 errors
.append((srcname
, dstname
, str(why
)))
160 # catch the Error from the recursive copytree so that we can
161 # continue with other files
162 except shutil
.Error
as err
:
163 errors
.extend(err
.args
[0])
165 shutil
.copystat(src
, dst
)
167 # can't copy file access times on Windows
169 except OSError as why
:
170 errors
.extend((src
, dst
, str(why
)))
172 raise shutil
.Error(errors
)
175 def copy_files(srcpath
, copy_list
, outdir
):
177 Copy the copy_list files and directories from the srcpath
178 to the outdir. The copy_list contains source and target
181 For now, it also ignores any *~ editor backup files, though
182 this should probably be generalized (maybe using .gitignore?)
183 Similarly the code that only copies some files (*.c, *.h,
184 *.awk, Kconfig, Makefile) to avoid any build remnants in the
185 kernel if they should exist.
187 for srcitem
, tgtitem
in copy_list
:
189 copytree(srcpath
, outdir
, ignore
=shutil
.ignore_patterns('*~'))
190 elif tgtitem
[-1] == '/':
191 def copy_ignore(dir, entries
):
194 if i
[-2:] == '.o' or i
[-1] == '~':
197 copytree(os
.path
.join(srcpath
, srcitem
),
198 os
.path
.join(outdir
, tgtitem
),
202 os
.makedirs(os
.path
.join(outdir
, os
.path
.dirname(tgtitem
)))
204 # ignore dirs we might have created just now
205 if e
.errno
!= errno
.EEXIST
:
207 shutil
.copy(os
.path
.join(srcpath
, srcitem
),
208 os
.path
.join(outdir
, tgtitem
))
211 def copy_git_files(srcpath
, copy_list
, rev
, outdir
):
213 "Copy" files from a git repository. This really means listing them with
214 ls-tree and then using git show to obtain all the blobs.
216 for srcitem
, tgtitem
in copy_list
:
217 for m
, t
, h
, f
in git
.ls_tree(rev
=rev
, files
=(srcitem
,), tree
=srcpath
):
219 f
= os
.path
.join(outdir
, f
.replace(srcitem
, tgtitem
))
220 d
= os
.path
.dirname(f
)
221 if not os
.path
.exists(d
):
224 git
.get_blob(h
, outf
, tree
=srcpath
)
226 os
.chmod(f
, int(m
, 8))
228 def automatic_backport_mangle_c_file(name
):
229 return name
.replace('/', '-')
232 def add_automatic_backports(args
):
234 export
= re
.compile(r
'^EXPORT_SYMBOL(_GPL)?\((?P<sym>[^\)]*)\)')
235 bpi
= kconfig
.get_backport_info(os
.path
.join(args
.bpid
.target_dir
, 'compat', 'Kconfig'))
236 configtree
= kconfig
.ConfigTree(os
.path
.join(args
.bpid
.target_dir
, 'Kconfig'), args
.bpid
)
237 ignore
=['Kconfig.kernel', 'Kconfig.versions']
238 configtree
.verify_sources(ignore
=ignore
)
239 git_debug_snapshot(args
, "verify sources for automatic backports")
240 all_selects
= configtree
.all_selects()
241 for sym
, vals
in bpi
.items():
242 if sym
.startswith('BPAUTO_BUILD_'):
243 if not sym
[13:] in all_selects
:
244 disable_list
.append(sym
)
246 symtype
, module_name
, c_files
, h_files
= vals
251 files
.append((f
, os
.path
.join('compat', automatic_backport_mangle_c_file(f
))))
253 files
.append((os
.path
.join('include', f
),
254 os
.path
.join('include', os
.path
.dirname(f
), 'backport-' + os
.path
.basename(f
))))
255 if args
.git_revision
:
256 copy_git_files(args
.kerneldir
, files
, args
.git_revision
, args
.bpid
.target_dir
)
258 copy_files(args
.kerneldir
, files
, args
.bpid
.target_dir
)
260 # now add the Makefile line
261 mf
= open(os
.path
.join(args
.bpid
.target_dir
, 'compat', 'Makefile'), 'a+')
262 o_files
= [automatic_backport_mangle_c_file(f
)[:-1] + 'o' for f
in c_files
]
263 if symtype
== 'tristate':
265 raise Exception('backporting a module requires a #module-name')
267 mf
.write('%s-objs += %s\n' % (module_name
, of
))
268 mf
.write('obj-$(%s%s) += %s.o\n' % (args
.bpid
.full_prefix
, sym
, module_name
))
269 elif symtype
== 'bool':
270 mf
.write('compat-$(%s%s) += %s\n' % (args
.bpid
.full_prefix
, sym
, ' '.join(o_files
)))
272 # finally create the include file
275 for l
in open(os
.path
.join(args
.bpid
.target_dir
, 'compat',
276 automatic_backport_mangle_c_file(f
)), 'r'):
279 syms
.append(m
.group('sym'))
281 outf
= open(os
.path
.join(args
.bpid
.target_dir
, 'include', f
), 'w')
282 outf
.write('/* Automatically created during backport process */\n')
283 outf
.write('#ifndef %s%s\n' % (args
.bpid
.full_prefix
, sym
))
284 outf
.write('#include_next <%s>\n' % f
)
285 outf
.write('#else\n');
287 outf
.write('#undef %s\n' % s
)
288 outf
.write('#define %s LINUX_BACKPORT(%s)\n' % (s
, s
))
289 outf
.write('#include <%s>\n' % (os
.path
.dirname(f
) + '/backport-' + os
.path
.basename(f
), ))
290 outf
.write('#endif /* %s%s */\n' % (args
.bpid
.full_prefix
, sym
))
293 def git_debug_init(args
):
295 Initialize a git repository in the output directory and commit the current
296 code in it. This is only used for debugging the transformations this code
297 will do to the output later.
299 if not args
.gitdebug
:
301 # Git supports re-initialization, although not well documented it can
302 # reset config stuff, lets avoid that if the tree already exists.
303 if not os
.path
.exists(os
.path
.join(args
.bpid
.project_dir
, '.git')):
304 git
.init(tree
=args
.bpid
.project_dir
)
305 git
.commit_all("Copied backport", tree
=args
.bpid
.project_dir
)
308 def git_debug_snapshot(args
, name
):
310 Take a git snapshot for the debugging.
312 if not args
.gitdebug
:
314 git
.commit_all(name
, tree
=args
.bpid
.project_dir
)
316 def get_rel_spec_stable(rel
):
318 Returns release specs for a linux-stable backports based release.
321 m
= re
.match(r
"(?P<VERSION>\d+)\.+" \
322 "(?P<PATCHLEVEL>\d+)[.]*" \
323 "(?P<SUBLEVEL>\d*)" \
324 "[-rc]+(?P<RC_VERSION>\d+)\-+" \
325 "(?P<RELMOD_UPDATE>\d+)[-]*" \
326 "(?P<RELMOD_TYPE>[usnpc]*)", \
329 m
= re
.match(r
"(?P<VERSION>\d+)\.+" \
330 "(?P<PATCHLEVEL>\d+)[.]*" \
331 "(?P<SUBLEVEL>\d*)\-+" \
332 "(?P<RELMOD_UPDATE>\d+)[-]*" \
333 "(?P<RELMOD_TYPE>[usnpc]*)", \
339 def get_rel_spec_next(rel
):
341 Returns release specs for a linux-next backports based release.
343 m
= re
.match(r
"(?P<DATE_VERSION>\d+)[-]*" \
344 "(?P<RELMOD_UPDATE>\d*)[-]*" \
345 "(?P<RELMOD_TYPE>[usnpc]*)", \
351 def get_rel_prep(rel
):
353 Returns a dict with prep work details we need prior to
354 uploading a backports release to kernel.org
356 rel_specs
= get_rel_spec_stable(rel
)
361 rel_specs
= get_rel_spec_next(rel
)
363 sys
.stdout
.write("rel: %s\n" % rel
)
365 if (rel_specs
['RELMOD_UPDATE'] == '0' or
366 rel_specs
['RELMOD_UPDATE'] == '1'):
369 date
= rel_specs
['DATE_VERSION']
374 if (len(month
) != 2):
382 rel_tag
= "backports-" + rel
.replace(rel_specs
['RELMOD_TYPE'], "")
385 if (not rel_specs
['RELMOD_UPDATE']):
387 if (rel_specs
['RELMOD_UPDATE'] == '0'):
389 ignore
+= rel_specs
['RELMOD_UPDATE']
390 if (rel_specs
['RELMOD_TYPE'] != ''):
391 ignore
+= rel_specs
['RELMOD_TYPE']
392 base_rel
= rel
.replace(ignore
, "")
393 paths
.append("v" + base_rel
)
394 rel_tag
= "v" + rel
.replace(rel_specs
['RELMOD_TYPE'], "")
396 rel_prep
= dict(stable
= is_stable
,
397 expected_tag
= rel_tag
,
398 paths_to_create
= paths
)
401 def create_tar_and_gz(tar_name
, dir_to_tar
):
403 We need both a tar file and gzip for kernel.org, the tar file
404 gets signed, then we upload the compressed version, kup-server
405 in the backend decompresses and verifies the tarball against
408 basename
= os
.path
.basename(dir_to_tar
)
409 tar
= tarfile
.open(tar_name
, "w")
410 tar
.add(dir_to_tar
, basename
)
413 tar_file
= open(tar_name
, "r")
415 gz_file
= gzip
.GzipFile(tar_name
+ ".gz", 'wb')
416 gz_file
.write(tar_file
.read())
419 def upload_release(args
, rel_prep
, logwrite
=lambda x
:None):
421 Given a path of a relase make tarball out of it, PGP sign it, and
422 then upload it to kernel.org using kup.
424 The linux-next based release do not require a RELMOD_UPDATE
425 given that typically only one release is made per day. Using
426 RELMOD_UPDATE for these releases is allowed though and if
427 present it must be > 1.
429 The linux-stable based releases require a RELMOD_UPDATE.
431 RELMOD_UPDATE must be numeric and > 0 just as the RC releases
434 The tree must also be tagged with the respective release, without
435 the RELMOD_TYPE. For linux-next based releases this consists of
436 backports- followed by DATE_VERSION and if RELMOD_TYPE is present.
437 For linux-stable releases this consists of v followed by the
438 full release version except the RELMOD_TYPE.
440 Uploads will not be allowed if these rules are not followed.
442 korg_path
= "/pub/linux/kernel/projects/backports"
444 if (rel_prep
['stable']):
445 korg_path
+= "/stable"
447 parent
= os
.path
.dirname(args
.bpid
.project_dir
)
448 release
= os
.path
.basename(args
.bpid
.project_dir
)
449 tar_name
= parent
+ '/' + release
+ ".tar"
450 gzip_name
= tar_name
+ ".gz"
452 create_tar_and_gz(tar_name
, args
.bpid
.project_dir
)
454 logwrite(gpg
.sign(tar_name
, extra_args
=['--armor', '--detach-sign']))
456 logwrite("------------------------------------------------------")
458 if (not args
.kup_test
):
459 logwrite("About to upload, current target path contents:")
461 logwrite("kup-test: current target path contents:")
463 logwrite(kup
.ls(path
=korg_path
))
465 for path
in rel_prep
['paths_to_create']:
466 korg_path
+= '/' + path
467 if (not args
.kup_test
):
468 logwrite("create directory: %s" % korg_path
)
469 logwrite(kup
.mkdir(korg_path
))
471 if (not args
.kup_test
):
472 logwrite("upload file %s to %s" % (gzip_name
, korg_path
))
473 logwrite(kup
.put(gzip_name
, tar_name
+ '.asc', korg_path
))
474 logwrite("\nFinished upload!\n")
475 logwrite("Target path contents:")
476 logwrite(kup
.ls(path
=korg_path
))
478 kup_cmd
= "kup put /\n\t\t%s /\n\t\t%s /\n\t\t%s" % (gzip_name
, tar_name
+ '.asc', korg_path
)
479 logwrite("kup-test: skipping cmd: %s" % kup_cmd
)
481 def apply_patches(args
, desc
, source_dir
, patch_src
, target_dir
, logwrite
=lambda x
:None):
483 Given a path of a directories of patches and SmPL patches apply
484 them on the target directory. If requested refresh patches, or test
485 a specific SmPL patch.
487 logwrite('Applying patches from %s to %s ...' % (patch_src
, target_dir
))
488 test_cocci
= args
.test_cocci
or args
.profile_cocci
489 test_cocci_found
= False
492 for root
, dirs
, files
in os
.walk(os
.path
.join(source_dir
, patch_src
)):
494 if not test_cocci
and f
.endswith('.patch'):
495 patches
.append(os
.path
.join(root
, f
))
496 if f
.endswith('.cocci'):
498 if f
not in test_cocci
:
500 test_cocci_found
= True
502 logwrite("Testing Coccinelle SmPL patch: %s" % test_cocci
)
503 elif args
.profile_cocci
:
504 logwrite("Profiling Coccinelle SmPL patch: %s" % test_cocci
)
505 sempatches
.append(os
.path
.join(root
, f
))
507 prefix_len
= len(os
.path
.join(source_dir
, patch_src
)) + 1
508 for pfile
in patches
:
509 print_name
= pfile
[prefix_len
:]
510 # read the patch file
511 p
= patch
.fromfile(pfile
)
512 # complain if it's not a patch
514 raise Exception('No patch content found in %s' % print_name
)
515 # leading / seems to be stripped?
516 if 'dev/null' in p
.items
[0].source
:
517 raise Exception('Patches creating files are not supported (in %s)' % print_name
)
518 # check if the first file the patch touches exists, if so
519 # assume the patch needs to be applied -- otherwise continue
520 patched_file
= '/'.join(p
.items
[0].source
.split('/')[1:])
521 fullfn
= os
.path
.join(target_dir
, patched_file
)
522 if not os
.path
.exists(fullfn
):
524 logwrite("Not applying %s, not needed" % print_name
)
527 logwrite("Applying patch %s" % print_name
)
530 # but for refresh, of course look at all files the patch touches
531 for patchitem
in p
.items
:
532 patched_file
= '/'.join(patchitem
.source
.split('/')[1:])
533 fullfn
= os
.path
.join(target_dir
, patched_file
)
534 shutil
.copyfile(fullfn
, fullfn
+ '.orig_file')
536 process
= subprocess
.Popen(['patch', '-p1'], stdout
=subprocess
.PIPE
,
537 stderr
=subprocess
.STDOUT
, stdin
=subprocess
.PIPE
,
538 close_fds
=True, universal_newlines
=True,
540 output
= process
.communicate(input=open(pfile
, 'r').read())[0]
541 output
= output
.split('\n')
546 logwrite('> %s' % line
)
547 if process
.returncode
!= 0:
549 logwrite("Failed to apply changes from %s" % print_name
)
551 logwrite('> %s' % line
)
555 pfilef
= open(pfile
+ '.tmp', 'a')
556 pfilef
.write(p
.top_header
)
558 for patchitem
in p
.items
:
559 patched_file
= '/'.join(patchitem
.source
.split('/')[1:])
560 fullfn
= os
.path
.join(target_dir
, patched_file
)
561 process
= subprocess
.Popen(['diff', '-p', '-u', patched_file
+ '.orig_file', patched_file
,
562 '--label', 'a/' + patched_file
,
563 '--label', 'b/' + patched_file
],
564 stdout
=pfilef
, close_fds
=True,
565 universal_newlines
=True, cwd
=target_dir
)
567 os
.unlink(fullfn
+ '.orig_file')
568 if not process
.returncode
in (0, 1):
569 logwrite("Failed to diff to refresh %s" % print_name
)
571 os
.unlink(pfile
+ '.tmp')
574 os
.rename(pfile
+ '.tmp', pfile
)
576 # remove orig/rej files that patch sometimes creates
577 for root
, dirs
, files
in os
.walk(target_dir
):
579 if f
[-5:] == '.orig' or f
[-4:] == '.rej':
580 os
.unlink(os
.path
.join(root
, f
))
581 git_debug_snapshot(args
, "apply %s patch %s" % (desc
, print_name
))
584 prefix_len
= len(os
.path
.join(source_dir
, patch_src
)) + 1
586 for cocci_file
in sempatches
:
587 # Until Coccinelle picks this up
588 pycocci
= os
.path
.join(source_dir
, 'devel/pycocci')
589 cmd
= [pycocci
, cocci_file
]
590 extra_spatch_args
= []
591 if args
.profile_cocci
:
592 cmd
.append('--profile-cocci')
593 cmd
.append(os
.path
.abspath(target_dir
))
594 print_name
= cocci_file
[prefix_len
:]
596 logwrite("Applying SmPL patch %s" % print_name
)
597 logwrite(" %s" % cmd
)
598 sprocess
= subprocess
.Popen(cmd
,
599 stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
,
600 close_fds
=True, universal_newlines
=True,
602 output
= sprocess
.communicate()[0]
604 if sprocess
.returncode
!= 0:
605 logwrite("Failed to process SmPL patch %s with %i" % (print_name
, sprocess
.returncode
))
607 output
= output
.split('\n')
612 logwrite('> %s' % line
)
614 # remove cocci_backup files
615 for root
, dirs
, files
in os
.walk(target_dir
):
617 if f
.endswith('.cocci_backup'):
618 os
.unlink(os
.path
.join(root
, f
))
619 git_debug_snapshot(args
, "apply %s SmPL patch %s" % (desc
, print_name
))
621 if test_cocci
and test_cocci_found
:
626 # Our binary requirements go here
629 req
.coccinelle('1.0.6')
630 if not req
.reqs_match():
633 # set up and parse arguments
634 parser
= argparse
.ArgumentParser(description
='generate backport tree')
635 parser
.add_argument('kerneldir', metavar
='<kernel tree>', type=str,
636 help='Kernel tree to copy drivers from')
637 parser
.add_argument('outdir', metavar
='<output directory>', type=str,
638 help='Directory to write the generated tree to')
639 parser
.add_argument('--copy-list', metavar
='<listfile>', type=argparse
.FileType('r'),
641 help='File containing list of files/directories to copy, default "copy-list"')
642 parser
.add_argument('--git-revision', metavar
='<revision>', type=str,
643 help='git commit revision (see gitrevisions(7)) to take objects from.' +
644 'If this is specified, the kernel tree is used as git object storage ' +
645 'and we use git ls-tree to get the files.')
646 parser
.add_argument('--clean', const
=True, default
=False, action
="store_const",
647 help='Clean output directory instead of erroring if it isn\'t empty')
648 parser
.add_argument('--integrate', const
=True, default
=False, action
="store_const",
649 help='Integrate a future backported kernel solution into ' +
650 'an older kernel tree source directory.')
651 parser
.add_argument('--refresh', const
=True, default
=False, action
="store_const",
652 help='Refresh patches as they are applied, the source dir will be modified!')
653 parser
.add_argument('--base-name', metavar
='<name>', type=str, default
='Linux',
654 help='name of base tree, default just "Linux"')
655 parser
.add_argument('--gitdebug', '--git-debug', const
=True, default
=False, action
="store_const",
656 help='Use git, in the output tree, to debug the various transformation steps ' +
657 'that the tree generation makes (apply patches, ...)')
658 parser
.add_argument('--verbose', const
=True, default
=False, action
="store_const",
659 help='Print more verbose information')
660 parser
.add_argument('--extra-driver', nargs
=2, metavar
=('<source dir>', '<copy-list>'), type=str,
661 action
='append', default
=[], help='Extra driver directory/copy-list.')
662 parser
.add_argument('--kup', const
=True, default
=False, action
="store_const",
663 help='For maintainers: upload a release to kernel.org')
664 parser
.add_argument('--kup-test', const
=True, default
=False, action
="store_const",
665 help='For maintainers: do all the work as if you were about to ' +
666 'upload to kernel.org but do not do the final `kup put` ' +
667 'and also do not run any `kup mkdir` commands. This will ' +
668 'however run `kup ls` on the target paths so ' +
669 'at the very least we test your kup configuration. ' +
670 'If this is your first time uploading use this first!')
671 parser
.add_argument('--test-cocci', metavar
='<sp_file>', type=str, default
=None,
672 help='Only use the cocci file passed for Coccinelle, don\'t do anything else, ' +
673 'also creates a git repo on the target directory for easy inspection ' +
674 'of changes done by Coccinelle.')
675 parser
.add_argument('--profile-cocci', metavar
='<sp_file>', type=str, default
=None,
676 help='Only use the cocci file passed and pass --profile to Coccinelle, ' +
677 'also creates a git repo on the target directory for easy inspection ' +
678 'of changes done by Coccinelle.')
679 args
= parser
.parse_args()
681 # When building a package we use CPTCFG as we can rely on the
682 # fact that kconfig treats CONFIG_ as an environment variable
683 # requring less changes on code. For kernel integration we use
684 # the longer CONFIG_BACKPORT given that we'll be sticking to
685 # the kernel symbol namespace, to address that we do a final
686 # search / replace. Technically its possible to rely on the
687 # same prefix for packaging as with kernel integration but
688 # there are already some users of the CPTCFG prefix.
691 bpid
= Bp_Identity(integrate
= args
.integrate
,
692 kconfig_prefix
= 'CONFIG_',
693 project_prefix
= 'BACKPORT_',
694 project_dir
= args
.outdir
,
695 target_dir
= os
.path
.join(args
.outdir
, 'backports/'),
696 target_dir_name
= 'backports/',
697 kconfig_source_var
= '$BACKPORT_DIR',
700 bpid
= Bp_Identity(integrate
= args
.integrate
,
701 kconfig_prefix
= 'CPTCFG_',
703 project_dir
= args
.outdir
,
704 target_dir
= args
.outdir
,
705 target_dir_name
= '',
706 kconfig_source_var
= '$BACKPORT_DIR',
710 sys
.stdout
.write(msg
)
711 sys
.stdout
.write('\n')
714 return process(args
.kerneldir
, args
.copy_list
,
715 git_revision
=args
.git_revision
,
718 refresh
=args
.refresh
, base_name
=args
.base_name
,
719 gitdebug
=args
.gitdebug
, verbose
=args
.verbose
,
720 extra_driver
=args
.extra_driver
,
722 kup_test
=args
.kup_test
,
723 test_cocci
=args
.test_cocci
,
724 profile_cocci
=args
.profile_cocci
,
727 def process(kerneldir
, copy_list_file
, git_revision
=None,
729 clean
=False, refresh
=False, base_name
="Linux", gitdebug
=False,
730 verbose
=False, extra_driver
=[], kup
=False,
734 logwrite
=lambda x
:None,
735 git_tracked_version
=False):
737 def __init__(self
, kerneldir
, copy_list_file
,
738 git_revision
, bpid
, clean
, refresh
, base_name
,
739 gitdebug
, verbose
, extra_driver
, kup
,
743 self
.kerneldir
= kerneldir
744 self
.copy_list
= copy_list_file
745 self
.git_revision
= git_revision
748 self
.refresh
= refresh
749 self
.base_name
= base_name
750 self
.gitdebug
= gitdebug
751 self
.verbose
= verbose
752 self
.extra_driver
= extra_driver
754 self
.kup_test
= kup_test
755 self
.test_cocci
= test_cocci
756 self
.profile_cocci
= profile_cocci
757 if self
.test_cocci
or self
.profile_cocci
:
759 def git_paranoia(tree
=None, logwrite
=lambda x
:None):
760 data
= git
.paranoia(tree
)
762 logwrite('Cannot use %s' % tree
)
763 logwrite('%s' % data
['output'])
766 logwrite('Validated tree: %s' % tree
)
768 args
= Args(kerneldir
, copy_list_file
,
769 git_revision
, bpid
, clean
, refresh
, base_name
,
770 gitdebug
, verbose
, extra_driver
, kup
, kup_test
,
771 test_cocci
, profile_cocci
)
775 if args
.kup_test
or args
.test_cocci
or args
.profile_cocci
or args
.refresh
:
776 logwrite('Cannot use integration with:\n\tkup_test\n\ttest_cocci\n\tprofile_cocci\n\trefresh\n');
779 # start processing ...
780 if (args
.kup
or args
.kup_test
):
781 git_paranoia(source_dir
, logwrite
)
782 git_paranoia(kerneldir
, logwrite
)
784 rel_describe
= git
.describe(rev
=None, tree
=source_dir
, extra_args
=['--dirty'])
785 release
= os
.path
.basename(bpid
.target_dir
)
786 version
= release
.replace("backports-", "")
788 rel_prep
= get_rel_prep(version
)
790 logwrite('Invalid backports release name: %s' % release
)
791 logwrite('For rules on the release name see upload_release()')
793 rel_type
= "linux-stable"
794 if (not rel_prep
['stable']):
795 rel_type
= "linux-next"
796 if (rel_prep
['expected_tag'] != rel_describe
):
797 logwrite('Unexpected %s based backports release tag on' % rel_type
)
798 logwrite('the backports tree tree: %s\n' % rel_describe
)
799 logwrite('You asked to make a release with this ')
800 logwrite('directory name: %s' % release
)
801 logwrite('The actual expected tag we should find on')
802 logwrite('the backports tree then is: %s\n' % rel_prep
['expected_tag'])
803 logwrite('For rules on the release name see upload_release()')
806 copy_list
= read_copy_list(args
.copy_list
)
807 deplist
= read_dependencies(os
.path
.join(source_dir
, 'dependencies'))
809 # validate output directory
810 check_output_dir(bpid
.target_dir
, args
.clean
)
813 backport_integrate_files
= [
814 ('Makefile.kernel', 'Makefile'),
815 ('Kconfig.integrate', 'Kconfig'),
817 backport_package_files
= [(x
, x
) for x
in [
822 'Kconfig.package.hacks',
827 backport_package_files
+= [
828 ('Kconfig.package', 'Kconfig'),
830 backport_files
= [(x
, x
) for x
in [
836 if not bpid
.integrate
:
837 backport_files
+= backport_package_files
839 backport_files
+= backport_integrate_files
841 if not args
.git_revision
:
842 logwrite('Copy original source files ...')
844 logwrite('Get original source files from git ...')
846 copy_files(os
.path
.join(source_dir
, 'backport'), backport_files
, bpid
.target_dir
)
850 if not args
.git_revision
:
851 copy_files(args
.kerneldir
, copy_list
, bpid
.target_dir
)
853 copy_git_files(args
.kerneldir
, copy_list
, args
.git_revision
, bpid
.target_dir
)
855 # FIXME: should we add a git version of this (e.g. --git-extra-driver)?
856 for src
, copy_list
in args
.extra_driver
:
857 if (args
.kup
or args
.kup_test
):
859 copy_files(src
, read_copy_list(open(copy_list
, 'r')), bpid
.target_dir
)
861 git_debug_snapshot(args
, 'Add driver sources')
863 disable_list
= add_automatic_backports(args
)
864 if git_tracked_version
:
865 backports_version
= "(see git)"
866 kernel_version
= "(see git)"
868 backports_version
= git
.describe(tree
=source_dir
, extra_args
=['--long'])
869 kernel_version
= git
.describe(rev
=args
.git_revision
or 'HEAD',
871 extra_args
=['--long'])
873 if not bpid
.integrate
:
874 f
= open(os
.path
.join(bpid
.target_dir
, 'versions'), 'w')
875 f
.write('BACKPORTS_VERSION="%s"\n' % backports_version
)
876 f
.write('BACKPORTED_KERNEL_VERSION="%s"\n' % kernel_version
)
877 f
.write('BACKPORTED_KERNEL_NAME="%s"\n' % args
.base_name
)
878 if git_tracked_version
:
879 f
.write('BACKPORTS_GIT_TRACKED="backport tracker ID: $(shell git rev-parse HEAD 2>/dev/null || echo \'not built in git tree\')"\n')
881 git_debug_snapshot(args
, "add versions files")
884 (re
.compile(r
'.*(?P<key>%%BACKPORT_DIR%%)'), '%%BACKPORT_DIR%%', 'backports/'),
885 (re
.compile(r
'.*(?P<key>%%BACKPORTS_VERSION%%).*'), '%%BACKPORTS_VERSION%%', backports_version
),
886 (re
.compile(r
'.*(?P<key>%%BACKPORTED_KERNEL_VERSION%%).*'), '%%BACKPORTED_KERNEL_VERSION%%', kernel_version
),
887 (re
.compile(r
'.*(?P<key>%%BACKPORTED_KERNEL_NAME%%).*'), '%%BACKPORTED_KERNEL_NAME%%', args
.base_name
),
890 for l
in open(os
.path
.join(bpid
.target_dir
, 'Kconfig'), 'r'):
891 for r
in kconf_regexes
:
894 l
= re
.sub(r
'(' + r
[1] + ')', r
'' + r
[2] + '', l
)
896 outf
= open(os
.path
.join(bpid
.target_dir
, 'Kconfig'), 'w')
899 git_debug_snapshot(args
, "modify top level backports/Kconfig with backports identity")
902 # No need to verify_sources() as compat's Kconfig has no 'source' call
903 bpcfg
= kconfig
.ConfigTree(os
.path
.join(bpid
.target_dir
, 'compat', 'Kconfig'), bpid
)
904 bpcfg
.disable_symbols(disable_list
)
905 git_debug_snapshot(args
, 'Add automatic backports')
907 failure
= apply_patches(args
, "backport", source_dir
, 'patches', bpid
.target_dir
, logwrite
)
911 # Kernel integration requires Kconfig.versions already generated for you,
912 # we cannot do this for a package as we have no idea what kernel folks
915 kver
= gen_version
.kernelversion(bpid
.project_dir
)
916 rel_specs
= gen_version
.get_rel_spec_stable(kver
)
918 logwrite('Cannot parse source kernel version, update parser')
920 data
= gen_version
.genkconfig_versions(rel_specs
)
921 fo
= open(os
.path
.join(bpid
.target_dir
, 'Kconfig.versions'), 'w')
924 git_debug_snapshot(args
, "generate kernel version requirement Kconfig file")
926 # some post-processing is required
927 configtree
= kconfig
.ConfigTree(os
.path
.join(bpid
.target_dir
, 'Kconfig'), bpid
)
928 ignore
=['Kconfig.kernel', 'Kconfig.versions', 'Kconfig.local']
930 configtree
.verify_sources(ignore
=ignore
)
931 git_debug_snapshot(args
, "verify sources on top level backports Kconfig")
933 orig_symbols
= configtree
.symbols()
935 logwrite('Modify Kconfig tree ...')
936 configtree
.prune_sources(ignore
=ignore
)
937 git_debug_snapshot(args
, "prune Kconfig tree")
939 if not bpid
.integrate
:
940 configtree
.force_tristate_modular()
941 git_debug_snapshot(args
, "force tristate options modular")
943 ignore
= [os
.path
.join(bpid
.target_dir
, x
) for x
in [
944 'Kconfig.package.hacks',
950 configtree
.adjust_backported_configs(ignore
=ignore
, orig_symbols
=orig_symbols
)
951 git_debug_snapshot(args
, "adjust backports config symbols we port")
953 configtree
.modify_selects()
954 git_debug_snapshot(args
, "convert select to depends on")
956 symbols
= configtree
.symbols()
958 # write local symbol list -- needed during packaging build
959 if not bpid
.integrate
:
960 f
= open(os
.path
.join(bpid
.target_dir
, 'local-symbols'), 'w')
962 f
.write('%s=\n' % sym
)
964 git_debug_snapshot(args
, "add symbols files")
965 # also write Kconfig.local, representing all local symbols
966 # with a BACKPORTED_ prefix
967 f
= open(os
.path
.join(bpid
.target_dir
, 'Kconfig.local'), 'w')
969 f
.write('config BACKPORTED_%s\n' % sym
)
970 f
.write('\ttristate\n')
971 f
.write('\tdefault %s\n' % sym
)
973 git_debug_snapshot(args
, "add Kconfig.local")
975 # add defconfigs that we want
976 defconfigs_dir
= os
.path
.join(source_dir
, 'backport', 'defconfigs')
977 os
.mkdir(os
.path
.join(bpid
.target_dir
, 'defconfigs'))
978 for dfbase
in os
.listdir(defconfigs_dir
):
979 copy_defconfig
= True
980 dfsrc
= os
.path
.join(defconfigs_dir
, dfbase
)
981 for line
in open(dfsrc
, 'r'):
986 if sym
+ '=' in line
:
990 copy_defconfig
= False
993 shutil
.copy(dfsrc
, os
.path
.join(bpid
.target_dir
, 'defconfigs', dfbase
))
995 git_debug_snapshot(args
, "add (useful) defconfig files")
997 logwrite('Rewrite Makefiles and Kconfig files ...')
999 # rewrite Makefile and source symbols
1001 # symbols we know only we can provide under the backport project prefix
1002 # for which we need an exception.
1003 skip_orig_syms
= [ bpid
.project_prefix
+ x
for x
in [
1007 parse_orig_syms
= [x
for x
in orig_symbols
if x
not in skip_orig_syms
]
1009 for some_symbols
in [parse_orig_syms
[i
:i
+ 50] for i
in range(0, len(parse_orig_syms
), 50)]:
1010 r
= 'CONFIG_((' + '|'.join([s
+ '(_MODULE)?' for s
in some_symbols
]) + ')([^A-Za-z0-9_]|$))'
1011 regexes
.append(re
.compile(r
, re
.MULTILINE
))
1012 for root
, dirs
, files
in os
.walk(bpid
.target_dir
):
1013 # don't go into .git dir (possible debug thing)
1017 data
= open(os
.path
.join(root
, f
), 'r').read()
1019 data
= r
.sub(r
'' + bpid
.full_prefix
+ '\\1', data
)
1020 # we have an absolue path in $(src) since we compile out of tree
1021 data
= re
.sub(r
'\$\(srctree\)/\$\(src\)', '$(src)', data
)
1022 data
= re
.sub(r
'\$\(srctree\)', '$(backport_srctree)', data
)
1023 data
= re
.sub(r
'-Idrivers', '-I$(backport_srctree)/drivers', data
)
1025 data
= re
.sub(r
'CPTCFG_', bpid
.full_prefix
, data
)
1026 fo
= open(os
.path
.join(root
, f
), 'w')
1030 git_debug_snapshot(args
, "rename config symbol / srctree usage")
1032 # disable unbuildable Kconfig symbols and stuff Makefiles that doesn't exist
1034 maketree
= make
.MakeTree(os
.path
.join(bpid
.target_dir
, 'Makefile'))
1036 maketree
= make
.MakeTree(os
.path
.join(bpid
.target_dir
, 'Makefile.kernel'))
1037 disable_kconfig
= []
1038 disable_makefile
= []
1039 for sym
in maketree
.get_impossible_symbols():
1040 disable_kconfig
.append(sym
[7:])
1041 disable_makefile
.append(sym
[7:])
1043 configtree
.disable_symbols(disable_kconfig
)
1044 git_debug_snapshot(args
, "disable impossible kconfig symbols")
1046 # add kernel version dependencies to Kconfig, from the dependency list
1047 # we read previously
1048 for sym
in tuple(deplist
.keys()):
1050 for dep
in deplist
[sym
]:
1051 if "kconfig:" in dep
:
1052 kconfig_expr
= dep
.replace('kconfig: ', '')
1053 new
.append(kconfig_expr
)
1054 elif (dep
== "DISABLE"):
1055 new
.append('BACKPORT_DISABLED_KCONFIG_OPTION')
1057 new
.append('!KERNEL_%s' % dep
.replace('.', '_'))
1059 deplist
[sym
] = ["BACKPORT_" + x
for x
in new
]
1062 configtree
.add_dependencies(deplist
)
1063 git_debug_snapshot(args
, "add kernel version dependencies")
1065 # disable things in makefiles that can't be selected and that the
1066 # build shouldn't recurse into because they don't exist -- if we
1067 # don't do that then a symbol from the kernel could cause the build
1068 # to attempt to recurse and fail
1070 # Note that we split the regex after 50 symbols, this is because of a
1071 # limitation in the regex implementation (it only supports 100 nested
1072 # groups -- 50 seemed safer and is still fast)
1074 for some_symbols
in [disable_makefile
[i
:i
+ 50] for i
in range(0, len(disable_makefile
), 50)]:
1075 r
= '^(([^#].*((' + bpid
.full_prefix_resafe
+ '|CONFIG_)(' + '|'.join([s
for s
in some_symbols
]) + ')))\W)'
1076 regexes
.append(re
.compile(r
, re
.MULTILINE
))
1077 for f
in maketree
.get_makefiles():
1078 data
= open(f
, 'r').read()
1080 data
= r
.sub(r
'#\1', data
)
1084 git_debug_snapshot(args
, "disable unsatisfied Makefile parts")
1087 f
= open(os
.path
.join(bpid
.project_dir
, 'Kconfig'), 'a')
1088 f
.write('source "backports/Kconfig"\n')
1090 git_debug_snapshot(args
, "hooked backport to top level Kconfig")
1092 failure
= apply_patches(args
, "integration", source_dir
, 'integration-patches/',
1093 bpid
.project_dir
, logwrite
)
1097 if (args
.kup
or args
.kup_test
):
1100 if not req
.reqs_match():
1102 upload_release(args
, rel_prep
, logwrite
=logwrite
)
1107 if __name__
== '__main__':