build-linux 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. #!/usr/bin/env python3
  2. import os
  3. import shutil
  4. import common
  5. from shell_helpers import LF
  6. class Main(common.BuildCliFunction):
  7. def __init__(self):
  8. super().__init__(
  9. description='''\
  10. Build the Linux kernel.
  11. '''
  12. )
  13. self.add_argument(
  14. '--config', default=[], action='append',
  15. help='''\
  16. Add a single kernel config configs to the current build. Sample value:
  17. 'CONFIG_FORTIFY_SOURCE=y'. Can be used multiple times to add multiple
  18. configs. Takes precedence over any config files.
  19. '''
  20. )
  21. self.add_argument(
  22. '--config-fragment', default=[], action='append',
  23. help='''\
  24. Also use the given kernel configuration fragment file.
  25. Pass multiple times to use multiple fragment files.
  26. '''
  27. )
  28. self.add_argument(
  29. '--custom-config-file',
  30. help='''\
  31. Ignore all default kernel configurations and use this file instead.
  32. Still uses options explicitly passed with `--config` and
  33. `--config-fragment` on top of it.
  34. '''
  35. )
  36. self.add_argument(
  37. '--config-only', default=False,
  38. help='''\
  39. Configure the kernel, but don't build it.
  40. '''
  41. )
  42. self.add_argument(
  43. 'extra_make_args',
  44. default=[],
  45. metavar='extra-make-args',
  46. nargs='*'
  47. )
  48. def build(self):
  49. build_dir = self.get_build_dir()
  50. if self.env['initrd'] or self.env['initramfs']:
  51. raise Exception('just trolling, --initrd and --initramfs are broken for now')
  52. os.makedirs(build_dir, exist_ok=True)
  53. tool = 'gcc'
  54. gcc = self.get_toolchain_tool(tool)
  55. prefix = gcc[:-len(tool)]
  56. common_args = {
  57. 'cwd': self.env['linux_src_dir'],
  58. }
  59. ccache = shutil.which('ccache')
  60. if ccache is not None:
  61. cc = '{} {}'.format(ccache, gcc)
  62. else:
  63. cc = gcc
  64. if self.env['verbose']:
  65. verbose = ['V=1']
  66. else:
  67. verbose = []
  68. common_make_args = [
  69. 'make', LF,
  70. '-j', str(self.env['nproc']), LF,
  71. 'ARCH={}'.format(self.env['linux_arch']), LF,
  72. 'CROSS_COMPILE={}'.format(prefix), LF,
  73. 'CC={}'.format(cc), LF,
  74. 'O={}'.format(build_dir), LF,
  75. ] + verbose
  76. if self.env['custom_config_file'] is not None:
  77. if not os.path.exists(self.env['custom_config_file']):
  78. raise Exception('config fragment file does not exist: {}'.format(self.env['custom_config_file']))
  79. base_config_file = self.env['custom_config_file']
  80. config_fragments = []
  81. else:
  82. base_config_file = os.path.join(self.env['linux_config_dir'], 'buildroot-{}'.format(self.env['arch']))
  83. config_fragments = ['min', 'default']
  84. for i, config_fragment in enumerate(config_fragments):
  85. config_fragments[i] = os.path.join(self.env['linux_config_dir'], config_fragment)
  86. config_fragments.extend(self.env['config_fragment'])
  87. if self.env['config'] != []:
  88. cli_config_fragment_path = os.path.join(build_dir, 'lkmc_cli_config_fragment')
  89. cli_config_str = '\n'.join(self.env['config'])
  90. self.write_string_to_file(cli_config_fragment_path, cli_config_str)
  91. config_fragments.append(cli_config_fragment_path)
  92. self.sh.cp(
  93. base_config_file,
  94. os.path.join(build_dir, '.config'),
  95. )
  96. self.sh.run_cmd(
  97. [
  98. os.path.join(self.env['linux_src_dir'], 'scripts', 'kconfig', 'merge_config.sh'), LF,
  99. '-m', LF,
  100. '-O', build_dir, LF,
  101. os.path.join(build_dir, '.config'), LF,
  102. ] +
  103. self.sh.add_newlines(config_fragments)
  104. )
  105. self.sh.run_cmd(
  106. (
  107. common_make_args +
  108. ['olddefconfig', LF]
  109. ),
  110. **common_args
  111. )
  112. if not self.env['config_only']:
  113. self.sh.run_cmd(
  114. (
  115. common_make_args +
  116. self.sh.add_newlines(self.env['extra_make_args'])
  117. ),
  118. **common_args
  119. )
  120. self.sh.run_cmd(
  121. (
  122. common_make_args +
  123. [
  124. 'INSTALL_MOD_PATH={}'.format(self.env['out_rootfs_overlay_dir']), LF,
  125. 'modules_install', LF,
  126. ]
  127. ),
  128. **common_args
  129. )
  130. # TODO: remove build and source https://stackoverflow.com/questions/13578618/what-does-build-and-source-link-do-in-lib-modules-kernel-version
  131. # TODO Basically all kernel modules also basically leak full host paths. Just terrible. Buildroot deals with that stuff nicely for us.
  132. # self.rmrf()
  133. def get_build_dir(self):
  134. return self.env['linux_build_dir']
  135. if __name__ == '__main__':
  136. Main().cli()