8686 " https://github.com/dakra/ghostel/releases"
8787 " Base URL for ghostel GitHub releases." )
8888
89- (defcustom ghostel-auto-download-module nil
90- " Automatically download a pre-built native module if missing.
91- When non-nil, ghostel will attempt to download a pre-built module
92- from GitHub releases on first load. When nil (the default), the
93- user must explicitly run \\ [ghostel-download-module] or build from
94- source with ./build.sh."
95- :type 'boolean
89+ (defcustom ghostel-module-auto-install 'ask
90+ " What to do when the native module is missing at load time.
91+ \\= `ask' — prompt with a choice to download, compile, or skip (default).
92+ \\= `download' — download a pre-built binary from GitHub releases.
93+ \\= `compile' — build from source via `ghostel-module-compile' .
94+ nil — do nothing; the user must install the module manually."
95+ :type '(choice (const :tag " Ask interactively" ask)
96+ (const :tag " Download pre-built binary" download)
97+ (const :tag " Compile from source" compile)
98+ (const :tag " Do nothing" nil ))
9699 :group 'ghostel )
97100
98101(defun ghostel--module-platform-tag ()
@@ -112,34 +115,83 @@ Returns nil if the platform is not recognized."
112115 (when tag
113116 (format " ghostel-module-%s%s " tag module-file-suffix))))
114117
115- (defun ghostel--maybe-download-module (dir )
116- " Try to download a pre-built module into DIR if available.
117- Downloads from the latest GitHub release matching the current platform.
118- Does nothing if the platform is unsupported or the download fails."
118+ (defun ghostel--module-download-url ()
119+ " Return the download URL for the current platform's pre-built module."
120+ (let ((asset-name (ghostel--module-asset-name)))
121+ (when asset-name
122+ (let ((version (ghostel--package-version)))
123+ (if version
124+ (format " %s /download/v%s /%s "
125+ ghostel-github-release-url version asset-name)
126+ (format " %s /latest/download/%s "
127+ ghostel-github-release-url asset-name))))))
128+
129+ (defun ghostel--download-module (dir )
130+ " Download a pre-built module into DIR.
131+ Returns non-nil on success."
119132 (condition-case err
120- (let ((asset-name (ghostel--module-asset-name)))
121- (when asset-name
122- (let* ((version (ghostel--package-version))
123- (url (if version
124- (format " %s /download/v%s /%s "
125- ghostel-github-release-url
126- version
127- asset-name)
128- (format " %s /latest/download/%s "
129- ghostel-github-release-url
130- asset-name)))
131- (dest (expand-file-name
132- (concat " ghostel-module" module-file-suffix) dir)))
133- ; ; Enforce HTTPS for security
134- (unless (string-prefix-p " https://" url)
135- (error " Refusing non-HTTPS download URL: %s " url))
133+ (let ((url (ghostel--module-download-url)))
134+ (when url
135+ (unless (string-prefix-p " https://" url)
136+ (error " Refusing non-HTTPS download URL: %s " url))
137+ (let ((dest (expand-file-name
138+ (concat " ghostel-module" module-file-suffix) dir)))
136139 (message " ghostel: downloading native module from %s ... " url)
137140 (when (ghostel--download-file url dest)
138- (message " ghostel: native module downloaded to %s " dest)))))
141+ (message " ghostel: native module downloaded successfully " )
142+ t ))))
139143 (error
140- (message " ghostel: auto- download failed: %s " (error-message-string err))
144+ (message " ghostel: download failed: %s " (error-message-string err))
141145 nil )))
142146
147+ (defun ghostel--compile-module (dir )
148+ " Compile the native module from source in DIR.
149+ Runs synchronously and returns non-nil on success."
150+ (let ((default-directory dir)
151+ (script (expand-file-name " build.sh" dir)))
152+ (if (file-executable-p script)
153+ (progn
154+ (message " ghostel: compiling native module (this may take a moment)... " )
155+ (let ((ret (call-process script nil " *ghostel-build*" nil )))
156+ (if (eq ret 0 )
157+ (progn (message " ghostel: native module compiled successfully " ) t )
158+ (display-warning 'ghostel
159+ " Module compilation failed. See *ghostel-build* buffer for details." )
160+ nil )))
161+ (display-warning 'ghostel
162+ (format " build.sh not found in %s .\n Clone with submodules and run ./build.sh manually. " dir))
163+ nil )))
164+
165+ (defun ghostel--ensure-module (dir )
166+ " Ensure the native module exists in DIR.
167+ Behavior is controlled by `ghostel-module-auto-install' ."
168+ (let ((action ghostel-module-auto-install))
169+ (when (eq action 'ask )
170+ (setq action (ghostel--ask-install-action dir)))
171+ (pcase action
172+ ('download (ghostel--download-module dir))
173+ ('compile (ghostel--compile-module dir))
174+ (_ nil ))))
175+
176+ (defun ghostel--ask-install-action (dir )
177+ " Prompt the user to choose how to install the missing native module.
178+ DIR is the target directory. Returns \\= 'download, \\= 'compile, or nil."
179+ (let* ((url (or (ghostel--module-download-url) " GitHub releases" ))
180+ (choice (read-char-choice
181+ (format " Ghostel native module not found.
182+
183+ [d] Download pre-built binary from:
184+ %s
185+ [c] Compile from source (requires Zig)
186+ [s] Skip — install manually later
187+
188+ Choice: " url)
189+ '(?d ?c ?s ))))
190+ (pcase choice
191+ (?d 'download )
192+ (?c 'compile )
193+ (?s nil ))))
194+
143195(defun ghostel--package-version ()
144196 " Return ghostel package version string, or nil.
145197Returns nil without error when `package.el' is unavailable."
@@ -166,7 +218,6 @@ Returns nil without error when `package.el' is unavailable."
166218 (start (point )))
167219 (when (< start (point-max ))
168220 (write-region start (point-max ) dest nil 'silent )
169- ; ; Make executable on Unix
170221 (set-file-modes dest #o755 )
171222 t )))))
172223 (when (buffer-live-p buf)
@@ -181,39 +232,33 @@ Returns nil without error when `package.el' is unavailable."
181232 buffer-file-name)))
182233 (mod (expand-file-name
183234 (concat " ghostel-module" module-file-suffix) dir)))
184- (if (file-exists-p mod)
185- (if (yes-or-no-p " Module already exists. Re-download? " )
186- (progn
187- (ghostel--maybe-download-module dir)
188- (when (file-exists-p mod)
189- (module-load mod)
190- (message " ghostel: module loaded successfully " )))
191- (message " Cancelled. " ))
192- (ghostel--maybe-download-module dir)
193- (if (file-exists-p mod)
194- (progn
195- (module-load mod)
196- (message " ghostel: module loaded successfully " ))
197- (user-error " Download failed. Run ./build.sh to build from source" )))))
235+ (when (and (file-exists-p mod)
236+ (not (yes-or-no-p " Module already exists. Re-download? " )))
237+ (user-error " Cancelled" ))
238+ (if (ghostel--download-module dir)
239+ (progn
240+ (module-load mod)
241+ (message " ghostel: module loaded successfully " ))
242+ (user-error " Download failed. Try M-x ghostel-module-compile to build from source" ))))
198243
199244; ; Load the native module
200245(unless (featurep 'ghostel-module )
201246 (let* ((dir (file-name-directory (or load-file-name buffer-file-name)))
202247 (mod (expand-file-name
203248 (concat " ghostel-module" module-file-suffix) dir)))
204249 (unless (file-exists-p mod)
205- (when ghostel-auto-download-module
206- (ghostel--maybe-download-module dir)))
250+ (ghostel--ensure-module dir))
207251 (if (file-exists-p mod)
208252 (condition-case err
209253 (module-load mod)
210254 (error
211255 (display-warning 'ghostel
212- (format " Failed to load native module: %s \n Run ./build.sh to rebuild "
256+ (format " Failed to load native module: %s \n Try M-x ghostel-module-compile to rebuild "
213257 (error-message-string err)))))
214- (display-warning 'ghostel
215- (concat " Native module not found: " mod
216- " \n Run ./build.sh or M-x ghostel-download-module" )))))
258+ (unless ghostel-module-auto-install
259+ (display-warning 'ghostel
260+ (concat " Native module not found: " mod
261+ " \n Run M-x ghostel-download-module or M-x ghostel-module-compile" ))))))
217262
218263; ; Declare native module functions for the byte compiler
219264(declare-function ghostel--new " ghostel-module" )
0 commit comments