build-ark.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. #!/usr/bin/env python3
  2. import argparse
  3. import functools
  4. import operator
  5. import os
  6. import shutil
  7. import subprocess
  8. import time
  9. def system(cmd: str) -> None:
  10. subprocess.run(cmd, shell=True, check=True)
  11. parser = argparse.ArgumentParser(usage="Build a patched Fedora kernel")
  12. parser.add_argument(
  13. "--package-name",
  14. help="The name of the patched package (e.g. foo -> kernel-foo).",
  15. required=True,
  16. )
  17. parser.add_argument(
  18. "--package-tag",
  19. help="The upstream tag to build.",
  20. required=True,
  21. )
  22. parser.add_argument(
  23. "--package-release",
  24. help="The release suffix of the modified package.",
  25. required=True,
  26. )
  27. parser.add_argument(
  28. "--ark-dir",
  29. help="The local path to the kernel-ark repository.",
  30. default="kernel-ark",
  31. )
  32. parser.add_argument(
  33. "--ark-url",
  34. help="The remote path to the kernel-ark repository.",
  35. default="https://gitlab.com/cki-project/kernel-ark",
  36. )
  37. parser.add_argument(
  38. "--patch",
  39. help="Applies a patch to the kernel source.",
  40. action="append",
  41. nargs="+",
  42. )
  43. parser.add_argument(
  44. "--config",
  45. help="Applies a KConfig fragment to the kernel source.",
  46. action="append",
  47. nargs="+",
  48. )
  49. parser.add_argument(
  50. "--file",
  51. help="Copy a file into the RPM buildroot.",
  52. action="append",
  53. nargs="+",
  54. )
  55. parser.add_argument(
  56. "--buildopts",
  57. help="Enable or disable options of the kernel spec file.",
  58. action="append",
  59. nargs="+",
  60. )
  61. parser.add_argument(
  62. "--outdir",
  63. help="The directory where the built RPM files will be saved.",
  64. default="out",
  65. )
  66. args = parser.parse_args()
  67. patches = [] if not args.patch else functools.reduce(operator.add, args.patch)
  68. configs = [] if not args.config else functools.reduce(operator.add, args.config)
  69. files = [] if not args.file else functools.reduce(operator.add, args.file)
  70. buildopts = [] if not args.buildopts else functools.reduce(operator.add, args.buildopts)
  71. # Make paths absolute.
  72. patches = [os.path.realpath(x) for x in patches]
  73. configs = [os.path.realpath(x) for x in configs]
  74. files = [os.path.realpath(x) for x in files]
  75. outdir = os.path.realpath(args.outdir)
  76. # Clone the kernel-ark repository if it doesn't exist.
  77. if not os.path.exists(args.ark_dir):
  78. system("git clone '%s' '%s'" % (args.ark_url, args.ark_dir))
  79. os.chdir(args.ark_dir)
  80. # Check out the requested tag.
  81. system("git fetch --tags")
  82. system("git clean -dfx")
  83. system("git checkout -b 'build/%s'" % time.time())
  84. system("git reset --hard '%s'" % args.package_tag)
  85. # Apply patches
  86. for patch in patches:
  87. system("git am '%s'" % patch)
  88. # Copy files
  89. for file in files:
  90. shutil.copy(file, "redhat/fedora_files/")
  91. # Apply config options
  92. #
  93. # The format that the kernel-ark tree expects is a bit different from
  94. # a standard kernel config. Every option is split into a single file
  95. # named after that config.
  96. #
  97. # Example:
  98. # $ cat redhat/configs/common/generic/CONFIG_PCI
  99. # CONFIG_PCI=y
  100. #
  101. # This supposedly makes things easier for Red Hat developers,
  102. # but it also ends up being really annoying for us.
  103. for config in configs:
  104. with open(config) as f:
  105. lines = f.readlines()
  106. # Filter out comments, this means only selecting lines that look like:
  107. # - CONFIG_FOO=b
  108. # - # CONFIG_FOO is not set
  109. for line in lines:
  110. enable = line.startswith("CONFIG_")
  111. disable = line.startswith("# CONFIG_")
  112. if not enable and not disable:
  113. continue
  114. NAME = ""
  115. if enable:
  116. NAME = line.split("=")[0]
  117. elif disable:
  118. NAME = line[2:].split(" ")[0]
  119. print("Applying %s" % line.rstrip("\n"))
  120. with open("redhat/configs/custom-overrides/generic/%s" % NAME, "w") as f:
  121. f.write(line)
  122. system("git add redhat/configs/custom-overrides/generic")
  123. system("git commit -m 'Merge %s config'" % args.package_name)
  124. cmd = []
  125. cmd.append("make")
  126. cmd.append("dist-rpms")
  127. cmd.append("SPECPACKAGE_NAME='kernel-%s'" % args.package_name)
  128. cmd.append("DISTLOCALVERSION='.%s'" % args.package_name)
  129. cmd.append("BUILD='%s'" % args.package_release)
  130. if len(buildopts) > 0:
  131. cmd.append("BUILDOPTS='%s'" % " ".join(buildopts))
  132. # Build RPMS
  133. system(" ".join(cmd))
  134. # Copy built RPMS to output directory
  135. os.makedirs(outdir, exist_ok=True)
  136. system("cp -r redhat/rpm/RPMS/* '%s'" % outdir)