Compare commits
1 Commits
main
...
feat/LKT_p
| Author | SHA1 | Date |
|---|---|---|
|
|
41f4de40e5 |
|
|
@ -9,303 +9,311 @@ from conan.tools.build import cross_building
|
||||||
from conan.tools.files import copy, get, save
|
from conan.tools.files import copy, get, save
|
||||||
|
|
||||||
|
|
||||||
class LinuxKbuildTreeConan(ConanFile):
|
def prepare(conanfile, output=None, defconfig=None, make_modules=None, url=None):
|
||||||
name = "linux-kbuild-tree"
|
if conanfile.settings.os != "Linux":
|
||||||
package_type = "application"
|
raise ConanInvalidConfiguration("linux-kbuild-tree requires a Linux target")
|
||||||
|
|
||||||
settings = "os", "arch", "compiler", "build_type"
|
pyreq = conanfile.python_requires["linux-kbuild-tree"]
|
||||||
|
version = str(pyreq.ref.version)
|
||||||
|
arch = kernel_arch(conanfile)
|
||||||
|
cross_compile = _cross_compile(conanfile)
|
||||||
|
if cross_compile is None:
|
||||||
|
raise ConanInvalidConfiguration(
|
||||||
|
"Cross-building a kernel tree requires "
|
||||||
|
"tools.build:compiler_executables with a GCC-like C compiler"
|
||||||
|
)
|
||||||
|
|
||||||
options = {
|
if defconfig is None:
|
||||||
"url": ["ANY"],
|
defconfig = conanfile.conf.get("user.linux-kbuild-tree:defconfig", default="defconfig")
|
||||||
"defconfig": ["ANY"],
|
if make_modules is None:
|
||||||
"make_modules": [True, False],
|
make_modules = conanfile.conf.get(
|
||||||
|
"user.linux-kbuild-tree:make_modules",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
make_modules = _as_bool(make_modules)
|
||||||
|
if url is None:
|
||||||
|
url = conanfile.conf.get("user.linux-kbuild-tree:url", default=None)
|
||||||
|
|
||||||
|
linux = output or os.path.join(conanfile.build_folder, "linux-kbuild-tree")
|
||||||
|
source_url, sha256 = _linux_source(pyreq.path, version, url=url)
|
||||||
|
|
||||||
|
if not os.path.exists(os.path.join(linux, "Makefile")):
|
||||||
|
get(conanfile, source_url, sha256=sha256, destination=linux, strip_root=True)
|
||||||
|
|
||||||
|
jobs = conanfile.conf.get("tools.build:jobs", default=None)
|
||||||
|
make_jobs = f" -j{jobs}" if jobs else ""
|
||||||
|
make_base = (
|
||||||
|
f"make --no-print-directory -C {shlex.quote(linux)}{make_jobs} "
|
||||||
|
f"ARCH={shlex.quote(str(arch))} "
|
||||||
|
f"CROSS_COMPILE={shlex.quote(str(cross_compile or ''))} "
|
||||||
|
f"{_host_warning_flags_args(version)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
_install_config(conanfile, pyreq.path, linux, make_base, defconfig)
|
||||||
|
conanfile.run(f"{make_base} olddefconfig prepare modules_prepare")
|
||||||
|
|
||||||
|
if make_modules:
|
||||||
|
# Required when CONFIG_MODVERSIONS=y and the target kernel exports
|
||||||
|
# symbols that are not available from a vendor-provided Module.symvers.
|
||||||
|
conanfile.run(f"{make_base} modules")
|
||||||
|
|
||||||
|
kernelrelease = _capture(f"{make_base} kernelrelease")
|
||||||
|
compiler = _capture(f"{_cross_gcc(cross_compile)} --version").splitlines()[0]
|
||||||
|
info_path = os.path.join(linux, "kbuild-tree-info.txt")
|
||||||
|
|
||||||
|
save(
|
||||||
|
conanfile,
|
||||||
|
info_path,
|
||||||
|
"\n".join(
|
||||||
|
[
|
||||||
|
f"url={source_url}",
|
||||||
|
f"ref={_linux_ref_from_version(version)}",
|
||||||
|
f"sha256={sha256 or ''}",
|
||||||
|
f"kernelrelease={kernelrelease}",
|
||||||
|
f"arch={arch}",
|
||||||
|
f"cross_compile={cross_compile or ''}",
|
||||||
|
f"compiler={compiler}",
|
||||||
|
f"make_modules={make_modules}",
|
||||||
|
"",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"path": linux,
|
||||||
|
"info_path": info_path,
|
||||||
|
"kernelrelease": kernelrelease,
|
||||||
|
"arch": arch,
|
||||||
|
"cross_compile": cross_compile or "",
|
||||||
}
|
}
|
||||||
|
|
||||||
default_options = {
|
|
||||||
"url": "",
|
def kernel_arch(conanfile):
|
||||||
"defconfig": "defconfig",
|
arch = str(conanfile.settings.arch)
|
||||||
"make_modules": False,
|
arch_map = {
|
||||||
|
"x86": "x86",
|
||||||
|
"x86_64": "x86",
|
||||||
|
"armv5el": "arm",
|
||||||
|
"armv5hf": "arm",
|
||||||
|
"armv6": "arm",
|
||||||
|
"armv7": "arm",
|
||||||
|
"armv7hf": "arm",
|
||||||
|
"armv8": "arm64",
|
||||||
|
"armv8_32": "arm",
|
||||||
|
"armv8.3": "arm64",
|
||||||
|
"armv8.5": "arm64",
|
||||||
|
"armv8.6": "arm64",
|
||||||
|
"armv8.7": "arm64",
|
||||||
|
"armv8.8": "arm64",
|
||||||
|
"armv8.9": "arm64",
|
||||||
|
"ppc32": "powerpc",
|
||||||
|
"ppc64": "powerpc",
|
||||||
|
"ppc64le": "powerpc",
|
||||||
|
"riscv32": "riscv",
|
||||||
|
"riscv64": "riscv",
|
||||||
|
"s390": "s390",
|
||||||
|
"s390x": "s390",
|
||||||
|
"sparc": "sparc",
|
||||||
|
"sparcv9": "sparc",
|
||||||
|
"mips": "mips",
|
||||||
|
"mips64": "mips",
|
||||||
}
|
}
|
||||||
|
if arch not in arch_map:
|
||||||
exports_sources = "configs/*"
|
raise ConanInvalidConfiguration(
|
||||||
|
f"Cannot derive Linux ARCH from settings.arch={arch!r}; "
|
||||||
def layout(self):
|
"add this Conan architecture to the recipe arch map"
|
||||||
self.folders.build = "build"
|
|
||||||
self.folders.generators = os.path.join(self.folders.build, "generators")
|
|
||||||
|
|
||||||
def package_id(self):
|
|
||||||
c_compiler = self._compiler_executable("c")
|
|
||||||
if c_compiler is not None:
|
|
||||||
self.info.conf.define("tools.build:compiler_executables", {"c": str(c_compiler)})
|
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
if self.settings.os != "Linux":
|
|
||||||
raise ConanInvalidConfiguration("linux-kbuild-tree requires a Linux target")
|
|
||||||
|
|
||||||
if not self.version:
|
|
||||||
raise ConanInvalidConfiguration(
|
|
||||||
"linux-kbuild-tree requires a package version, e.g. "
|
|
||||||
"conan create recipes/linux-kbuild-tree --version=6.5.9"
|
|
||||||
)
|
|
||||||
|
|
||||||
self._kernel_arch
|
|
||||||
|
|
||||||
if cross_building(self) and self._cross_compile is None:
|
|
||||||
raise ConanInvalidConfiguration(
|
|
||||||
"Cross-building a kernel tree requires "
|
|
||||||
"tools.build:compiler_executables with a GCC-like C compiler"
|
|
||||||
)
|
|
||||||
|
|
||||||
def source(self):
|
|
||||||
source_url, sha256 = self._linux_source()
|
|
||||||
get(
|
|
||||||
self,
|
|
||||||
source_url,
|
|
||||||
sha256=sha256,
|
|
||||||
destination=os.path.join(self.source_folder, "linux"),
|
|
||||||
strip_root=True,
|
|
||||||
)
|
)
|
||||||
|
return arch_map[arch]
|
||||||
|
|
||||||
def build(self):
|
|
||||||
linux = os.path.join(self.source_folder, "linux")
|
|
||||||
arch = self._kernel_arch
|
|
||||||
cross_compile = self._cross_compile or ""
|
|
||||||
jobs = self.conf.get("tools.build:jobs", default=None)
|
|
||||||
make_jobs = f" -j{jobs}" if jobs else ""
|
|
||||||
|
|
||||||
make_base = (
|
def _linux_ref_from_version(version):
|
||||||
f"make --no-print-directory -C {shlex.quote(linux)}{make_jobs} "
|
# Upstream Linux tags base releases as v6.8, while the kernel release
|
||||||
f"ARCH={shlex.quote(str(arch))} "
|
# and Conan package version are commonly represented as 6.8.0.
|
||||||
f"CROSS_COMPILE={shlex.quote(str(cross_compile))} "
|
version_parts = str(version).split(".")
|
||||||
f"{self._host_warning_flags_args}"
|
if len(version_parts) == 3 and version_parts[2] == "0":
|
||||||
|
return f"v{version_parts[0]}.{version_parts[1]}"
|
||||||
|
return f"v{version}"
|
||||||
|
|
||||||
|
|
||||||
|
def _linux_archive_version(version):
|
||||||
|
version_parts = str(version).split(".")
|
||||||
|
if len(version_parts) == 3 and version_parts[2] == "0":
|
||||||
|
return f"{version_parts[0]}.{version_parts[1]}"
|
||||||
|
return str(version)
|
||||||
|
|
||||||
|
|
||||||
|
def _linux_source(recipe_path, version, url=None):
|
||||||
|
source = _conandata_sources(recipe_path).get(str(version), {})
|
||||||
|
source_url = url or source.get("url") or _kernel_cdn_url(version)
|
||||||
|
return source_url, source.get("sha256")
|
||||||
|
|
||||||
|
|
||||||
|
def _kernel_cdn_url(version):
|
||||||
|
archive_version = _linux_archive_version(version)
|
||||||
|
major = archive_version.split(".", 1)[0]
|
||||||
|
return (
|
||||||
|
f"https://cdn.kernel.org/pub/linux/kernel/v{major}.x/"
|
||||||
|
f"linux-{archive_version}.tar.gz"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _conandata_sources(recipe_path):
|
||||||
|
path = os.path.join(recipe_path, "conandata.yml")
|
||||||
|
if not os.path.isfile(path):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
sources = {}
|
||||||
|
current = None
|
||||||
|
in_sources = False
|
||||||
|
with open(path, encoding="utf-8") as conandata:
|
||||||
|
for raw_line in conandata:
|
||||||
|
line = raw_line.rstrip()
|
||||||
|
stripped = line.strip()
|
||||||
|
if not stripped or stripped.startswith("#"):
|
||||||
|
continue
|
||||||
|
if stripped == "sources:":
|
||||||
|
in_sources = True
|
||||||
|
continue
|
||||||
|
if not in_sources:
|
||||||
|
continue
|
||||||
|
if not line.startswith(" "):
|
||||||
|
break
|
||||||
|
version_match = re.match(r'^\s{2}"?([^":]+)"?:\s*$', line)
|
||||||
|
if version_match:
|
||||||
|
current = version_match.group(1)
|
||||||
|
sources[current] = {}
|
||||||
|
continue
|
||||||
|
field_match = re.match(r'^\s{4}([A-Za-z0-9_-]+):\s*"?([^"]*)"?\s*$', line)
|
||||||
|
if current and field_match:
|
||||||
|
key, value = field_match.groups()
|
||||||
|
sources[current][key] = value
|
||||||
|
return sources
|
||||||
|
|
||||||
|
|
||||||
|
def _host_warning_flags_args(version):
|
||||||
|
hostcflags = []
|
||||||
|
|
||||||
|
if _version_lt(version, "5.16.0") and _host_compiler_accepts("-Wno-error=use-after-free"):
|
||||||
|
# Older objtool builds use -Werror and trip GCC 14's realloc(ptr)
|
||||||
|
# use-after-free diagnostic in tools/lib/subcmd/subcmd-util.h.
|
||||||
|
hostcflags.append("-Wno-error=use-after-free")
|
||||||
|
|
||||||
|
if not hostcflags:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
flags = shlex.quote(" ".join(hostcflags))
|
||||||
|
return f"HOSTCFLAGS={flags} EXTRA_CFLAGS={flags}"
|
||||||
|
|
||||||
|
|
||||||
|
def _as_bool(value):
|
||||||
|
if isinstance(value, str):
|
||||||
|
return value.lower() in ("1", "true", "yes", "on")
|
||||||
|
return bool(value)
|
||||||
|
|
||||||
|
|
||||||
|
def _host_compiler_accepts(flag):
|
||||||
|
try:
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"gcc",
|
||||||
|
flag,
|
||||||
|
"-x",
|
||||||
|
"c",
|
||||||
|
"-c",
|
||||||
|
"-o",
|
||||||
|
os.devnull,
|
||||||
|
"-",
|
||||||
|
],
|
||||||
|
input="int main(void) { return 0; }\n",
|
||||||
|
text=True,
|
||||||
|
stdout=subprocess.DEVNULL,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
check=True,
|
||||||
)
|
)
|
||||||
|
except (OSError, subprocess.CalledProcessError):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
self._install_config(linux, make_base)
|
|
||||||
|
|
||||||
self.run(f"{make_base} olddefconfig prepare modules_prepare")
|
def _version_lt(version, other):
|
||||||
|
return _version_tuple(str(version)) < _version_tuple(other)
|
||||||
|
|
||||||
if self.options.make_modules:
|
|
||||||
# Required when CONFIG_MODVERSIONS=y and the target kernel exports
|
|
||||||
# symbols that are not available from a vendor-provided Module.symvers.
|
|
||||||
self.run(f"{make_base} modules")
|
|
||||||
|
|
||||||
kernelrelease = self._capture(f"{make_base} kernelrelease")
|
def _version_tuple(version):
|
||||||
compiler = self._capture(f"{self._cross_gcc} --version").splitlines()[0]
|
return tuple(int(part) for part in version.split("."))
|
||||||
source_url, sha256 = self._linux_source()
|
|
||||||
|
|
||||||
save(
|
|
||||||
self,
|
|
||||||
os.path.join(self.build_folder, "kbuild-tree-info.txt"),
|
|
||||||
"\n".join(
|
|
||||||
[
|
|
||||||
f"url={source_url}",
|
|
||||||
f"ref={self._linux_ref_from_version()}",
|
|
||||||
f"sha256={sha256 or ''}",
|
|
||||||
f"kernelrelease={kernelrelease}",
|
|
||||||
f"arch={arch}",
|
|
||||||
f"cross_compile={cross_compile}",
|
|
||||||
f"compiler={compiler}",
|
|
||||||
f"make_modules={self.options.make_modules}",
|
|
||||||
"",
|
|
||||||
]
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def package(self):
|
def _cross_compile(conanfile):
|
||||||
linux = os.path.join(self.source_folder, "linux")
|
if not cross_building(conanfile):
|
||||||
|
return ""
|
||||||
|
|
||||||
# External module builds need source files, generated headers, scripts,
|
c_compiler = _compiler_executable(conanfile, "c")
|
||||||
# .config, and often Module.symvers. Packaging the whole prepared tree is
|
if not c_compiler:
|
||||||
# intentionally conservative and can be optimized after the flow is stable.
|
|
||||||
copy(self, "*", src=linux, dst=self.package_folder, excludes=(".git", ".git/*"))
|
|
||||||
copy(self, ".config", src=linux, dst=self.package_folder, keep_path=False)
|
|
||||||
copy(
|
|
||||||
self,
|
|
||||||
"kbuild-tree-info.txt",
|
|
||||||
src=self.build_folder,
|
|
||||||
dst=self.package_folder,
|
|
||||||
keep_path=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _linux_ref_from_version(self):
|
|
||||||
# Upstream Linux tags base releases as v6.8, while the kernel release
|
|
||||||
# and Conan package version are commonly represented as 6.8.0.
|
|
||||||
version_parts = str(self.version).split(".")
|
|
||||||
if len(version_parts) == 3 and version_parts[2] == "0":
|
|
||||||
return f"v{version_parts[0]}.{version_parts[1]}"
|
|
||||||
return f"v{self.version}"
|
|
||||||
|
|
||||||
def _linux_archive_version(self):
|
|
||||||
version_parts = str(self.version).split(".")
|
|
||||||
if len(version_parts) == 3 and version_parts[2] == "0":
|
|
||||||
return f"{version_parts[0]}.{version_parts[1]}"
|
|
||||||
return str(self.version)
|
|
||||||
|
|
||||||
def _linux_source(self):
|
|
||||||
source = self.conan_data.get("sources", {}).get(str(self.version)) or {}
|
|
||||||
source_url = source.get("url") or self._kernel_cdn_url()
|
|
||||||
return source_url, source.get("sha256")
|
|
||||||
|
|
||||||
def _kernel_cdn_url(self):
|
|
||||||
archive_version = self._linux_archive_version()
|
|
||||||
major = archive_version.split(".", 1)[0]
|
|
||||||
return (
|
|
||||||
f"https://cdn.kernel.org/pub/linux/kernel/v{major}.x/"
|
|
||||||
f"linux-{archive_version}.tar.gz"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _host_warning_flags_args(self):
|
|
||||||
hostcflags = []
|
|
||||||
|
|
||||||
if self._version_lt("5.16.0") and self._host_compiler_accepts(
|
|
||||||
"-Wno-error=use-after-free"
|
|
||||||
):
|
|
||||||
# Older objtool builds use -Werror and trip GCC 14's realloc(ptr)
|
|
||||||
# use-after-free diagnostic in tools/lib/subcmd/subcmd-util.h.
|
|
||||||
hostcflags.append("-Wno-error=use-after-free")
|
|
||||||
|
|
||||||
if not hostcflags:
|
|
||||||
return ""
|
|
||||||
|
|
||||||
flags = shlex.quote(" ".join(hostcflags))
|
|
||||||
return f"HOSTCFLAGS={flags} EXTRA_CFLAGS={flags}"
|
|
||||||
|
|
||||||
def _host_compiler_accepts(self, flag):
|
|
||||||
try:
|
|
||||||
subprocess.run(
|
|
||||||
[
|
|
||||||
"gcc",
|
|
||||||
flag,
|
|
||||||
"-x",
|
|
||||||
"c",
|
|
||||||
"-c",
|
|
||||||
"-o",
|
|
||||||
os.devnull,
|
|
||||||
"-",
|
|
||||||
],
|
|
||||||
input="int main(void) { return 0; }\n",
|
|
||||||
text=True,
|
|
||||||
stdout=subprocess.DEVNULL,
|
|
||||||
stderr=subprocess.DEVNULL,
|
|
||||||
check=True,
|
|
||||||
)
|
|
||||||
except (OSError, subprocess.CalledProcessError):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _version_lt(self, other):
|
|
||||||
return self._version_tuple(str(self.version)) < self._version_tuple(other)
|
|
||||||
|
|
||||||
def _version_tuple(self, version):
|
|
||||||
return tuple(int(part) for part in version.split("."))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _kernel_arch(self):
|
|
||||||
arch = str(self.settings.arch)
|
|
||||||
arch_map = {
|
|
||||||
"x86": "x86",
|
|
||||||
"x86_64": "x86",
|
|
||||||
"armv5el": "arm",
|
|
||||||
"armv5hf": "arm",
|
|
||||||
"armv6": "arm",
|
|
||||||
"armv7": "arm",
|
|
||||||
"armv7hf": "arm",
|
|
||||||
"armv8": "arm64",
|
|
||||||
"armv8_32": "arm",
|
|
||||||
"armv8.3": "arm64",
|
|
||||||
"armv8.5": "arm64",
|
|
||||||
"armv8.6": "arm64",
|
|
||||||
"armv8.7": "arm64",
|
|
||||||
"armv8.8": "arm64",
|
|
||||||
"armv8.9": "arm64",
|
|
||||||
"ppc32": "powerpc",
|
|
||||||
"ppc64": "powerpc",
|
|
||||||
"ppc64le": "powerpc",
|
|
||||||
"riscv32": "riscv",
|
|
||||||
"riscv64": "riscv",
|
|
||||||
"s390": "s390",
|
|
||||||
"s390x": "s390",
|
|
||||||
"sparc": "sparc",
|
|
||||||
"sparcv9": "sparc",
|
|
||||||
"mips": "mips",
|
|
||||||
"mips64": "mips",
|
|
||||||
}
|
|
||||||
if arch not in arch_map:
|
|
||||||
raise ConanInvalidConfiguration(
|
|
||||||
f"Cannot derive Linux ARCH from settings.arch={arch!r}; "
|
|
||||||
"add this Conan architecture to the recipe arch map"
|
|
||||||
)
|
|
||||||
return arch_map[arch]
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _cross_compile(self):
|
|
||||||
if not cross_building(self):
|
|
||||||
return ""
|
|
||||||
|
|
||||||
c_compiler = self._compiler_executable("c")
|
|
||||||
if not c_compiler:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return self._cross_compile_from_executable(c_compiler)
|
|
||||||
|
|
||||||
def _cross_compile_from_compiler_executables(self):
|
|
||||||
c_compiler = self._compiler_executable("c")
|
|
||||||
if not c_compiler:
|
|
||||||
return None
|
|
||||||
return self._cross_compile_from_executable(c_compiler)
|
|
||||||
|
|
||||||
def _cross_compile_from_executable(self, c_compiler):
|
|
||||||
executable = os.path.basename(c_compiler)
|
|
||||||
match = re.match(r"^(.+?)(?:gcc|cc)(?:-\d+(?:\.\d+)*)?$", executable)
|
|
||||||
if not match:
|
|
||||||
return None
|
|
||||||
return match.group(1)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _cross_gcc(self):
|
|
||||||
cross_compile = self._cross_compile or ""
|
|
||||||
return shlex.quote(f"{cross_compile}gcc")
|
|
||||||
|
|
||||||
def _compiler_executable(self, language):
|
|
||||||
executables = self.conf.get("tools.build:compiler_executables", default={})
|
|
||||||
if not executables:
|
|
||||||
return None
|
|
||||||
if hasattr(executables, "get"):
|
|
||||||
return executables.get(language)
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _install_config(self, linux, make_base):
|
return _cross_compile_from_executable(c_compiler)
|
||||||
defconfig = str(self.options.defconfig)
|
|
||||||
if not defconfig:
|
|
||||||
if not os.path.exists(os.path.join(linux, ".config")):
|
|
||||||
raise ConanInvalidConfiguration(
|
|
||||||
"Missing kernel config: set linux-kbuild-tree/*:defconfig "
|
|
||||||
"or provide a source tree with .config"
|
|
||||||
)
|
|
||||||
return
|
|
||||||
|
|
||||||
exported_config = os.path.join(self.source_folder, "configs", defconfig)
|
|
||||||
if os.path.isfile(exported_config):
|
def _cross_compile_from_executable(c_compiler):
|
||||||
copy(
|
executable = os.path.basename(c_compiler)
|
||||||
self,
|
match = re.match(r"^(.+?)(?:gcc|cc)(?:-\d+(?:\.\d+)*)?$", executable)
|
||||||
defconfig,
|
if not match:
|
||||||
src=os.path.dirname(exported_config),
|
return None
|
||||||
dst=linux,
|
return match.group(1)
|
||||||
keep_path=False,
|
|
||||||
|
|
||||||
|
def _cross_gcc(cross_compile):
|
||||||
|
return shlex.quote(f"{cross_compile or ''}gcc")
|
||||||
|
|
||||||
|
|
||||||
|
def _compiler_executable(conanfile, language):
|
||||||
|
executables = conanfile.conf.get("tools.build:compiler_executables", default={})
|
||||||
|
if not executables:
|
||||||
|
return None
|
||||||
|
if hasattr(executables, "get"):
|
||||||
|
return executables.get(language)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _install_config(conanfile, recipe_path, linux, make_base, defconfig):
|
||||||
|
defconfig = "" if defconfig is None else str(defconfig)
|
||||||
|
if not defconfig:
|
||||||
|
if not os.path.exists(os.path.join(linux, ".config")):
|
||||||
|
raise ConanInvalidConfiguration(
|
||||||
|
"Missing kernel config: pass defconfig or provide a source tree with .config"
|
||||||
)
|
)
|
||||||
os.replace(os.path.join(linux, defconfig), os.path.join(linux, ".config"))
|
return
|
||||||
return
|
|
||||||
|
|
||||||
self.run(f"{make_base} {shlex.quote(defconfig)}")
|
exported_config = os.path.join(recipe_path, "configs", defconfig)
|
||||||
|
if os.path.isfile(exported_config):
|
||||||
|
copy(
|
||||||
|
conanfile,
|
||||||
|
defconfig,
|
||||||
|
src=os.path.dirname(exported_config),
|
||||||
|
dst=linux,
|
||||||
|
keep_path=False,
|
||||||
|
)
|
||||||
|
os.replace(os.path.join(linux, defconfig), os.path.join(linux, ".config"))
|
||||||
|
return
|
||||||
|
|
||||||
def _capture(self, command):
|
conanfile.run(f"{make_base} {shlex.quote(defconfig)}")
|
||||||
output = subprocess.check_output(
|
|
||||||
command,
|
|
||||||
shell=True,
|
def _capture(command):
|
||||||
stderr=subprocess.STDOUT,
|
output = subprocess.check_output(
|
||||||
text=True,
|
command,
|
||||||
).strip()
|
shell=True,
|
||||||
if not output:
|
stderr=subprocess.STDOUT,
|
||||||
raise ConanInvalidConfiguration(f"Command produced no output: {command}")
|
text=True,
|
||||||
return output
|
).strip()
|
||||||
|
if not output:
|
||||||
|
raise ConanInvalidConfiguration(f"Command produced no output: {command}")
|
||||||
|
return output
|
||||||
|
|
||||||
|
|
||||||
|
class LinuxKbuildTreeConan(ConanFile):
|
||||||
|
name = "linux-kbuild-tree"
|
||||||
|
package_type = "python-require"
|
||||||
|
upload_policy = "skip"
|
||||||
|
|
||||||
|
exports = "configs/*"
|
||||||
|
|
|
||||||
|
|
@ -8,23 +8,26 @@ from conan.tools.files import copy
|
||||||
|
|
||||||
class TestPackageConan(ConanFile):
|
class TestPackageConan(ConanFile):
|
||||||
test_type = "explicit"
|
test_type = "explicit"
|
||||||
|
python_requires = "tested_reference_str"
|
||||||
|
|
||||||
settings = "os", "compiler", "build_type", "arch"
|
settings = "os", "compiler", "build_type", "arch"
|
||||||
exports_sources = "Makefile", "test_kbuild_module.c"
|
exports_sources = "Makefile", "test_kbuild_module.c"
|
||||||
|
|
||||||
def requirements(self):
|
|
||||||
self.requires(self.tested_reference_str)
|
|
||||||
|
|
||||||
def layout(self):
|
def layout(self):
|
||||||
self.folders.build = "build"
|
self.folders.build = "build"
|
||||||
self.folders.generators = os.path.join(self.folders.build, "generators")
|
self.folders.generators = os.path.join(self.folders.build, "generators")
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
kernel_tree = self.dependencies["linux-kbuild-tree"].package_folder
|
linux_kbuild_tree = self.python_requires["linux-kbuild-tree"].module
|
||||||
|
tree_info = linux_kbuild_tree.prepare(
|
||||||
|
self,
|
||||||
|
output=os.path.join(self.build_folder, "linux-kbuild-tree"),
|
||||||
|
)
|
||||||
|
|
||||||
|
kernel_tree = tree_info["path"]
|
||||||
module_dir = os.path.join(self.build_folder, "module")
|
module_dir = os.path.join(self.build_folder, "module")
|
||||||
copy(self, "*", src=self.source_folder, dst=module_dir)
|
copy(self, "*", src=self.source_folder, dst=module_dir)
|
||||||
|
|
||||||
tree_info = self._read_tree_info(kernel_tree)
|
|
||||||
arch = tree_info.get("arch", "")
|
arch = tree_info.get("arch", "")
|
||||||
cross_compile = tree_info.get("cross_compile", "")
|
cross_compile = tree_info.get("cross_compile", "")
|
||||||
jobs = self.conf.get("tools.build:jobs", default=None)
|
jobs = self.conf.get("tools.build:jobs", default=None)
|
||||||
|
|
@ -44,18 +47,3 @@ class TestPackageConan(ConanFile):
|
||||||
module_path = os.path.join(self.build_folder, "module", "test_kbuild_module.ko")
|
module_path = os.path.join(self.build_folder, "module", "test_kbuild_module.ko")
|
||||||
if not os.path.isfile(module_path):
|
if not os.path.isfile(module_path):
|
||||||
raise ConanException(f"Kernel module was not built: {module_path}")
|
raise ConanException(f"Kernel module was not built: {module_path}")
|
||||||
|
|
||||||
def _read_tree_info(self, kernel_tree):
|
|
||||||
path = os.path.join(kernel_tree, "kbuild-tree-info.txt")
|
|
||||||
if not os.path.isfile(path):
|
|
||||||
raise ConanException(f"Missing linux-kbuild-tree metadata: {path}")
|
|
||||||
|
|
||||||
result = {}
|
|
||||||
with open(path, encoding="utf-8") as tree_info:
|
|
||||||
for line in tree_info:
|
|
||||||
line = line.strip()
|
|
||||||
if not line or "=" not in line:
|
|
||||||
continue
|
|
||||||
key, value = line.split("=", 1)
|
|
||||||
result[key] = value
|
|
||||||
return result
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue