branch: elpa/proof-general
commit 5eed5eb87fb3c9ad63387db3cd4cd8ae719c1c2c
Author: Pierre Courtieu <pierre.court...@cnam.fr>
Commit: Pierre Courtieu <pierre.court...@cnam.fr>

    Trying to support the rocq CLI.
    
    Also support _RocqProject files.
---
 coq/coq-mode.el         |  16 +++-
 coq/coq-par-compile.el  |  15 ++-
 coq/coq-system.el       | 239 +++++++++++++++++++++++++++---------------------
 coq/coq.el              |   1 -
 generic/proof-config.el |   2 +-
 5 files changed, 154 insertions(+), 119 deletions(-)

diff --git a/coq/coq-mode.el b/coq/coq-mode.el
index cdb0ef19ec..2bc1ec39e9 100644
--- a/coq/coq-mode.el
+++ b/coq/coq-mode.el
@@ -36,18 +36,24 @@
 ;; FIXME: this should probably be done like for smie above.
 (defvar coq-may-use-prettify (fboundp 'prettify-symbols-mode))
 
-(defcustom coq-prog-name
-  (or (if (executable-find "coqtop") "coqtop")
+(defun coq-detect-prog-gen (default &rest others)
+  (or (cl-find-if #'executable-find (cons default others))
       (let ((exec-path (append exec-path '("C:/Program Files/Coq/bin"))))
-        (executable-find "coqtop"))
-      "coqtop")
+        (cl-find-if #'executable-find (cons default others)))
+      default))
+
+(defun coq-autodetect-progname ()
+  (coq-detect-prog-gen "coqtop" "rocq"))
+
+(defcustom coq-prog-name (coq-autodetect-progname)
   "Name of program to run as Coq.
 On Windows with latest Coq package you might need something like:
    C:/Program Files/Coq/bin/coqtop.opt.exe
 instead of just \"coqtop\".
 This must be a single program name with no arguments.  See option
 `coq-prog-args' to manually adjust the arguments to the Coq process.
-See also `coq-prog-env' to adjust the environment."
+See also `coq-prog-env' to adjust the environment.
+With then 2025 new CLI 'rocq', this command only return 'rocq'."
   :type 'string
   :group 'coq
   :group 'coq-mode)
diff --git a/coq/coq-par-compile.el b/coq/coq-par-compile.el
index b67612a381..19c4d16de6 100644
--- a/coq/coq-par-compile.el
+++ b/coq/coq-par-compile.el
@@ -768,22 +768,21 @@ earlier than 8.19."
 Argument CLPATH must be `coq-load-path' from the buffer
 that triggered the compilation, in order to provide correct
 load-path options to coqdep."
-  (nconc (copy-sequence coq-compile-extra-coqdep-arguments) ; copy for nconc
-         (coq-par-coqdep-warning-arguments)
-         (coq-coqdep-prog-args clpath
-                               (file-name-directory lib-src-file)
-                               (coq--pre-v85))
-         (list lib-src-file)))
+  (nconc
+   (coq-coqdep-prog-args clpath (file-name-directory lib-src-file) 
(coq--pre-v85))
+   (copy-sequence coq-compile-extra-coqdep-arguments) ; copy for nconc
+   (coq-par-coqdep-warning-arguments)
+   (list lib-src-file)))
 
 (defun coq-par-coqc-arguments (lib-src-file clpath)
   "Compute the command line arguments for invoking coqc on LIB-SRC-FILE.
 Argument CLPATH must be `coq-load-path' from the buffer
 that triggered the compilation, in order to provide correct
 load-path options to coqdep."
-  (nconc (copy-sequence coq-compile-extra-coqc-arguments) ; copy for nconc
-         (coq-coqc-prog-args clpath
+  (nconc (coq-coqc-prog-args clpath
                              (file-name-directory lib-src-file)
                              (coq--pre-v85))
+         (copy-sequence coq-compile-extra-coqc-arguments) ; copy for nconc
          (list lib-src-file)))
 
 (defun coq-par-analyse-coq-dep-exit (status output command)
diff --git a/coq/coq-system.el b/coq/coq-system.el
index 56178c2e25..8ded6f4d7c 100644
--- a/coq/coq-system.el
+++ b/coq/coq-system.el
@@ -29,6 +29,14 @@
 (defvar coq-prog-args)
 (defvar coq-debug)
 
+;; Regular files should be of the form "valid_modulename.v" coq
+;; accepts lots of things as letter, this is probably much stricter.
+;; In practice it should be OK though, except maybe for non latin
+;; characters.
+(defun coq-regular-filename-p (s)
+  (let ((noext (file-name-base s)))
+    (string-match-p "\\`[[:alpha:]_][[:alnum:]_]*\\'" noext)))
+
 ;; Arbitrary arguments can already be passed through _CoqProject.
 ;; However this is not true for all assistants, so we don't modify the
 ;; (defpgcustom prog-args) declaration.
@@ -38,23 +46,26 @@
 
 (put 'coq-prog-args 'safe-local-variable #'coq--string-list-p)
 
+;; DEAD CODE?
 (defcustom coq-prog-env nil
   "List of environment settings to pass to Coq process.
 On Windows you might need something like:
   (setq coq-prog-env '(\"HOME=C:\\Program Files\\Coq\\\"))"
   :group 'coq)
 
-(defcustom coq-dependency-analyzer
-  (if (executable-find "coqdep") "coqdep"
-    (proof-locate-executable "coqdep" t '("C:/Program Files/Coq/bin")))
+(defun coq-detect-coqdep () (coq-detect-prog-gen "coqdep" "rocq"))
+(defun coq-detect-coqc () (coq-detect-prog-gen "coqc" "rocq"))
+
+;; We return the executable coqtop or rocq. But not the rocq option
+;; (dep, c, top)) triggering the particular tool.
+(defcustom coq-dependency-analyzer (coq-detect-coqdep)
   "Command to invoke coqdep."
   :type 'string
   :safe 'stringp
   :group 'coq)
 
-(defcustom coq-compiler
-  (if (executable-find "coqc") "coqc"
-    (proof-locate-executable "coqc" t '("C:/Program Files/Coq/bin")))
+(defcustom coq-compiler (coq-detect-coqc)
+   ;FIXME
   "Command to invoke the coq compiler."
   :type 'string
   :safe 'stringp
@@ -114,16 +125,16 @@ The given option should make coqtop return immediately.
 Optionally check the return code and return nil if the check
 fails.  Return also nil on other kinds of errors (e.g., `coq-prog-name'
 not found).
-This function supports calling coqtop via tramp."
-  (let ((coq-command (or coq-prog-name "coqtop"))
-        retv)
+This function supports calling coqtop via tramp.
+This function must not rely on coq-autodetect-version, it would be a cycle."
+  (let* ((coq-command (or coq-prog-name (coq-autodetect-progname)))
+         (coq-args (if (string-match "rocq" coq-command) (list "top" option) 
(list option)))
+         (process-args (append (list coq-command nil t nil) coq-args))
+         retv)
     (condition-case nil
         (with-temp-buffer
-          (setq retv (if option
-                         (process-file coq-command nil t nil option)
-                       (process-file coq-command nil t nil)))
-          (if (or (not expectedretv) (equal retv expectedretv))
-              (buffer-string)))
+          (setq retv (apply 'process-file process-args))
+          (if (or (not expectedretv) (equal retv expectedretv)) 
(buffer-string)))
       (error nil))))
 
 (defun coq-autodetect-version (&optional interactive-p)
@@ -172,6 +183,18 @@ Returns nil if the version can't be detected."
         (signal 'coq-unclassifiable-version  coq-version-to-use))
        (t (signal (car err) (cdr err))))))))
 
+(defun coq--post-v9 ()
+  "Return t if the auto-detected version of Coq (or rocs) is >= 9.
+Return nil if the version cannot be detected."
+  (let ((coq-version-to-use (or (coq-version t) "8.20")))
+    (condition-case err
+       (not (coq--version< coq-version-to-use "9"))
+      (error
+       (cond
+       ((equal (substring (cadr err) 0 15) "Invalid version")
+        (signal 'coq-unclassifiable-version  coq-version-to-use))
+       (t (signal (car err) (cdr err))))))))
+
 (defun coq--post-v86 ()
   "Return t if the auto-detected version of Coq is >= 8.6.
 Return nil if the version cannot be detected."
@@ -443,65 +466,76 @@ options of a few coq-project files does the right thing."
     (coq--options-test-roundtrip "-R /test Test")
     (coq--options-test-roundtrip "-I /test")))
 
-(defun coq-coqdep-prog-args (loadpath &optional current-directory pre-v85)
+;; it is better to inform coqtop of the name of the current module
+;; using the -topfile option, so that coqtop understands references
+;; to it. But don't insist if this would fail (because of wrong
+;; file name): Some people want to script .v files without ever
+;; compiling them.
+(defun coq-topfile-prog-arg ()
+  (when (and (coq--supports-topfile) buffer-file-name)
+    (if (coq-regular-filename-p buffer-file-name)
+        (cons "-topfile" (cons buffer-file-name nil))
+      (message "Warning: buffer %s seems not compilable," buffer-file-name)
+      (message "probably because of its name, no -topfile option set.")
+      nil)))
+
+
+(defun coq-include-prog-args (loadpath &optional current-directory pre-v85)
   "Build a list of options for coqdep.
 LOADPATH, CURRENT-DIRECTORY, PRE-V85: see `coq-include-options'."
   (coq-include-options loadpath current-directory pre-v85))
 
 (defun coq--clean-prog-args (args)
-  "Return ARGS without the entries added by `coq-coqtop-prog-args'.
+  "Return ARGS without the entries added by `coq-coqtop-prog-args' and such.
 
 Such entries are currently -emacs and -topfile."
   (pcase args
-    ((or `("-emacs" . ,rest)
+    ((or `("-emacs" . ,rest) `("top" . ,rest) `("c" . ,rest) `("dep" . ,rest)
          `("-topfile" . (,(pred (apply-partially #'equal buffer-file-name)) . 
,rest)))
      (coq--clean-prog-args rest))
     (`(,car . ,cdr)
      (cons car (coq--clean-prog-args cdr)))
     (_ args)))
 
+
+(defun coq-common-prog-args (loadpath &optional current-directory pre-v85)
+  "Build a list of options common for coqc and coqtop and coqdep.
+LOADPATH, CURRENT-DIRECTORY, PRE-V85: see `coq-include-options'."
+;; coqtop always adds the current directory to the LoadPath, so don't
+  ;; include it in the -Q options.
+  (append (coq--clean-prog-args coq-prog-args)
+          (let* ((coq-load-path-include-current nil)
+                 (cmd (coq-include-prog-args loadpath current-directory 
pre-v85)))
+            cmd)))
+
+(defun coq-coqdep-prog-args (loadpath &optional current-directory pre-v85)
+  "Build a list of option for coqdep."
+  (append
+   (if (coq--post-v9) (list "dep") nil)
+   (coq-include-prog-args loadpath current-directory pre-v85)))
+
 (defun coq-coqc-prog-args (loadpath &optional current-directory pre-v85)
   "Build a list of options for coqc.
 LOADPATH, CURRENT-DIRECTORY, PRE-V85: see `coq-include-options'."
   ;; coqtop always adds the current directory to the LoadPath, so don't
   ;; include it in the -Q options.
-  (append (coq--clean-prog-args coq-prog-args)
-          (let ((coq-load-path-include-current nil)) ; Not needed in >=8.5beta3
-            (coq-coqdep-prog-args loadpath current-directory pre-v85))))
+  (append (if (coq--post-v9) (list "c") nil)
+          (coq-common-prog-args loadpath current-directory pre-v85)))
 
-;;XXXXXXXXXXXXXX
 ;; coq-coqtop-prog-args is the user-set list of arguments to pass to
 ;; Coq process, see 'defpacustom prog-args' in pg-custom.el for
 ;; documentation.
 
-;; Regular files should be of the form "valid_modulename.v" coq
-;; accepts lots of things as letter, this is probably much stricter.
-;; In practice it should be OK though, except maybe for non latin
-;; characters.
-(defun coq-regular-filename-p (s)
-  (let ((noext (file-name-base s)))
-    (string-match-p "\\`[[:alpha:]_][[:alnum:]_]*\\'" noext)))
-
 (defun coq-coqtop-prog-args (loadpath &optional current-directory pre-v85)
   ;; coqtop always adds the current directory to the LoadPath, so don't
   ;; include it in the -Q options. This is not true for coqdep.
   "Build a list of options for coqc.
 LOADPATH, CURRENT-DIRECTORY, PRE-V85: see `coq-coqc-prog-args'."
-  (let ((topfile-supported (coq--supports-topfile)))
-    (append
-     ;; it is better to inform coqtop of the name of the current module
-     ;; using the -topfile option, so that coqtop understands references
-     ;; to it. But don't insist if this would fail (because of wrong
-     ;; file name): Some people want to script .v files without ever
-     ;; compiling them.
-     (if (and topfile-supported buffer-file-name
-              (coq-regular-filename-p buffer-file-name))
-         (cons "-topfile" (cons buffer-file-name nil))
-       (if (and topfile-supported buffer-file-name)
-           (message "Warning: this Coq buffer is probably not compilable \
-because of its name, no -topfile option set."))
-       nil)
-     (cons "-emacs" (coq-coqc-prog-args loadpath current-directory pre-v85)))))
+  (append
+   (if (coq--post-v9) (list "top") nil)
+   (list "-emacs")
+   (coq-topfile-prog-arg)
+   (coq-common-prog-args loadpath current-directory pre-v85)))
 
 (defun coq-prog-args ()
   "Recompute `coq-load-path' before calling `coq-coqtop-prog-args'."
@@ -522,29 +556,61 @@ path (including the -R lib options) (see 
`coq-load-path')."
   :safe 'booleanp
   :group 'coq)
 
-(defcustom coq-project-filename "_CoqProject"
-  "The name of coq project file.
-The coq project file of a coq development (cf. Coq documentation on
-\"makefile generation\") should contain the arguments given to
-coq_makefile. In particular it contains the -I and -R
-options (preferably one per line).  If `coq-use-coqproject' is
+;; FIXME: should we add "Make" as a valid project name? Maybe only if
+;; _xxProject is not present? And/Or if its content seems ok?
+(defun coq-default-project-find-file (dir)
+  "Default function for `coq-project-filename'.
+
+If DIR contains an acceptable coq/rocq project file, return it. Return
+nil otherwise. See `coq-project-filename'.
+"
+  (let* ((case-fold-search t)
+         (files (directory-files dir)))
+    ;; \\` and \\' avoid matching "_CoqProject~" and such
+    (cl-find-if
+     (lambda (s) (string-match "\\`\\(_coqproject\\|_rocqproject\\)\\'" s nil 
t))
+     files)))
+
+(defcustom coq-project-filename #'coq-default-project-file-p
+  "Variable containing the function detecting a project file.
+
+See it default value `coq-default-project-find-file' for an example.
+
+The function takes one argument, the name of a directory, and returns
+the name of a coq/rocq project file it contains if there is one. Return
+nil otherwise.
+
+This is used to detect the coq project file (using
+`locate-dominating-file'). By default we accept _CoqProject and
+_RocqProject (and any case-variant of these) without checking coq/rocq
+version. If you want something smarter please redefine
+`coq-project-filename' directly by using:
+
+(setq coq-project-filename #'my-own-predicate)
+
+About the coq/rocq project file (cf. Coq documentation on project files
+and \"makefile generation\"):
+
+The coq project file of a coq development should contain the arguments
+given to coq_makefile. In particular it contains the -I and -R
+options (preferably one per line). If `coq-use-coqproject' is
 t (default) the content of this file will be used by Proof General to
-infer the `coq-load-path' and the `coq-prog-args' variables that set
-the coqtop invocation by Proof General.  This is now the recommended
-way of configuring the coqtop invocation.  Local file variables may
-still be used to override the coq project file's configuration.
-.dir-locals.el files also work and override project file settings."
-  :type 'string
-  :safe 'stringp
+infer the `coq-load-path' and the `coq-prog-args' variables that set the
+coqtop invocation by Proof General. This is now the recommended way of
+configuring the coqtop invocation. Local file variables may still be
+used to override the coq project file's configuration. .dir-locals.el
+files also work and override project file settings.
+"
+  :type 'function
   :group 'coq)
 
 (defun coq-find-project-file ()
   "Return '(buf alreadyopen) where buf is the buffer visiting coq project file.
 alreadyopen is t if buffer already existed."
   (when buffer-file-name
-    (let* ((projectfiledir (locate-dominating-file buffer-file-name 
coq-project-filename)))
+    (let* ((projectfiledir (locate-dominating-file buffer-file-name 
#'coq-default-project-find-file)))
       (when projectfiledir
-       (let* ((projectfile (expand-file-name coq-project-filename 
projectfiledir))
+       (let* ((projectfile (expand-file-name (coq-default-project-find-file 
projectfiledir) projectfiledir))
               ;; we store this intermediate result to know if we have to kill
               ;; the coq project buffer at the end
               (projectbufferalreadyopen (find-buffer-visiting projectfile))
@@ -603,7 +669,7 @@ coqtop. But -arg \"'a b'\" means to pass a and b together."
            (setq args
                 (append args
                         (split-string-and-unquote concatenated-args 
coq--project-file-separator)))))))
-    (cons "-emacs" args)))
+    args))
 
 (defun coq--extract-load-path-1 (option base-directory)
   "Convert one _CoqProject OPTION, relative to BASE-DIRECTORY."
@@ -686,6 +752,11 @@ Does nothing if `coq-use-project-file' is nil."
 ;; avoid adding for other modes , the setting is performed inside
 ;; coq-mode-hook. This is described in www.emacswiki.org/emacs/LocalVariables
 
+;; NOTE: the coq-prog-arg variable is local to the buffer, and it is
+;; re-populated each time coq is launched on this buffer. In other
+;; words: this hook is useful for coq files on which coq will not be
+;; run.
+
 ;; TODO: also read COQBIN somewhere?
 ;; Note: this does not need to be at a particular place in the hook, but we
 ;; need to make this hook local.
@@ -703,6 +774,10 @@ Does nothing if `coq-use-project-file' is nil."
           (lambda ()
             ;; It seems coq-prog-name and proof-prog-name are not correctly 
linked
             ;; so let us make sure they are the same before computing options
+            ;; coq < 9 needs "coqtop", 
+            (setq proof-prog-name (coq-autodetect-progname))
+            (setq coq-compiler (coq-detect-coqc))
+            (setq coq-dependency-analyzer (coq-detect-coqdep))
             (setq coq-prog-name proof-prog-name)
             (setq coq-prog-args (coq-prog-args))))
 
@@ -729,48 +804,4 @@ Does nothing if `coq-use-project-file' is nil."
 
 (provide 'coq-system)
 
-
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;    To guess the command line options   ;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; OBSOLETE, should take _CoqProject into account.
-(defun coq-guess-command-line (filename)
-  "Guess the right command line options to compile FILENAME using `make -n'.
-This function is obsolete, the recommended way of setting the coqtop
-options is to use a _Coqproject file as described in coq
-documentation.  ProofGeneral reads this file and sets compilation
-options according to its contents.  See `coq-project-filename'.  Per
-file configuration may then be set using local file variables."
-  (if (local-variable-p 'coq-prog-name (current-buffer))
-      coq-prog-name
-    (let* ((dir (or (file-name-directory filename) "."))
-           (makedir
-            (cond
-             ((file-exists-p (concat dir "Makefile")) ".")
-             ;; ((file-exists-p (concat dir "../Makefile")) "..")
-             ;; ((file-exists-p (concat dir "../../Makefile")) "../..")
-             (t nil))))
-      (if (and coq-use-makefile makedir)
-          (let*
-              ;;TODO, add dir name when makefile is in .. or ../..
-              ;;
-              ;; da: FIXME: this code causes problems if the make
-              ;; command fails.  It should not be used by default
-              ;; and should be made more robust.
-              ;;
-              ((compiled-file (concat (substring
-                                       filename 0
-                                       (string-match ".v$" filename)) ".vo"))
-               (command (shell-command-to-string
-                         (concat  "cd " dir ";"
-                                  "make -n -W " filename " " compiled-file
-                                  "| sed s/coqc/coqtop/"))))
-            (message command)
-            (setq coq-prog-args nil)
-            (concat
-             (substring command 0 (string-match " [^ ]*$" command))
-             "-emacs"))
-        coq-prog-name))))
-
 ;;; coq-system.el ends here
diff --git a/coq/coq.el b/coq/coq.el
index 2b83a1ddd4..2e4951f836 100644
--- a/coq/coq.el
+++ b/coq/coq.el
@@ -1899,7 +1899,6 @@ at `proof-assistant-settings-cmds' evaluation time.")
   (setq proof-assistant-home-page coq-www-home-page)
 
   (setq proof-prog-name coq-prog-name)
-  (setq proof-guess-command-line #'coq-guess-command-line)
   (setq proof-prog-name-guess t)
 
   ;; We manage file saveing via coq-compile-auto-save and for coq
diff --git a/generic/proof-config.el b/generic/proof-config.el
index 07e338599a..ed0703c106 100644
--- a/generic/proof-config.el
+++ b/generic/proof-config.el
@@ -107,7 +107,7 @@
 The function could take a filename as argument, run `make -n' to see
 how to compile the file non-interactively, then translate the result
 into an interactive invocation of the proof assistant with the same
-command line options.  For an example, see coq/coq.el."
+command line options."
   :type 'function
   :group 'prover-config)
 

Reply via email to