Jump to content

Compiling kernels with envkernel.sh: Difference between revisions

From postmarketOS Wiki
Decatf (talk | contribs)
Create page on how to use envkernel.sh
 
update to gitlab.postmarketos.org
 
(20 intermediate revisions by 12 users not shown)
Line 1: Line 1:
This page details how to compile a kernel manually using the <code>envkernel.sh</code> helper script.
This page details how to compile a kernel manually using the <code>envkernel.sh</code> helper script as well as how to package and install a locally compiled kernel. This workflow can be used to quickly iterate and test during kernel development by using the existing kernel packaging and upgrade process to install a development kernel.


Usually when compiling a kernel, you would install [https://kernelnewbies.org/KernelBuild all dependencies] and a cross compiler in your host system. To make it easier, we have the <code>envkernel.sh</code> script (for fish shell: <code>envkernel.fish</code>). It sets up an Alpine Linux chroot with all the dependencies by using <code>pmbootstrap</code> internally, exports the environment variables to use the right cross compiler and creates an alias to <code>make</code>. This means, whenever you type <code>make</code> after sourcing that script, it will actually run <code>make</code> in the chroot.
Usually when compiling a kernel, you would install [https://kernelnewbies.org/KernelBuild all dependencies] and a cross compiler in your host system. To make it easier, we have the <code>envkernel.sh</code> script (for fish shell: <code>envkernel.fish</code>). It sets up an Alpine Linux chroot with all the dependencies by using <code>pmbootstrap</code> internally, exports the environment variables to use the right cross compiler and creates an alias to <code>make</code>. This means, whenever you type <code>make</code> after sourcing that script, it will actually run <code>make</code> in the chroot.


== Selecting a kernel ==
== Selecting a kernel ==


This section details how to fetch the kernel source of an existing postmarketOS device. Existing kernel aports can be found in the <code>aports/device</code> directory.
This section details how to fetch the kernel source of an existing postmarketOS device. Existing kernel aports can be found in the <code>pmaports/device</code> directory. Make sure to use your actual device name instead of the example name.


In the following example we will use the <code>oneplus-bacon</code> device. The kernel aport is in the [https://gitlab.com/postmarketOS/pmaports/blob/09ce31ca20b7d6b87a89cbb89a5aff7014e1d414/device/linux-oneplus-bacon/APKBUILD <code>aports/device/linux-oneplus-bacon</code>] directory. Within the APKBUILD is the <code>source</code> variable which lists the kernel source repository.   
The kernel aport is in the <code>pmaports/device/linux-wiki-example</code> directory. Within the APKBUILD is the <code>source</code> variable which lists the kernel source repository.   


After determining the kernel you want to work with, download the source code.   
After determining the kernel you want to work with, download the source code.   
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
$ git clone https://github.com/LineageOS/android_kernel_oneplus_msm8974.git
$ git clone https://github.com/LineageOS/android_kernel_wiki_example.git
$ cd android_kernel_oneplus_msm8974
$ cd android_kernel_wiki_example
</syntaxhighlight>
</syntaxhighlight>


Line 20: Line 20:


<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
$ git checkout dd65620ba04a8c6ba0e30553c9c95388daefae02
$ git checkout dd65620ba04a8c6ba0e30553c9c95388daefae02 # example
</syntaxhighlight>
</syntaxhighlight>


=== Applying patches ===
=== Applying patches ===
Some kernel aports have patches applied on top of the remote source. You may want to apply these patches if they aren't in the revision you have checked out.   
Some kernel aports have patches applied on top of the remote source. You may want to apply these patches if they aren't in the source tree you have checked out.   


For example <code>oneplus-bacon</code> has a patch which isn't in the git revision in the prior example.   
Our example device <code>wiki-example</code> has a patch which isn't in the git revision we just checked out.   
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
$ git apply /code/pmboostrap/aports/device/linux-oneplus-bacon/02_gpu-msm-fix-gcc5-compile.patch
$ git apply pmaports/device/linux-wiki-example/02_fix-example.patch
</syntaxhighlight>
</syntaxhighlight>


Kernels which can only be compiled with older GCC versions may need the <code>compiler-gcc6.h</code>. If this header exists in the kernel aport then copy it to the kernel source directory.
== Compile the kernel ==
<syntaxhighlight lang="shell-session">
$ cp /code/pmboostrap/aports/device/linux-oneplus-bacon/compiler-gcc6.h include/linux/
</syntaxhighlight>


== Compile the kernel ==
{{note|The <code>envkernel.sh</code> script is not provided when you installed pmbootstrap from pip. You have to install pmbootstrap from git to have it.}}


The <code>envkernel.sh</code> script will set up a build environment based on the target device configured for <code>pmbootstrap</code>.   
The <code>envkernel.sh</code> script will set up a build environment based on the target device configured for <code>pmbootstrap</code>.   


In this example we are using the <code>oneplus-bacon</code> device. So we should be sure to have run <code>pmbootstrap init</code> and select <code>oneplus-bacon</code> as the target device. Run the <code>envkernel.sh</code> script once in the kernel source directory to initialize the build environment. If the kernel can only be compiled with GCC6 then pass the <code>--gcc6</code> flag.
Make sure to replace the example names with your correct device name. When running <code>pmbootstrap init</code>, the correct target device needs to be selected. Most devices should include a link to their kernel package at the bottom of their wiki page.
 
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
$ source /code/pmbootstrap/envkernel.sh --gcc6
$ pmbootstrap init
</syntaxhighlight>
</syntaxhighlight>


Copy the defconfig from the kernel aport to the kernel source directory.   
Run the <code>envkernel.sh</code> script (found in the pmbootstrap git repository) once in the kernel source directory to initialize the build environment. If the kernel can only be compiled with GCC6 then pass the <code>--gcc6</code> flag.   
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
$ cp /code/pmboostrap/aports/device/linux-oneplus-bacon/config-oneplus-bacon.armhf arch/arm/oneplus_bacon_defconfig
$ source pmbootstrap/helpers/envkernel.sh --gcc6
</syntaxhighlight>
 
Copy the defconfig from the kernel aport to the kernel source directory. Replace the defconfig file that your build would use.
<syntaxhighlight lang="shell-session">
$ cp ~/.local/var/pmbootstrap/cache_git/pmaports/device/testing/linux-wiki-example/config-linux-wiki-example.aarch64 arch/arm64/configs/example_defconfig
</syntaxhighlight>
</syntaxhighlight>


Build the defconfig and kernel with the following commands.   
Build the defconfig and kernel with the following commands.   
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
$ make oneplus_bacon_defconfig
$ make example_defconfig
$ make
$ make
</syntaxhighlight>
</syntaxhighlight>
Line 61: Line 64:
Some kernels require extra packaging steps such as <code>dtbtool</code>. The <code>envkernel.sh</code> build environment works within the pmbootstrap chroots. The <code>run-script</code> alias can be used to execute scripts within the source or build directory in the chroot.
Some kernels require extra packaging steps such as <code>dtbtool</code>. The <code>envkernel.sh</code> build environment works within the pmbootstrap chroots. The <code>run-script</code> alias can be used to execute scripts within the source or build directory in the chroot.


In this example we are using the <code>oneplus-bacon</code> which requires <code>dtbtool</code>. Create a script with the <code>dtbtool</code> command named <code>post-make.sh</code> in the kernel source directory.
This example explains how to deal with a device thatr requires <code>dtbtool</code>. Create a script with the <code>dtbtool</code> command named <code>post-make.sh</code> in the kernel source directory.
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
#!/bin/sh
#!/bin/sh
Line 75: Line 78:


=== Packaging and flashing the kernel ===
=== Packaging and flashing the kernel ===
A kernel compiled under <code>envkernel.sh</code> can be packaged by pmbootstrap by using the <code>--envkernel</code> build option.
A kernel compiled under <code>envkernel.sh</code> can be packaged by pmbootstrap by using the <code>--envkernel</code> build option.
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
$ pmbootstrap build --envkernel linux-oneplus-bacon
$ pmbootstrap build --envkernel linux-wiki-example
</syntaxhighlight>
 
Usually if the device boots to SSH with USB networking and has support for kernel upgrades, you can install the new kernel by sideloading.
<syntaxhighlight lang="shell-session">
$ pmbootstrap sideload linux-wiki-example
</syntaxhighlight>
</syntaxhighlight>


After compiling and packaging the kernel, it can be flashed to device using the standard pmbootstrap flash commands.
After compiling and packaging the kernel, it can be flashed to device using the standard pmbootstrap flash commands. Note that if your kernel includes kernel modules (most mainline kernels do) these will not be installed and some features might break when you boot back up. In this case you can sideload as above once you have a booting kernel.
<syntaxhighlight lang="shell-session">
<syntaxhighlight lang="shell-session">
$ pmbootstrap flasher flash_kernel
$ pmbootstrap flasher flash_kernel
</syntaxhighlight>
</syntaxhighlight>
== Packaging kernels built without envkernel ==
As of {{MR|2175}} the envkernel packaging logic can be used to package kernels built directly on the host. This is intended to allow folks who prefer to set up their own kernel building environment to still make use of pmbootstrap for packaging and sideloading the new kernel.
Currently this requires that you use <code>.output</code> as your output directory (add <code>O=.output</code> as an argument to make). With that done just build your kernel as normal and follow the packaging/flashing steps above. Note that the output directory needs to be set for any make invocation, including things like defconfig or menuconfig. If you have a <code>.config</code> in the source tree (instead of in <code>.output</code>) you will get error messages about the source directory not being "clean" when you try to build the kernel with <code>O=.output</code>.
== Clangd ==
If you want to use the Clangd language server while working on the kernel, you need to generate the <code>compile_commands.json</code> file for your build. The kernel source contains the <code>scripts/clang-tools/gen_compile_commands.py</code> script to do that. It works with envkernel or independent cross compiling, but you will need to tell it where to find the build output (same value as for <code>O=</code>):
<syntaxhighlight lang="shell-session">
$ ./scripts/clang-tools/gen_compile_commands.py -d .output/
</syntaxhighlight>
If you built using envkernel the <code>compile_commands.json</code> file might contain <code>/mnt/linux/</code> (the mount point for kernel source inside the chroot) in a few places. You'll need to replace that with the correct path.
If your build was done using GCC (as is the default with envkernel) the build commands might use some options Clang and thus Clangd don't understand, and you might need to tell Clang the target architecture. You can override compile flags in a [https://clangd.llvm.org/config.html <code>.clangd</code> config file]. The config might look similar to this, please check which options you really need for your own build (incorrect options could also cause issues):
<syntaxhighlight lang="yaml">
CompileFlags:
  Compiler: clang
  Add:
    - -xc  # C only, no C++
    - --target=aarch64
  Remove:
    - -fno-allow-store-data-races
    - -fconserve-stack
    - -march=*
    - -mabi=*
</syntaxhighlight>
== See also ==
* {{MR|1703}} optional gcc6 argument
* {{MR|1747}} pmbootstrap build --envkernel
* [https://gitlab.postmarketos.org/postmarketOS/pmbootstrap/issues/1672#note_105668759 a quick Q&A about using envkernel.sh for the Nexus 5] (if you have more questions, please open a new issue or ask in the chat).
* {{MR|2175}} support for using the envkernel packaging logic on kernels built on the host


[[Category:Guide]]
[[Category:Guide]]

Latest revision as of 09:30, 3 November 2024

This page details how to compile a kernel manually using the envkernel.sh helper script as well as how to package and install a locally compiled kernel. This workflow can be used to quickly iterate and test during kernel development by using the existing kernel packaging and upgrade process to install a development kernel.

Usually when compiling a kernel, you would install all dependencies and a cross compiler in your host system. To make it easier, we have the envkernel.sh script (for fish shell: envkernel.fish). It sets up an Alpine Linux chroot with all the dependencies by using pmbootstrap internally, exports the environment variables to use the right cross compiler and creates an alias to make. This means, whenever you type make after sourcing that script, it will actually run make in the chroot.

Selecting a kernel

This section details how to fetch the kernel source of an existing postmarketOS device. Existing kernel aports can be found in the pmaports/device directory. Make sure to use your actual device name instead of the example name.

The kernel aport is in the pmaports/device/linux-wiki-example directory. Within the APKBUILD is the source variable which lists the kernel source repository.

After determining the kernel you want to work with, download the source code.

$ git clone https://github.com/LineageOS/android_kernel_wiki_example.git
$ cd android_kernel_wiki_example

Preparing the source directory

To compile the current version of the kernel, check out the git revision found in the APKBUILD.

$ git checkout dd65620ba04a8c6ba0e30553c9c95388daefae02 # example

Applying patches

Some kernel aports have patches applied on top of the remote source. You may want to apply these patches if they aren't in the source tree you have checked out.

Our example device wiki-example has a patch which isn't in the git revision we just checked out.

$ git apply pmaports/device/linux-wiki-example/02_fix-example.patch

Compile the kernel

Note The envkernel.sh script is not provided when you installed pmbootstrap from pip. You have to install pmbootstrap from git to have it.

The envkernel.sh script will set up a build environment based on the target device configured for pmbootstrap.

Make sure to replace the example names with your correct device name. When running pmbootstrap init, the correct target device needs to be selected. Most devices should include a link to their kernel package at the bottom of their wiki page.

$ pmbootstrap init

Run the envkernel.sh script (found in the pmbootstrap git repository) once in the kernel source directory to initialize the build environment. If the kernel can only be compiled with GCC6 then pass the --gcc6 flag.

$ source pmbootstrap/helpers/envkernel.sh --gcc6

Copy the defconfig from the kernel aport to the kernel source directory. Replace the defconfig file that your build would use.

$ cp ~/.local/var/pmbootstrap/cache_git/pmaports/device/testing/linux-wiki-example/config-linux-wiki-example.aarch64 arch/arm64/configs/example_defconfig

Build the defconfig and kernel with the following commands.

$ make example_defconfig
$ make

Run the kernel

Post make scripts

Some kernels require extra packaging steps such as dtbtool. The envkernel.sh build environment works within the pmbootstrap chroots. The run-script alias can be used to execute scripts within the source or build directory in the chroot.

This example explains how to deal with a device thatr requires dtbtool. Create a script with the dtbtool command named post-make.sh in the kernel source directory.

#!/bin/sh
dtbTool -s 2048 -p "${srcdir}/scripts/dtc/" -o \
	"${builddir}/arch/arm/boot/dt.img" \
	"${builddir}/arch/arm/boot"

Run the script.

$ run-script post-make.sh

Packaging and flashing the kernel

A kernel compiled under envkernel.sh can be packaged by pmbootstrap by using the --envkernel build option.

$ pmbootstrap build --envkernel linux-wiki-example

Usually if the device boots to SSH with USB networking and has support for kernel upgrades, you can install the new kernel by sideloading.

$ pmbootstrap sideload linux-wiki-example

After compiling and packaging the kernel, it can be flashed to device using the standard pmbootstrap flash commands. Note that if your kernel includes kernel modules (most mainline kernels do) these will not be installed and some features might break when you boot back up. In this case you can sideload as above once you have a booting kernel.

$ pmbootstrap flasher flash_kernel

Packaging kernels built without envkernel

As of !2175 the envkernel packaging logic can be used to package kernels built directly on the host. This is intended to allow folks who prefer to set up their own kernel building environment to still make use of pmbootstrap for packaging and sideloading the new kernel.

Currently this requires that you use .output as your output directory (add O=.output as an argument to make). With that done just build your kernel as normal and follow the packaging/flashing steps above. Note that the output directory needs to be set for any make invocation, including things like defconfig or menuconfig. If you have a .config in the source tree (instead of in .output) you will get error messages about the source directory not being "clean" when you try to build the kernel with O=.output.

Clangd

If you want to use the Clangd language server while working on the kernel, you need to generate the compile_commands.json file for your build. The kernel source contains the scripts/clang-tools/gen_compile_commands.py script to do that. It works with envkernel or independent cross compiling, but you will need to tell it where to find the build output (same value as for O=):

$ ./scripts/clang-tools/gen_compile_commands.py -d .output/

If you built using envkernel the compile_commands.json file might contain /mnt/linux/ (the mount point for kernel source inside the chroot) in a few places. You'll need to replace that with the correct path.

If your build was done using GCC (as is the default with envkernel) the build commands might use some options Clang and thus Clangd don't understand, and you might need to tell Clang the target architecture. You can override compile flags in a .clangd config file. The config might look similar to this, please check which options you really need for your own build (incorrect options could also cause issues):

CompileFlags:
  Compiler: clang
  Add:
    - -xc  # C only, no C++
    - --target=aarch64
  Remove:
    - -fno-allow-store-data-races
    - -fconserve-stack
    - -march=*
    - -mabi=*

See also