diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index ac18aceadcf..5a4274d6f68 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -51,6 +51,9 @@ ;; files inside the root must not be considered a part of it). It ;; should be consistent with `project-files'. ;; +;; `project-build-dir' can be overridden if the project backend has some +;; extra information about the project build directory. +;; ;; This list can change in future versions. ;; ;; Transient project: @@ -208,6 +211,22 @@ project-prompter :group 'project :version "30.1") +(defcustom project-build-dir nil + "Build directory for current project. +This is the custom that the user could specify in dir-locals to override +the option specified by the project's backend." + :type 'directory + :safe t + :version "30.1") + +(defcustom project-compile-command nil + "Build command for current project. +This is the custom that the user could specify in dir-locals to override +the option specified by the project's backend." + :type 'directory + :safe t + :version "30.1") + ;;;###autoload (defun project-current (&optional maybe-prompt directory) "Return the project instance in DIRECTORY, defaulting to `default-directory'. @@ -289,8 +308,42 @@ project-external-roots headers search path, load path, class path, and so on." nil) +(cl-defgeneric project-build-dir (_project) + "Return build directory of the current PROJECT. + +This function is intended to be defined by the backend when possible. +Otherwise this returns nil and the `project-compile' command will be +called in the project-root. +If the user defines the directory-local variable `project-build-dir' it +will have precedence over the result of this function." + nil) + +(defun project-get-build-dir (project) + "Return build directory of the current PROJECT. +1. If the variable `project-build-dir' is defined, this function returns +it. 2. Else if the function `project-build-dir' return non-nil. +3. else it return the project-root. +If the defined path is relative, this expands it relatively to the +project's root." + (let ((dir (or project-build-dir ;; variable for dir-locals or connection local vars + (project-build-dir project) ;; backend function + (project-root project)))) ;; I assume project-root is always absolute + (if (file-name-absolute-p dir) + dir + (expand-file-name dir (project-root project))))) + +(cl-defgeneric project-compile-command (_project) + "Return build command of the current PROJECT. + +This function is intended to be defined by the backend when possible. +Otherwise this returns nil and the `project-compile' command will use +the default `compile-command' value. If the user defines the +directory-local variable `project-build-command' it will have preference +over this function and this will be never called." + nil) + (cl-defgeneric project-name (project) - "A human-readable name for the project. + "A human-readable name for the PROJECT. Nominally unique, but not enforced." (file-name-nondirectory (directory-file-name (project-root project)))) @@ -1391,10 +1444,16 @@ project-compile "Run `compile' in the project root." (declare (interactive-only compile)) (interactive) - (let ((default-directory (project-root (project-current t))) - (compilation-buffer-name-function - (or project-compilation-buffer-name-function - compilation-buffer-name-function))) + ;; I am wondering whenever we need to expand connection local + ;; variables at this point... maybe before or inside the let. + (let* ((project (project-current t)) + (default-directory (project-get-build-dir project)) + (compile-command (or project-compile-command + (project-compile-command project) + compile-command)) + (compilation-buffer-name-function + (or project-compilation-buffer-name-function + compilation-buffer-name-function))) (call-interactively #'compile))) (defun project-recompile (&optional edit-command)