]> git.ipfire.org Git - thirdparty/pdns.git/blame - DEVELOPMENT.md
Meson: Use include_directories for pgsqlbackend
[thirdparty/pdns.git] / DEVELOPMENT.md
CommitLineData
d4ad3054
FM
1PowerDNS Development Environment
2--------------------------------
3
4Thank you for you interest to contribute to the PowerDNS project.
5This document will explain one way to set up a development environment based on the language server protocol (LSP) when working on PowerDNS.
6
7# Introduction
8
9The environment will consist of setting up the [`clangd`](https://clangd.llvm.org/) C/C++ language server to enable fancy IDE features for development.
10[`ccls`](https://github.com/MaskRay/ccls) can also be used in place of `clangd`.
11
12Furthermore, additional [on-the-fly checks using `clang-tidy`](#on-the-fly-clang-tidy) can be easily enabled.
13
14On some systems, `clangd` and `clang-tidy` are available as packages separate from the `clang` package.
15Ensure that you have all three binaries available and running on your system.
16
17# Compilation Database
18
19For projects with non-trivial build systems, like PowerDNS, `clangd` requires a [compilation database](https://clang.llvm.org/docs/JSONCompilationDatabase.html).
20
77f8022f 21Since PowerDNS' autotools-based build system does not have native support for generating such a database, an external tool like [Bear (the Build EAR)](https://github.com/rizsotto/Bear) or [compiledb](https://pypi.org/project/compiledb) can be used.
d4ad3054 22
77f8022f 23## Using Bear
d4ad3054
FM
24Once you have `bear` installed, configure a build of your choice (either the PowerDNS `auth`, `recursor` or `dnsdist`) using `clang` and `clang++`:
25
26```sh
27make distclean # Ensure we rebuild all files so that bear can pick them up.
28CC=clang CXX=clang++ ./configure --with-modules=gsqlite3 --disable-lua-records
29```
30
31We can now build PowerDNS using `bear` and `make` which produces a compilation database file named `compile_commands.json`:
32
33```sh
34bear --append -- make -j 8
35```
36
77f8022f
OM
37## Using compiledb
38Once you have `compiledb` installed, configure the build and run compiledb:
39
40```sh
41make distclean # Ensure we rebuild all files so that bear can pick them up.
42CC=clang CXX=clang++ ./configure ...
43make -nwk | /path/to/compiledb -o- > compile_commands.json
44```
45
46to generate the compilation database.
47For the authoritative server, the configure command is run in the top level directory, while the compiledb command should be run in the `pdns` subdirectory.
48
49# Seting up the LSP client
50
d4ad3054
FM
51Once the compilation database is generated, you can now move onto setting up an LSP client in your editor or IDE.
52
53Note that the process of generating the compilation database file only needs to be run when a file is added to the project or when build flags change (e.g. dependencies are added).
54
55# Editors
56
57## Emacs
58
59This section explains how to set up [Emacs](https://www.gnu.org/software/emacs/) with [LSP Mode](https://emacs-lsp.github.io/) for C/C++ development using `clangd` which supports [on-the-fly checking](https://www.flycheck.org/en/latest/) and [auto-completion](https://company-mode.github.io/), among many other features.
60
b4c7e352
FM
61Instructions for an alternative, more minimal, setup using [Eglot](https://github.com/joaotavora/eglot) [are also available](#minimal-emacs).
62
d4ad3054
FM
63Code snippets below should be added to your Emacs init file (e.g. `~/.config/emacs/init`).
64
65We'll start by enabling Emacs package repositories and declaring which packages we would like to have installed:
66
67```elisp
68(with-eval-after-load 'package
69 (setq package-archives
70 '(("gnu" . "https://elpa.gnu.org/packages/")
71 ("melpa" . "https://melpa.org/packages/")))
72 (push 'company package-selected-packages)
73 (push 'flycheck package-selected-packages)
74 (push 'lsp-mode package-selected-packages)
75 (push 'lsp-ui package-selected-packages)
76 (push 'lsp-treemacs package-selected-packages))
77```
78
79To avoid restarting Emacs, you can evaluate that previous s-expression by pointing at the last parenthesis and using `C-x C-e` or selecting the block and using `M-x eval-region`.
80
81Once done, run `M-x package-refresh-contents`, then `M-x package-install-selected-packages`.
82This should install some packages for you.
83
84Now let's set up our common programming mode, this enables the following features:
85
86* Highlighting the current line.
87* Displaying line numbers.
88* Auto-inserting indentation.
89* Auto-inserting closing parenthesis, bracket, etc...
90* Auto-completion.
91* On-the-fly code checking.
92* On-the-fly spell checking.
93* Highlighting matching parentheses, brackets, etc...
94* Auto-displaying documentation briefs in the echo area.
95
96```elisp
97(with-eval-after-load 'prog-mode
98 (add-hook 'prog-mode-hook #'hl-line-mode)
99 (add-hook 'prog-mode-hook #'display-line-numbers-mode)
100 (add-hook 'prog-mode-hook #'electric-layout-mode)
101 (add-hook 'prog-mode-hook #'electric-pair-mode)
102 (add-hook 'prog-mode-hook #'company-mode)
103 (add-hook 'prog-mode-hook #'flycheck-mode)
104 (add-hook 'prog-mode-hook #'flyspell-prog-mode)
105 (add-hook 'prog-mode-hook #'show-paren-mode)
106 (add-hook 'prog-mode-hook #'eldoc-mode))
107```
108
109Now let's set up `flycheck` for on-the-fly code checking, this adds the following key bindings:
110
111* `M-n` to jump to the next error.
112* `M-p` to jump to the previous error.
113
114```elisp
115(with-eval-after-load 'flycheck
116 (define-key flycheck-mode-map (kbd "M-n") #'flycheck-next-error)
117 (define-key flycheck-mode-map (kbd "M-p") #'flycheck-previous-error)
118 (setq flycheck-checker-error-threshold nil)
119 (setq flycheck-check-syntax-automatically
120 '(idle-change new-line mode-enabled idle-buffer-switch))
121 (setq flycheck-idle-change-delay 0.25)
122 (setq flycheck-idle-buffer-switch-delay 0.25))
123```
124
125And set up `company-mode` for auto-completion:
126
127```elisp
128(with-eval-after-load 'company
129 (setq company-backends '((company-capf company-files company-keywords)))
130 (setq completion-ignore-case t)
131 (setq company-minimum-prefix-length 1)
132 (setq company-selection-wrap-around t)
133 (define-key company-mode-map (kbd "<tab>") #'company-indent-or-complete-common))
134```
135
136Then set up `lsp-mode` to integrate everything together, which enables the following additional features:
137
138* Header breadcrumbs showing the path to the current item under point.
139* Semantic syntax highlighting as opposed to regex-based ones.
140
141And adds the following key bindings:
142
143* `F2` to switch between implementation and header file.
144* `C-c f` to format the current file according to `clang-format`.
145* `C-c g` to format the selected region according to `clang-format`.
146* `C-c r` to reliably rename the item under point based on `clangd`.
147* `C-c h` to show code documentation about the item under point.
148* `C-c =` to expand selection outwards from the item under point.
149* `M-RET` to list and run available language server code actions.
150* `C-c x` to navigate to any symbol in the project.
151* `C-c e` to show a navigation list of errors/warnings in the project.
152* `C-c s` to show a navigation list of symbols in the project.
153* `C-c c` to show the call hierarchy of a method or function.
154* `C-c t` to show the type/inheritance hierarchy of a type.
155
156```elisp
157(with-eval-after-load 'lsp-mode
158 (define-key lsp-mode-map (kbd "C-c f") #'lsp-format-buffer)
159 (define-key lsp-mode-map (kbd "C-c g") #'lsp-format-region)
160 (define-key lsp-mode-map (kbd "C-c r") #'lsp-rename)
161 (define-key lsp-mode-map (kbd "C-c h") #'lsp-describe-thing-at-point)
162 (define-key lsp-mode-map (kbd "C-=" ) #'lsp-extend-selection)
163 (define-key lsp-mode-map (kbd "M-RET") #'lsp-execute-code-action)
164 (define-key lsp-mode-map (kbd "C-c e") #'lsp-treemacs-errors-list)
165 (define-key lsp-mode-map (kbd "C-c s") #'lsp-treemacs-symbols)
166 (define-key lsp-mode-map (kbd "C-c c") #'lsp-treemacs-call-hierarchy)
167 (define-key lsp-mode-map (kbd "C-c t") #'lsp-treemacs-type-hierarchy)
168 (add-hook 'lsp-mode-hook #'lsp-treemacs-sync-mode)
169 (setq lsp-progress-prefix " Progress: ")
170 (setq lsp-completion-provider :none) ; Company-capf is already set
171 (setq lsp-headerline-breadcrumb-enable t)
172 (setq lsp-restart 'auto-restart)
173 (setq lsp-enable-snippet nil)
174 (setq lsp-keymap-prefix "C-c")
175 (setq lsp-idle-delay 0.1)
176 (setq lsp-file-watch-threshold nil)
177 (setq lsp-enable-semantic-highlighting t)
178 (setq lsp-enable-indentation t)
179 (setq lsp-enable-on-type-formatting t)
180 (setq lsp-before-save-edits nil)
181 (setq lsp-auto-configure t)
182 (setq lsp-signature-render-documentation t)
183 (setq lsp-modeline-code-actions-enable nil)
184 (setq lsp-log-io nil)
185 (setq lsp-enable-imenu nil))
186
187(with-eval-after-load 'lsp-headerline
188 (setq lsp-headerline-breadcrumb-icons-enable nil))
189
190(with-eval-after-load 'lsp-semantic-tokens
191 (setq lsp-semantic-tokens-apply-modifiers t))
192
193(with-eval-after-load 'lsp-clangd
194 (setq lsp-clients-clangd-args
195 '("--header-insertion-decorators"
196 "--all-scopes-completion"
197 "--clang-tidy"
198 "--completion-style=detailed"
199 "--header-insertion=never"
200 "--inlay-hints"
201 "--limit-results=1000"
202 "-j=4"
203 "--malloc-trim"
204 "--pch-storage=memory"))
205 (with-eval-after-load 'cc-mode
206 (define-key c-mode-base-map (kbd "<f2>") #'lsp-clangd-find-other-file)))
207
208(with-eval-after-load 'treemacs-interface
209 (global-set-key (kbd "<f12>") #'treemacs-delete-other-windows))
210
211(with-eval-after-load 'treemacs-customization
212 (setq treemacs-width 70))
213
214(with-eval-after-load 'treemacs-mode
215 (add-hook 'treemacs-mode-hook #'toggle-truncate-lines))
216```
217
218And now we set up `lsp-ui-mode` to provide a few more features and key bindings:
219
220* `C-c d` to show rendered documentation.
221* `M-.` to peek at the definition of the item under point.
222* `M-?` to peek at references to the item under point.
223* `M-I` to peek at implementations of virtual methods.
224* `M-,` to jump back.
225
226```elisp
227(with-eval-after-load 'lsp-ui-flycheck
228 (setq lsp-ui-flycheck-enable t))
229
230(with-eval-after-load 'lsp-ui-doc
231 ; Disable on-the-fly showing of rendered documentation.
232 (setq lsp-ui-doc-enable nil)
233 (setq lsp-ui-doc-alignment 'frame)
234 (setq lsp-ui-doc-header t)
235 (setq lsp-ui-doc-include-signature t)
236 (setq lsp-ui-doc-max-height 30)
237 (setq lsp-ui-doc-use-webkit t))
238
239(with-eval-after-load 'lsp-ui-peek
240 (setq lsp-ui-peek-list-width 30)
241 (setq lsp-ui-peek-always-show t))
242
243(with-eval-after-load 'lsp-ui-sideline
244 (setq lsp-ui-sideline-enable nil))
245
246(with-eval-after-load 'lsp-ui
247 (define-key lsp-ui-mode-map (kbd "M-." ) #'lsp-ui-peek-find-definitions)
248 (define-key lsp-ui-mode-map (kbd "M-?" ) #'lsp-ui-peek-find-references)
249 (define-key lsp-ui-mode-map (kbd "M-I" ) #'lsp-ui-peek-find-implementation)
250 (define-key lsp-ui-mode-map (kbd "C-c d" ) #'lsp-ui-doc-show)
251 (define-key lsp-ui-mode-map (kbd "C-c ! l") #'lsp-ui-flycheck-list))
252```
253
254And finally, set up the C/C++ programming mode with a few settings:
255
256* Indentation of 2 spaces.
257* Simple auto-detection of coding style.
258* Marking of badly-styled comments.
259* Running the LSP client.
260
261```elisp
262(defmacro set-up-c-style-comments ()
263 "Set up C-style /* ... */ comments."
264 `(with-eval-after-load 'newcomment
265 (setq-local comment-style 'extra-line)))
266
267(with-eval-after-load 'cc-mode
268 (add-hook 'c-mode-common-hook #'lsp))
269
270(with-eval-after-load 'cc-vars
271 (setq c-mark-wrong-style-of-comment t)
272 (setq c-default-style '((other . "user")))
273 (setq c-basic-offset 2)
274 (add-hook 'c-mode-common-hook (lambda nil (progn (set-up-c-style-comments)))))
275```
276
277### TODO Items
278
279* Whitespace cleanup on save.
280* Snippet support with `yasnippet`.
281* Add `which-key` support.
282* Add `ivy` support.
283* Add `company-prescient` for auto-completion ranking.
284
b4c7e352
FM
285## Minimal Emacs
286
287Code snippets below should be added to your Emacs init file (e.g. `~/.config/emacs/init`).
288
289We'll start by enabling Emacs package repositories and declaring which packages we would like to have installed:
290
291```elisp
292(with-eval-after-load 'package
293 (setq package-archives
294 '(("gnu" . "https://elpa.gnu.org/packages/")
295 ("melpa" . "https://melpa.org/packages/")))
296 (push 'company package-selected-packages)
297 (push 'eglot package-selected-packages))
298```
299
300To avoid restarting Emacs, you can evaluate that previous s-expression by pointing at the last parenthesis and using `C-x C-e` or selecting the block and using `M-x eval-region`.
301
302Once done, run `M-x package-refresh-contents`, then `M-x package-install-selected-packages`.
303This should install some packages for you.
304
305Now, let's set up Eglot and Company:
306
307```elisp
308(require 'eglot)
309(add-to-list 'eglot-server-programs '((c++-mode c-mode) "clangd"))
310(add-hook 'c-mode-hook 'eglot-ensure)
311(add-hook 'c++-mode-hook 'eglot-ensure)
312
313(with-eval-after-load 'prog-mode
314 (add-hook 'prog-mode-hook #'company-mode))
315```
316
317That's it.
318
d4ad3054
FM
319# Code Checkers
320
321## On-the-fly `clang-tidy`
322
323`clangd` automatically integrates with `clang-tidy` if a `.clang-tidy` configuration file is available.
324See [the "`clang-tidy`" section of the CONTRIBUTING document](CONTRIBUTING.md#clang-tidy) on how to set up `clang-tidy`.