Skip to content

Commit f503a9e

Browse files
committed
matlab--access.el: consolidate access to the MATLAB installation
1 parent f744615 commit f503a9e

File tree

6 files changed

+323
-446
lines changed

6 files changed

+323
-446
lines changed

matlab--access.el

Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
;;; matlab--access.el --- MATLAB access -*- lexical-binding: t -*-
2+
3+
;; Copyright 2001-2025 Free Software Foundation, Inc.
4+
;;
5+
;; URL: https://github.com/mathworks/Emacs-MATLAB-Mode
6+
;; SPDX-License-Identifier: GPL-3.0-or-later
7+
;;
8+
;; Author: John Ciolfi <[email protected]>
9+
10+
;; This file is free software: you can redistribute it and/or modify
11+
;; it under the terms of the GNU General Public License as published
12+
;; by the Free Software Foundation, either version 3 of the License,
13+
;; or (at your option) any later version.
14+
;;
15+
;; This file is distributed in the hope that it will be useful,
16+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
;; GNU General Public License for more details.
19+
;;
20+
;; You should have received a copy of the GNU General Public License
21+
;; along with this file. If not, see <https://www.gnu.org/licenses/>.
22+
23+
;;; Commentary:
24+
;;
25+
;; Access to the MATLAB installation:
26+
;; `matlab--platform' - "glnxa64", "maca64", "win64", etc.
27+
;; `matlab--get-abs-matlab-exe' - full path to the MATLAB executable
28+
;; `matlab--get-mlint-exe' - path to the MLint executabe, which
29+
;; should be an absolute path, but not
30+
;; guaranteed
31+
;; These should only be used by other matlab*.el files.
32+
;;
33+
;; In 2025, code which was written years back was moved from other files
34+
;; to this file.
35+
36+
;;; Code:
37+
38+
(require 'cl-macs)
39+
40+
;;; matlab--platform
41+
42+
(defvar matlab--platform
43+
;; See
44+
;; >> lower(computer)
45+
;; MATLABROOT/bin/util/arch.sh (or arch.bat)
46+
(cond ((eq system-type 'darwin)
47+
(cond
48+
((string-match "^arm" system-configuration) ;; e.g. arm-apple-darwin20.3.0
49+
"maca64")
50+
((string-match "^x86_64" system-configuration)
51+
"maci64")
52+
((string-match "^i386" system-configuration)
53+
(let ((mt (getenv "MACHTYPE")))
54+
(if (and (stringp mt) (string= "x86_32" mt))
55+
;; This hack is bad since an Emacs started from
56+
;; the doc doesn't have this variable, thus by defaulting
57+
;; to checking the 32 bit (not common anymore) version,
58+
;; we'll get the right answer most of the time.
59+
"maci" "maci64")))
60+
(t
61+
"mac")))
62+
((eq system-type 'gnu/linux)
63+
(cond ((string-match "64\\|i686" system-configuration)
64+
"glnxa64")
65+
(t "glnx86")))
66+
((eq system-type 'solaris)
67+
"sol2")
68+
((eq system-type 'hpux)
69+
"hpux")
70+
((eq system-type 'windows-nt)
71+
;; Thought about checking the env PROCESSOR_ARCHITEW6432,
72+
;; but this said AMD on my Intel, which seemed suspicious.
73+
(let ((proc (getenv "PROCESSOR_IDENTIFIER")))
74+
(if (and (stringp proc) (string-match "64" proc))
75+
"win64"
76+
"win32")))
77+
(t "unknown"))
78+
"MATLAB platform. See >> lower(computer).")
79+
80+
;;; MATLAB command (full path to matlab executable)
81+
82+
(defgroup matlab-shell nil
83+
"MATLAB shell mode."
84+
:prefix "matlab-shell-"
85+
:group 'matlab)
86+
87+
(defcustom matlab-shell-command "matlab"
88+
"The MATLAB command executable used to start MATLAB.
89+
This can be:
90+
- the name of the MATLAB command (e.g. \"matlab\") which is
91+
found on the system PATH.
92+
- an absolute path to the matlab executable. For example,
93+
\"/<path-to-MATLAB-install-dir>/bin/matlab\"
94+
If matlab-shell-command is set to \"matlab\" and \"matlab\" is not
95+
on the system PATH, `matlab-shell' will look for the matlab
96+
command executable in the default MATLAB installation locations."
97+
:type 'string
98+
:group 'matlab-shell)
99+
100+
;;; matlab executable and matlabroot
101+
102+
(defvar matlab--default-matlab-exe
103+
'((gnu/linux . "/usr/local/MATLAB/R*/bin/matlab")
104+
(darwin . "/Applications/MATLAB_R*.app/bin/matlab")
105+
(windows-nt . "C:/Program Files/MATLAB/R*/bin/matlab.exe"))
106+
"Standard MATLAB command installation locations, SYSTEM => GLOB.")
107+
108+
(defun matlab--matlab-exe-not-found (no-help-window &optional default-loc)
109+
"Signal error, MATLAB command not on system PATH or in optional DEFAULT-LOC.
110+
If NO-HELP-WINDOW is t, do not show the help window"
111+
(let ((msg (format "Unable to locate \"%s\" on the system PATH%s"
112+
matlab-shell-command
113+
(if default-loc
114+
(format " or in the default installation location, %s"
115+
default-loc)
116+
""))))
117+
(when (not no-help-window)
118+
(let ((help-buf-name "*matlab-shell-help*"))
119+
(with-current-buffer (get-buffer-create help-buf-name)
120+
(with-help-window help-buf-name
121+
(insert msg "
122+
123+
To fix, update your system PATH to include
124+
\"/<path-to-MATLAB-install>/bin\"
125+
To verify matlab is on your path, run \"matlab -h\" in a terminal.
126+
127+
Alternatively, you can provide the full path to the
128+
MATLAB command executable by customizing option
129+
`matlab-shell-command'\n")))))
130+
131+
(user-error "%s" msg)))
132+
(cl-defun matlab--get-abs-matlab-exe (&optional no-error)
133+
"Absolute path to the MATLAB executable.
134+
When `matlab-shell-command' is an absolute path, then this will
135+
be resolved to its true name. Otherwise, `matlab-shell-command'
136+
is found using `executable-find'. If `matlab-shell-command' is
137+
\"matlab\" and not the system PATH, this will return the latest
138+
MATLAB installed command found using
139+
`matlab--default-matlab-exe'.
140+
141+
If NO-ERROR is t, and matlab command is not found, nil is return,
142+
otherwise an error is signaled."
143+
(condition-case err
144+
(let (abs-matlab-exe)
145+
(cond
146+
147+
;;Case: the path to the matlab executable was provided, validate it exists and
148+
;; return it.
149+
((file-name-absolute-p matlab-shell-command)
150+
(when (not (file-exists-p matlab-shell-command))
151+
(user-error "Invalid setting for `matlab-shell-command', %s does not exist"
152+
matlab-shell-command))
153+
(when (not (file-executable-p matlab-shell-command))
154+
(user-error "Invalid setting for `matlab-shell-command', %s is not executable"
155+
matlab-shell-command))
156+
;; Use the path provided. Consider the case where a launcher script is provided and the
157+
;; launcher script is symlink'd. In this case, we shouldn't resolve the symlinks, i.e.
158+
;; using file-truename would break this case.
159+
(setq abs-matlab-exe matlab-shell-command))
160+
161+
;; Case: set to a relative path
162+
;;
163+
((when (file-name-directory matlab-shell-command)
164+
(user-error "Relative paths are not supported for `matlab-shell-command', %s"
165+
matlab-shell-command)))
166+
167+
;; Case: "matlab" (or something similar), locate it on the executable path
168+
;; else locate in standard install locations.
169+
(t
170+
(let ((remote (file-remote-p default-directory)))
171+
(if remote
172+
(if (setq abs-matlab-exe (executable-find matlab-shell-command t))
173+
(setq abs-matlab-exe (concat remote abs-matlab-exe))
174+
(user-error "Unable to locate matlab executable on %s
175+
See https://github.com/mathworks/Emacs-MATLAB-Mode/doc/remote-matlab-emacs.org for tips" remote))
176+
;; else look local
177+
(setq abs-matlab-exe (executable-find matlab-shell-command))
178+
(when (not abs-matlab-exe)
179+
(if (string= matlab-shell-command "matlab")
180+
;; Get latest matlab command exe from the default installation location.
181+
(let* ((default-loc (cdr (assoc system-type matlab--default-matlab-exe)))
182+
(default-matlab (when default-loc
183+
(car (last (sort
184+
(file-expand-wildcards default-loc)
185+
#'string<))))))
186+
(when (not default-matlab)
187+
(matlab--matlab-exe-not-found no-error default-loc))
188+
(when (not (file-executable-p default-matlab))
189+
(user-error "%s is not executable" default-matlab))
190+
(setq abs-matlab-exe default-matlab))
191+
;; else unable to locate it
192+
(matlab--matlab-exe-not-found no-error)))))))
193+
194+
;; Return existing absolute path to the MATLAB command executable
195+
abs-matlab-exe)
196+
(error (when (not no-error) (error "%s" (error-message-string err))))))
197+
198+
(defun matlab--get-matlabroot ()
199+
"Return the MATLABROOT from `matlab--get-abs-matlab-exe'.
200+
The returned MATLABROOT does not have a trailing slash.
201+
Returns nil if unable to determine the MATLABROOT."
202+
;; strip "/bin/matlab" from /path/to/matlabroot/bin/matlab
203+
(let ((abs-matlab-exe (matlab--get-abs-matlab-exe 'no-error)))
204+
(when abs-matlab-exe
205+
(let ((bin-dir (directory-file-name (file-name-directory abs-matlab-exe))))
206+
;; matlabroot no slash
207+
(directory-file-name (file-name-directory bin-dir))))))
208+
209+
;;; emacsclient
210+
211+
(defun matlab--find-emacsclient ()
212+
"Locate the emacsclient corresponding for current Emacs.
213+
Emacs binary is defined by variable `invocation-name' in variable
214+
`invocation-directory'"
215+
(let ((ec "emacsclient"))
216+
(cond
217+
;; Mac
218+
((equal system-type 'darwin)
219+
(if (file-exists-p (concat invocation-directory "emacsclient")) ;; running the default emacs?
220+
(setq ec (concat invocation-directory "emacsclient"))
221+
;; On Mac, one can install into
222+
;; /Applications/Emacs.app/Contents/MacOS/Emacs
223+
;; /Applications/Emacs.app/Contents/MacOS/bin/emacsclient
224+
(if (file-exists-p (concat invocation-directory "bin/emacsclient"))
225+
(setq ec (concat invocation-directory "bin/emacsclient")))))
226+
;; Windows
227+
((equal system-type 'windows-nt)
228+
(if (file-exists-p (concat invocation-directory "emacsclientw.exe"))
229+
(setq ec (concat invocation-directory "emacsclientw.exe"))
230+
(error "Unable to locate emacsclientw.exe. It should be in %s" invocation-directory)))
231+
;; Linux or other UNIX system
232+
(t
233+
;; Debian 9 can be setup to have:
234+
;; /usr/bin/emacs
235+
;; /usr/bin/emacsclient
236+
;; /usr/bin/emacs24
237+
;; /usr/bin/emacsclient.emacs24
238+
;; /usr/bin/emacs25
239+
;; /usr/bin/emacsclient.emacs25
240+
(if (and (equal invocation-name "emacs")
241+
(file-exists-p (concat invocation-directory "emacsclient")))
242+
(setq ec (concat invocation-directory "emacsclient"))
243+
(if (file-exists-p (concat invocation-directory "emacsclient." invocation-name))
244+
(setq ec (concat invocation-directory "emacsclient." invocation-name))))))
245+
246+
;; Return, ec, the emacsclient to use
247+
ec))
248+
249+
;;; mlint
250+
251+
(defgroup mlint nil
252+
"MLint minor mode."
253+
:prefix "mlint-"
254+
:group 'matlab)
255+
256+
(defcustom mlint-programs (list "mlint")
257+
"*List of possible mlint programs.
258+
First entry in the list that exists is used.
259+
The \"mlint\" entry means use mlint next to the
260+
matlab executable defined by `matlab-shell-command'.
261+
Other entries should be absolute paths."
262+
:group 'mlint
263+
:type '(repeat (file :tag "MLint Program: ")))
264+
265+
(defun matlab--get-mlint-exe ()
266+
"Return MLint executable or nil if not found.
267+
The returned execuable will be the full path to mlint. If we resolved
268+
the \"mlint\" entry in `mlint-programs', in which case this is the mlint
269+
next to the matlab found by `matlab--get-abs-matlab-exe'. If we
270+
resolved another entry in `mlint-programs', we'll use that and by
271+
convention that entry should be an absolute path, but that's not
272+
guaranteed."
273+
(let (mlint-exe)
274+
(cl-loop for mlint in mlint-programs do
275+
(if (string= mlint "mlint")
276+
(let ((matlab-exe (matlab--get-abs-matlab-exe 'no-error)))
277+
(when matlab-exe
278+
(setq mlint-exe
279+
(replace-regexp-in-string "matlab\\(\\.exe\\)?\\'"
280+
(concat matlab--platform "/mlint\\1")
281+
matlab-exe))
282+
(cl-return)))
283+
(when (file-executable-p mlint)
284+
;; We can't use file-truename on mlint because that would resolve
285+
;; symbolic links.
286+
(setq mlint-exe mlint)
287+
(cl-return))))
288+
mlint-exe))
289+
290+
(provide 'matlab--access)
291+
;;; matlab--access.el ends here

matlab-compat.el

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -24,72 +24,6 @@
2424

2525
;;; Code:
2626

27-
;; Finding executables
28-
(defun matlab-find-executable-directory (program)
29-
"Find the executable PROGRAM on the exec path, following any links.
30-
Return the base directory it is in."
31-
(let ((dir nil))
32-
33-
(dolist (P exec-path)
34-
(let ((nm (expand-file-name program P)))
35-
(when (and (file-exists-p nm) (file-executable-p nm))
36-
(let* ((fa (file-attributes nm))
37-
(lnk (car fa)))
38-
;; The car is t for a directory, a string for a link, nil otherwise
39-
(if (stringp lnk)
40-
;; We have a link - use that as our directory.
41-
(setq dir (file-name-directory lnk))
42-
;; No link - just use this path.
43-
(setq dir P)))
44-
)))
45-
dir))
46-
47-
;; Completion Tools
48-
(defun matlab-display-completion-list (completions common-substring)
49-
"Method for displaying COMPLETIONS with a COMMON-SUBSTRING."
50-
(ignore common-substring)
51-
(let ((args (list completions)))
52-
(apply 'display-completion-list args)))
53-
54-
;;; Finding EmacsClient
55-
(defun matlab-find-emacsclient ()
56-
"Locate the emacsclient corresponding for current Emacs.
57-
Emacs binary is defined by variable `invocation-name' in variable
58-
`invocation-directory'"
59-
(let ((ec "emacsclient"))
60-
(cond
61-
;; Mac
62-
((equal system-type 'darwin)
63-
(if (file-exists-p (concat invocation-directory "emacsclient")) ;; running the default emacs?
64-
(setq ec (concat invocation-directory "emacsclient"))
65-
;; On Mac, one can install into
66-
;; /Applications/Emacs.app/Contents/MacOS/Emacs
67-
;; /Applications/Emacs.app/Contents/MacOS/bin/emacsclient
68-
(if (file-exists-p (concat invocation-directory "bin/emacsclient"))
69-
(setq ec (concat invocation-directory "bin/emacsclient")))))
70-
;; Windows
71-
((equal system-type 'windows-nt)
72-
(if (file-exists-p (concat invocation-directory "emacsclientw.exe"))
73-
(setq ec (concat invocation-directory "emacsclientw.exe"))
74-
(error "Unable to locate emacsclientw.exe. It should be in %s" invocation-directory)))
75-
;; Linux or other UNIX system
76-
(t
77-
;; Debian 9 can be setup to have:
78-
;; /usr/bin/emacs
79-
;; /usr/bin/emacsclient
80-
;; /usr/bin/emacs24
81-
;; /usr/bin/emacsclient.emacs24
82-
;; /usr/bin/emacs25
83-
;; /usr/bin/emacsclient.emacs25
84-
(if (and (equal invocation-name "emacs")
85-
(file-exists-p (concat invocation-directory "emacsclient")))
86-
(setq ec (concat invocation-directory "emacsclient"))
87-
(if (file-exists-p (concat invocation-directory "emacsclient." invocation-name))
88-
(setq ec (concat invocation-directory "emacsclient." invocation-name))))))
89-
;; Return, ec, the emacsclient to use
90-
ec
91-
))
92-
9327
(when (not (fboundp 'string-replace)) ;; string-replace appeared in Emacs 28
9428
(defun string-replace (from-string to-string in-string)
9529
(let ((case-fold-search nil))

0 commit comments

Comments
 (0)