Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ int main(){

A full "Hello world" service with a working nix workflow is available in the [hello world repo](https://github.com/includeos/hello_world). The repository can also be used as a a starting point for developing your own IncludeOS service.

For more advanced service examples see the the integration tests (under ./IncludeOS/test/\*/integration).
For more advanced service examples see the the integration tests (under `./IncludeOS/test/\*/integration`).

### <a name="develop_kernel"></a> Kernel development
### <a name="build_kernel"></a> Building IncludeOS

To build IncludeOS, run

Expand All @@ -70,24 +70,35 @@ $ nix-build

This will build the toolchain and all IncludeOS kernel libraries.

Note that the first build will take some time to complete, as the IncludeOS toolchain is rebuilt from source code. This includes clang, llvm, libcxx, musl and so on. There is no nix binary cache available for these files at the moment. Subsequent builds will go much faster when the toolchain has been cached in the local nix-store.
Note that the first build will take some time (~7 hours on our machines) to complete, as the IncludeOS toolchain is rebuilt from source code. This includes clang, llvm, libcxx, musl and so on. There is no nix binary cache available for these files at the moment. Subsequent builds will go much faster when the toolchain has been cached in the local nix-store.

After making changes to the kernel, run `nix-build` again to get new binaries. If you are iterating on changes in one section of the kernel you can speed up the build significantly by using ccache. All `nix-build` and `nix-shell` commands in this section support the optional parameter `--arg withCcache true`.
After making changes to the kernel, run `nix-build` again to get new binaries. If you are iterating on changes in one section of the kernel you can speed up the build significantly (from a few minutes to a matter of seconds) by using ccache. All `nix-build` and `nix-shell` commands in this section support the optional parameter `--arg withCcache <true|false>` (this is only enabled by default for the development shell). We strongly advise setting up the ccache before you start iterating on the code. For NixOS, you can [check the wiki](https://nixos.wiki/wiki/CCache) on how to do this, other distros will have different instructions.

It's not always practical to rebuild the whole kernel during development. You can get a development shell with a preconfigured environment using `shell.nix`:
### <a name="develop_kernel"></a> Kernel development workflow
It's not always practical to rebuild the whole kernel during development. You can get a development shell with a preconfigured environment using `develop.nix`:

```bash
$ nix-shell
$ nix-shell ./develop.nix
```

Further instructions will be shown for optionally configuring VM networking or overriding the build path when starting the shell.
From here, you will be put in a shell suited for development. If your editor has LSP support with clangd (e.g. neovim, emacs, VSCode, Zed), you will get proper goto-declarations and warnings. You can quickly rebuild the kernel library from here with `cmake -B build && (cd build; make)`, with all build dependencies handled by the shell. You can also build and run a unikernel from the shell with this oneliner: `(cd ./example && cmake -B build && (cd build && make) && boot ./build/hello_includeos.elf.bin)` after building IncludeOS. It might be convenient to use one terminal (or tab) for your editor, a second for building IncludeOS, and a third for building a unikernel.

By default th shell will also build the unikernel from `example.nix`. The example unikernel can be booted from within the shell:
In summary:
```bash
~/repos/IncludeOS $ nix-shell ./develop.nix
[nix-shell:~/repos/IncludeOS]$ cmake -B build && (cd build; make) # rebuilds IncludeOS
[nix-shell:~/repos/IncludeOS]$ cd example
[nix-shell:~/repos/IncludeOS/example]$ cmake -B build && (cd build; make) # rebuilds a bootable unikernel
[nix-shell:~/repos/IncludeOS/example]$ boot ./build/hello_includeos.elf.bin # runs the unikernel image with qemu through vmrunner
```

Alternatively, you may want to use `nix-shell` by itself (or nested inside the development shell), which handles both building the unikernel found under `./example/` and puts you in a convenient shell for testing out a unikernel.

```bash
$ nix-shell
[...]
nix$ boot hello_includeos.elf.bin
~/repos/IncludeOS $ nix-shell --run 'boot hello_includeos.elf.bin'
# or
~/repos/IncludeOS $ nix-shell # updating IncludeOS after entering this shell won't rebuild the os library automatically!
[nix-shell:~/repos/IncludeOS]$ boot hello_includeos.elf.bin
```

If you want to build a different unikernel than the example, this can be specified with the `--argstr unikernel [path]` parameter. This is primarily used for integration tests. For example, to build and run the stacktrace-test:
Expand Down
100 changes: 100 additions & 0 deletions develop.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# develop.nix
# sets up a development shell in which you can open your editor
{
buildpath ? "build",

# path to your unikernel source (project root with CMakeLists.txt)
unikernel ? ".",

# optional: path to a local vmrunner checkout (otherwise use includeos.vmrunner)
vmrunner ? "",

# Enable ccache support. See overlay.nix for details.
withCcache ? true,

# Enable multicore suport.
smp ? false,

includeos ? import ./default.nix { inherit withCcache smp; },

arch ? "x86_64"
}:

# override stdenv for furhter derivations so they're in sync with includeos patch requirements
includeos.pkgs.mkShell.override { inherit (includeos) stdenv; } rec {
vmrunnerPkg =
if vmrunner == "" then
includeos.vmrunner
else
includeos.pkgs.callPackage (builtins.toPath /. + vmrunner) {};

# handy tools available in the shell
packages = [
(includeos.pkgs.python3.withPackages (p: [
vmrunnerPkg
]))
includeos.pkgs.buildPackages.cmake
includeos.pkgs.buildPackages.nasm
includeos.pkgs.qemu
includeos.pkgs.which
includeos.pkgs.grub2
includeos.pkgs.iputils
includeos.pkgs.xorriso
includeos.pkgs.jq
];

# libraries/headers we include against
buildInputs = [
includeos
includeos.chainloader
includeos.lest
includeos.pkgs.openssl
includeos.pkgs.rapidjson
];

shellHook = ''
IOS_SRC=${toString ./.}
if [ ! -d "$IOS_SRC" ]; then
echo "$unikernel is not a valid directory" >&2
return 1
fi

echo "Configuring in: ${buildpath}"
echo "Source tree: $IOS_SRC"

# delete old just in case it's dirty
rm -rf ${buildpath}
mkdir -p ${buildpath}

# build includeOS
cmake -S "$IOS_SRC" -B ${buildpath} \
-D CMAKE_EXPORT_COMPILE_COMMANDS=ON \
-D ARCH=${arch} \
-D CMAKE_MODULE_PATH=${includeos}/cmake

# procuced by CMake
CCDB="${buildpath}/compile_commands.json"

#
# attempting to use -resource-dir with 'clang++ -print-resource-dir'
# doesn't work here as we're using -nostdlib/-nostdlibinc
#
tmp="$CCDB.clangd.tmp"
jq \
--arg libcxx "${includeos.libraries.libcxx.include}" \
--arg libc "${includeos.libraries.libc}" \
--arg localsrc "${toString ./.}" \
'
map(.command |= ( .
+ " -isystem \($libcxx)"
+ " -isystem \($libc)/include"
| gsub("(?<a>-I)(?<b>/lib/LiveUpdate/include)"; .a + $localsrc + .b)
))
' "$CCDB" > "$tmp" && mv "$tmp" "$CCDB"


# most clangd configurations and editors will look in ./build/, but this just makes it easier to find for some niche edge cases
ln -sfn "${buildpath}/compile_commands.json" "$IOS_SRC/compile_commands.json"
'';
}

31 changes: 24 additions & 7 deletions overlay.nix
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,20 @@ final: prev: {
};
});

pkgsIncludeOS = prev.pkgsStatic.lib.makeScope prev.pkgsStatic.newScope (self: {
pkgsIncludeOS = prev.pkgsStatic.lib.makeScope prev.pkgsStatic.newScope (self:
let
ccacheNoticeHook = prev.writeTextFile {
name = "ccache-notice-hook";
destination = "/nix-support/setup-hook";
text = ''
echo "====="
echo "ccache is enabled!"
echo "If you run into any issues, try: --arg withCcache false"
echo "It's recommended to run tests with ccache disabled to avoid cache incoherencies."
echo "====="
'';
};
in {
# self.callPackage will use this stdenv.
stdenv = final.stdenvIncludeOS.includeos_stdenv;

Expand All @@ -70,26 +83,30 @@ final: prev: {
ccacheWrapper = prev.ccacheWrapper.override {
inherit (self.stdenv) cc;
extraConfig = ''
export CCACHE_COMPRESS=1
export CCACHE_DIR="/nix/var/cache/ccache"
export CCACHE_UMASK=007
export CCACHE_SLOPPINESS=random_seed
if [ ! -d "$CCACHE_DIR" ]; then
echo "====="
echo "Directory '$CCACHE_DIR' does not exist"
echo "Please create it with:"
echo " sudo mkdir -m0770 '$CCACHE_DIR'"
echo " sudo chown root:nixbld '$CCACHE_DIR'"
echo ""
echo 'Alternatively, disable ccache with `--arg withCcache false`'
echo "====="
exit 1
fi
if [ ! -w "$CCACHE_DIR" ]; then
echo "====="
echo "Directory '$CCACHE_DIR' is not accessible for user $(whoami)"
echo "Directory '$CCACHE_DIR' exists, but is not accessible for user $(whoami)"
echo "Please verify its access permissions"
echo 'Alternatively, disable ccache with `--arg withCcache false`'
echo "====="
exit 1
fi

export CCACHE_COMPRESS=1
export CCACHE_UMASK=007
export CCACHE_SLOPPINESS=random_seed
'';
};

Expand Down Expand Up @@ -123,7 +140,7 @@ final: prev: {
nativeBuildInputs = [
prev.buildPackages.cmake
prev.buildPackages.nasm
] ++ prev.lib.optionals withCcache [self.ccacheWrapper];
] ++ prev.lib.optionals withCcache [self.ccacheWrapper ccacheNoticeHook];

buildInputs = [
self.botan2
Expand All @@ -145,7 +162,7 @@ final: prev: {
cp -r -v ${final.stdenvIncludeOS.libraries.libcxx.include} $out/libcxx/include
cp -r -v ${final.stdenvIncludeOS.libraries.libunwind} $out/libunwind
cp -r -v ${final.stdenvIncludeOS.libraries.libgcc} $out/libgcc
'';
'';

archFlags = if self.stdenv.targetPlatform.system == "i686-linux" then
[
Expand Down