Skip to content

Commit 94db2b3

Browse files
authored
Merge pull request #2302 from mazunki/feat-nix-develop
support a nix-shell environment for editors
2 parents 21dd949 + c1d4e42 commit 94db2b3

File tree

3 files changed

+146
-18
lines changed

3 files changed

+146
-18
lines changed

README.md

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ int main(){
5858

5959
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.
6060

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

63-
### <a name="develop_kernel"></a> Kernel development
63+
### <a name="build_kernel"></a> Building IncludeOS
6464

6565
To build IncludeOS, run
6666

@@ -70,24 +70,35 @@ $ nix-build
7070

7171
This will build the toolchain and all IncludeOS kernel libraries.
7272

73-
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.
73+
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.
7474

75-
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`.
75+
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.
7676

77-
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`:
77+
### <a name="develop_kernel"></a> Kernel development workflow
78+
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`:
7879

7980
```bash
80-
$ nix-shell
81+
$ nix-shell ./develop.nix
8182
```
8283

83-
Further instructions will be shown for optionally configuring VM networking or overriding the build path when starting the shell.
84+
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.
8485

85-
By default th shell will also build the unikernel from `example.nix`. The example unikernel can be booted from within the shell:
86+
In summary:
87+
```bash
88+
~/repos/IncludeOS $ nix-shell ./develop.nix
89+
[nix-shell:~/repos/IncludeOS]$ cmake -B build && (cd build; make) # rebuilds IncludeOS
90+
[nix-shell:~/repos/IncludeOS]$ cd example
91+
[nix-shell:~/repos/IncludeOS/example]$ cmake -B build && (cd build; make) # rebuilds a bootable unikernel
92+
[nix-shell:~/repos/IncludeOS/example]$ boot ./build/hello_includeos.elf.bin # runs the unikernel image with qemu through vmrunner
93+
```
94+
95+
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.
8696

8797
```bash
88-
$ nix-shell
89-
[...]
90-
nix$ boot hello_includeos.elf.bin
98+
~/repos/IncludeOS $ nix-shell --run 'boot hello_includeos.elf.bin'
99+
# or
100+
~/repos/IncludeOS $ nix-shell # updating IncludeOS after entering this shell won't rebuild the os library automatically!
101+
[nix-shell:~/repos/IncludeOS]$ boot hello_includeos.elf.bin
91102
```
92103

93104
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:

develop.nix

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# develop.nix
2+
# sets up a development shell in which you can open your editor
3+
{
4+
buildpath ? "build",
5+
6+
# path to your unikernel source (project root with CMakeLists.txt)
7+
unikernel ? ".",
8+
9+
# optional: path to a local vmrunner checkout (otherwise use includeos.vmrunner)
10+
vmrunner ? "",
11+
12+
# Enable ccache support. See overlay.nix for details.
13+
withCcache ? true,
14+
15+
# Enable multicore suport.
16+
smp ? false,
17+
18+
includeos ? import ./default.nix { inherit withCcache smp; },
19+
20+
arch ? "x86_64"
21+
}:
22+
23+
# override stdenv for furhter derivations so they're in sync with includeos patch requirements
24+
includeos.pkgs.mkShell.override { inherit (includeos) stdenv; } rec {
25+
vmrunnerPkg =
26+
if vmrunner == "" then
27+
includeos.vmrunner
28+
else
29+
includeos.pkgs.callPackage (builtins.toPath /. + vmrunner) {};
30+
31+
# handy tools available in the shell
32+
packages = [
33+
(includeos.pkgs.python3.withPackages (p: [
34+
vmrunnerPkg
35+
]))
36+
includeos.pkgs.buildPackages.cmake
37+
includeos.pkgs.buildPackages.nasm
38+
includeos.pkgs.qemu
39+
includeos.pkgs.which
40+
includeos.pkgs.grub2
41+
includeos.pkgs.iputils
42+
includeos.pkgs.xorriso
43+
includeos.pkgs.jq
44+
];
45+
46+
# libraries/headers we include against
47+
buildInputs = [
48+
includeos
49+
includeos.chainloader
50+
includeos.lest
51+
includeos.pkgs.openssl
52+
includeos.pkgs.rapidjson
53+
];
54+
55+
shellHook = ''
56+
IOS_SRC=${toString ./.}
57+
if [ ! -d "$IOS_SRC" ]; then
58+
echo "$unikernel is not a valid directory" >&2
59+
return 1
60+
fi
61+
62+
echo "Configuring in: ${buildpath}"
63+
echo "Source tree: $IOS_SRC"
64+
65+
# delete old just in case it's dirty
66+
rm -rf ${buildpath}
67+
mkdir -p ${buildpath}
68+
69+
# build includeOS
70+
cmake -S "$IOS_SRC" -B ${buildpath} \
71+
-D CMAKE_EXPORT_COMPILE_COMMANDS=ON \
72+
-D ARCH=${arch} \
73+
-D CMAKE_MODULE_PATH=${includeos}/cmake
74+
75+
# procuced by CMake
76+
CCDB="${buildpath}/compile_commands.json"
77+
78+
#
79+
# attempting to use -resource-dir with 'clang++ -print-resource-dir'
80+
# doesn't work here as we're using -nostdlib/-nostdlibinc
81+
#
82+
tmp="$CCDB.clangd.tmp"
83+
jq \
84+
--arg libcxx "${includeos.libraries.libcxx.include}" \
85+
--arg libc "${includeos.libraries.libc}" \
86+
--arg localsrc "${toString ./.}" \
87+
'
88+
map(.command |= ( .
89+
+ " -isystem \($libcxx)"
90+
+ " -isystem \($libc)/include"
91+
| gsub("(?<a>-I)(?<b>/lib/LiveUpdate/include)"; .a + $localsrc + .b)
92+
))
93+
' "$CCDB" > "$tmp" && mv "$tmp" "$CCDB"
94+
95+
96+
# most clangd configurations and editors will look in ./build/, but this just makes it easier to find for some niche edge cases
97+
ln -sfn "${buildpath}/compile_commands.json" "$IOS_SRC/compile_commands.json"
98+
'';
99+
}
100+

overlay.nix

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,20 @@ final: prev: {
5656
};
5757
});
5858

59-
pkgsIncludeOS = prev.pkgsStatic.lib.makeScope prev.pkgsStatic.newScope (self: {
59+
pkgsIncludeOS = prev.pkgsStatic.lib.makeScope prev.pkgsStatic.newScope (self:
60+
let
61+
ccacheNoticeHook = prev.writeTextFile {
62+
name = "ccache-notice-hook";
63+
destination = "/nix-support/setup-hook";
64+
text = ''
65+
echo "====="
66+
echo "ccache is enabled!"
67+
echo "If you run into any issues, try: --arg withCcache false"
68+
echo "It's recommended to run tests with ccache disabled to avoid cache incoherencies."
69+
echo "====="
70+
'';
71+
};
72+
in {
6073
# self.callPackage will use this stdenv.
6174
stdenv = final.stdenvIncludeOS.includeos_stdenv;
6275

@@ -70,26 +83,30 @@ final: prev: {
7083
ccacheWrapper = prev.ccacheWrapper.override {
7184
inherit (self.stdenv) cc;
7285
extraConfig = ''
73-
export CCACHE_COMPRESS=1
7486
export CCACHE_DIR="/nix/var/cache/ccache"
75-
export CCACHE_UMASK=007
76-
export CCACHE_SLOPPINESS=random_seed
7787
if [ ! -d "$CCACHE_DIR" ]; then
7888
echo "====="
7989
echo "Directory '$CCACHE_DIR' does not exist"
8090
echo "Please create it with:"
8191
echo " sudo mkdir -m0770 '$CCACHE_DIR'"
8292
echo " sudo chown root:nixbld '$CCACHE_DIR'"
93+
echo ""
94+
echo 'Alternatively, disable ccache with `--arg withCcache false`'
8395
echo "====="
8496
exit 1
8597
fi
8698
if [ ! -w "$CCACHE_DIR" ]; then
8799
echo "====="
88-
echo "Directory '$CCACHE_DIR' is not accessible for user $(whoami)"
100+
echo "Directory '$CCACHE_DIR' exists, but is not accessible for user $(whoami)"
89101
echo "Please verify its access permissions"
102+
echo 'Alternatively, disable ccache with `--arg withCcache false`'
90103
echo "====="
91104
exit 1
92105
fi
106+
107+
export CCACHE_COMPRESS=1
108+
export CCACHE_UMASK=007
109+
export CCACHE_SLOPPINESS=random_seed
93110
'';
94111
};
95112

@@ -123,7 +140,7 @@ final: prev: {
123140
nativeBuildInputs = [
124141
prev.buildPackages.cmake
125142
prev.buildPackages.nasm
126-
] ++ prev.lib.optionals withCcache [self.ccacheWrapper];
143+
] ++ prev.lib.optionals withCcache [self.ccacheWrapper ccacheNoticeHook];
127144

128145
buildInputs = [
129146
self.botan2
@@ -145,7 +162,7 @@ final: prev: {
145162
cp -r -v ${final.stdenvIncludeOS.libraries.libcxx.include} $out/libcxx/include
146163
cp -r -v ${final.stdenvIncludeOS.libraries.libunwind} $out/libunwind
147164
cp -r -v ${final.stdenvIncludeOS.libraries.libgcc} $out/libgcc
148-
'';
165+
'';
149166

150167
archFlags = if self.stdenv.targetPlatform.system == "i686-linux" then
151168
[

0 commit comments

Comments
 (0)