build-ark.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  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. "--mode",
  63. help="Whether to build a source RPM or binary RPMs.",
  64. choices=["rpms", "srpm"],
  65. default="rpms",
  66. )
  67. parser.add_argument(
  68. "--outdir",
  69. help="The directory where the built RPM files will be saved.",
  70. default="out",
  71. )
  72. args = parser.parse_args()
  73. patches = [] if not args.patch else functools.reduce(operator.add, args.patch)
  74. configs = [] if not args.config else functools.reduce(operator.add, args.config)
  75. files = [] if not args.file else functools.reduce(operator.add, args.file)
  76. buildopts = [] if not args.buildopts else functools.reduce(operator.add, args.buildopts)
  77. # Make paths absolute.
  78. patches = [os.path.realpath(x) for x in patches]
  79. configs = [os.path.realpath(x) for x in configs]
  80. files = [os.path.realpath(x) for x in files]
  81. outdir = os.path.realpath(args.outdir)
  82. # Clone the kernel-ark repository if it doesn't exist.
  83. if not os.path.exists(args.ark_dir):
  84. system("git clone '%s' '%s'" % (args.ark_url, args.ark_dir))
  85. os.chdir(args.ark_dir)
  86. # Check out the requested tag.
  87. system("git fetch --tags")
  88. system("git clean -dfx")
  89. system("git checkout -b 'build/%s'" % time.time())
  90. system("git reset --hard '%s'" % args.package_tag)
  91. # Apply patches
  92. for patch in patches:
  93. system("git am '%s'" % patch)
  94. # Copy files
  95. for file in files:
  96. shutil.copy(file, "redhat/fedora_files/")
  97. # Apply config options
  98. #
  99. # The format that the kernel-ark tree expects is a bit different from
  100. # a standard kernel config. Every option is split into a single file
  101. # named after that config.
  102. #
  103. # Example:
  104. # $ cat redhat/configs/common/generic/CONFIG_PCI
  105. # CONFIG_PCI=y
  106. #
  107. # This supposedly makes things easier for Red Hat developers,
  108. # but it also ends up being really annoying for us.
  109. for config in configs:
  110. with open(config) as f:
  111. lines = f.readlines()
  112. # Filter out comments, this means only selecting lines that look like:
  113. # - CONFIG_FOO=b
  114. # - # CONFIG_FOO is not set
  115. for line in lines:
  116. enable = line.startswith("CONFIG_")
  117. disable = line.startswith("# CONFIG_")
  118. if not enable and not disable:
  119. continue
  120. NAME = ""
  121. if enable:
  122. NAME = line.split("=")[0]
  123. elif disable:
  124. NAME = line[2:].split(" ")[0]
  125. print("Applying %s" % line.rstrip("\n"))
  126. with open("redhat/configs/custom-overrides/generic/%s" % NAME, "w") as f:
  127. f.write(line)
  128. system("git add redhat/configs/custom-overrides/generic")
  129. system("git commit -m 'Merge %s config'" % args.package_name)
  130. cmd = ["make"]
  131. if args.mode == "rpms":
  132. cmd.append("dist-rpms")
  133. else:
  134. cmd.append("dist-srpm")
  135. cmd.append("SPECPACKAGE_NAME='kernel-%s'" % args.package_name)
  136. cmd.append("DISTLOCALVERSION='.%s'" % args.package_name)
  137. cmd.append("BUILD='%s'" % args.package_release)
  138. if len(buildopts) > 0:
  139. cmd.append("BUILDOPTS='%s'" % " ".join(buildopts))
  140. # Build RPMS
  141. system(" ".join(cmd))
  142. if args.mode == "rpms":
  143. rpmdir = "RPMS"
  144. else:
  145. rpmdir = "SRPMS"
  146. # Copy built RPMS to output directory
  147. os.makedirs(outdir, exist_ok=True)
  148. system("cp -r redhat/rpm/%s/* '%s'" % (rpmdir, outdir))