]> git.ipfire.org Git - thirdparty/pdns.git/blob - DEVELOPMENT.md
Merge pull request #14023 from omoerbeek/rec-proxy-physaddr
[thirdparty/pdns.git] / DEVELOPMENT.md
1 PowerDNS Development Environment
2 --------------------------------
3
4 Thank you for you interest to contribute to the PowerDNS project.
5 This 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
9 The 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
12 Furthermore, additional [on-the-fly checks using `clang-tidy`](#on-the-fly-clang-tidy) can be easily enabled.
13
14 On some systems, `clangd` and `clang-tidy` are available as packages separate from the `clang` package.
15 Ensure that you have all three binaries available and running on your system.
16
17 # Compilation Database
18
19 For projects with non-trivial build systems, like PowerDNS, `clangd` requires a [compilation database](https://clang.llvm.org/docs/JSONCompilationDatabase.html).
20
21 Since 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.
22
23 ## Using Bear
24 Once you have `bear` installed, configure a build of your choice (either the PowerDNS `auth`, `recursor` or `dnsdist`) using `clang` and `clang++`:
25
26 ```sh
27 make distclean # Ensure we rebuild all files so that bear can pick them up.
28 CC=clang CXX=clang++ ./configure --with-modules=gsqlite3 --disable-lua-records
29 ```
30
31 We can now build PowerDNS using `bear` and `make` which produces a compilation database file named `compile_commands.json`:
32
33 ```sh
34 bear --append -- make -j 8
35 ```
36
37 ## Using compiledb
38 Once you have `compiledb` installed, configure the build and run compiledb:
39
40 ```sh
41 make distclean # Ensure we rebuild all files so that bear can pick them up.
42 CC=clang CXX=clang++ ./configure ...
43 make -nwk | /path/to/compiledb -o- > compile_commands.json
44 ```
45
46 to generate the compilation database.
47 For 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
51 Once the compilation database is generated, you can now move onto setting up an LSP client in your editor or IDE.
52
53 Note 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
59 This 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
61 Instructions for an alternative, more minimal, setup using [Eglot](https://github.com/joaotavora/eglot) [are also available](#minimal-emacs).
62
63 Code snippets below should be added to your Emacs init file (e.g. `~/.config/emacs/init`).
64
65 We'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
79 To 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
81 Once done, run `M-x package-refresh-contents`, then `M-x package-install-selected-packages`.
82 This should install some packages for you.
83
84 Now 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
109 Now 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
125 And 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
136 Then 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
141 And 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
218 And 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
254 And 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
285 ## Minimal Emacs
286
287 Code snippets below should be added to your Emacs init file (e.g. `~/.config/emacs/init`).
288
289 We'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
300 To 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
302 Once done, run `M-x package-refresh-contents`, then `M-x package-install-selected-packages`.
303 This should install some packages for you.
304
305 Now, 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
317 That's it.
318
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.
324 See [the "`clang-tidy`" section of the CONTRIBUTING document](CONTRIBUTING.md#clang-tidy) on how to set up `clang-tidy`.