I like using Nix shell for this as well. All of my projects specify their dependencies (compiler, formatter, lsp, etc) there, and with direnv-mode it means I can run Emacs from anywhere and when opening a project with a nix shell specified it automatically picks up everything. It's really neat.