elpaca icon indicating copy to clipboard operation
elpaca copied to clipboard

[Bug/Support]:How to use elpaca with local packages not in a git repo?

Open garyo opened this issue 3 months ago • 11 comments

Confirmation

  • [x] I have checked the documentation (README, Wiki, docstrings, etc)
  • [ ] I am checking these without reading them.
  • [x] I have searched previous issues to see if my question is a duplicate.

Elpaca Version

I have a single-file package gco-pkm.el in my ~/.config/emacs/lisp dir (which is in my load-path). I'd like to load that using use-package (which I have hooked up to elpaca with default :ensure t), but only after elpaca has built & loaded org and transient and other dependent packages. And another package gco-pkm-transient.el which I'd like loaded after the first one.

Can I just do

(use-package org
  :ensure t 
  ;; other stuff...)
(use-package transient :ensure t)

(use-package gco-pkm
  :ensure nil
  :load-path "lisp/"
  :after org
  :demand t  ; force loading when org is ready
)

(use-package gco-pkm-transient
  :ensure nil
  :load-path "lisp/"
  :after (gco-pkm org transient)
  :demand t

? I don't see those packages in the elpaca-log buffer, so I don't know if it really processed them as part of its dependency graph or if I'm just getting lucky. Is there an elpaca-approved way to do this?

Operating System

MacOS

Description

No response

garyo avatar Sep 02 '25 16:09 garyo

Seems to be the case : https://github.com/progfolio/elpaca/issues/459#issuecomment-2887798966

ananashawaw avatar Sep 03 '25 01:09 ananashawaw

I think this is different. In #459 the local package is in its own local git repo, and elpaca clones it and builds it. This question is about a regular .el file, something you could just (require ...).

garyo avatar Sep 03 '25 02:09 garyo

The final solution provided : https://github.com/progfolio/elpaca/issues/459#issuecomment-2973460209 is indeed using :repo so it'd need to be git, but the answer I linked is :

(use-package my-package :ensure nil :load-path "~/src/my-package" :after (uuid org-ml))

it only uses use-package keywords, as such, it should totally work. I'm just presenting this solution since, as one provided by the maintainer directly, it'd fit with "an elpaca-approved way to do this?" ^v^

ananashawaw avatar Sep 03 '25 03:09 ananashawaw

@ananashawaw doesn't work for me. Tried with :elpaca nil too. Elpaca just pulls whatever is on MELPA ignoring my :load-path

TideSofDarK avatar Sep 04 '25 05:09 TideSofDarK

Working on a redesign of the way build steps are handled which will allow for installing directly from a single file. Will ping when ready. Thank you.

progfolio avatar Sep 08 '25 00:09 progfolio

@progfolio thanks for your work! As far as I'm concerned there are 3 ways to specify "installation" with elpaca+use-package:

  1. :ensure t which will pull stuff from MELPA
  2. :ensure nil which will generally try to load stuff bundled with Emacs (probably not only that if you add some load paths? Personally I use it only for bundled packages)
  3. :ensure (name :host XXX :repo XXX) which will load from remote git repo My wish is that there would be some way to quickly toggle between one of these 3 and my local package folder (whatever it is: a repo or just a plain folder) Could be either :ensure (name :load-path XXX) or maybe just :load-path instead of a :ensure altogether.

TideSofDarK avatar Sep 08 '25 08:09 TideSofDarK

Here is an example of what I'm doing for local package dev :

(use-package ascii-table
   :ensure (ascii-table :repo "~/.emacs.d/elpaca/repos/emacs-ascii-table")
   :custom
   (ascii-table-initial-base 10)
   (ascii-table-initial-control nil)
   (ascii-table-initial-escape t))

Altho the repo is available on melpa, elpaca doesn't attempt to fetch it, even when rebuilding or with a fetch-all, as such, the local version is loaded, do note that I don't have always-ensure or always-demand on

ananashawaw avatar Sep 09 '25 20:09 ananashawaw

Altho the repo is available on melpa, elpaca doesn't attempt to fetch it, even when rebuilding or with a fetch-all, as such, the local version is loaded

But the interesting question is this: if your ascii-table.el file declares dependencies with Package-Requires:, does elpaca know about those and build and load them in the correct order? And then if some other package managed by elpaca declares a dependency on ascii-table, does it get properly loaded only after your package? In other words, is your ascii-table a full-fledged elpaca citizen participating in the dependency resolution algorithm, or not? I believe that's what @progfolio is referring to above.

garyo avatar Sep 09 '25 22:09 garyo

I've made some progress on the https://github.com/progfolio/elpaca/tree/feat/refactor branch.

First, I refactored the way build steps are determined. The elpaca-build-steps-functions can be used to contextually determine the correct build steps for a package. This allowed for moving all of the Git-specific assumptions into a separate subpackage. I also implemented a subpackage to install single source files from disk. The :type recipe keyword is used to dispatch what type of package is desired. This also opens the door for other types of package sources (e.g. tarballs).

In addition, I've replaced the :pre-build and :post-build recipe keywords with a more flexible :build DSL. This combined with two new macros, elpaca-defstep (for conveniently defining elisp based build steps) and elpaca-defscript (the same for system binary based steps), allow for much more flexible build definitions.

The test case below demonstrates all of these features:

Test Case

How to run this test?

(elpaca-test
  :installer
  "https://raw.githubusercontent.com/progfolio/elpaca/refs/heads/feat/refactor/doc/installer.el"
  :ref "feat/refactor"
  :early-init (setq elpaca-menu-functions '(elpaca-menu-declarations))
  (let ((default-directory (expand-file-name "local-file-package" temporary-file-directory)))
    (with-temp-buffer
      (unless (file-exists-p default-directory)
        (make-directory default-directory))

      (insert
       ";;; local-file-package.el --- a local file test -*- lexical-binding: t; -*-
;; Copyright (C) 2025

;; Author:  <n@ness>
;; Keywords:

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.

;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with this program.  If not, see <https://www.gnu.org/licenses/>.

;;; Commentary:

;;

;;; Code:

;;;###autoload
(defun local-file-package ()
  \"A local file test\"
  (interactive)
  (message \"COMMAND TEST: PASS\"))

(provide 'local-file-package)
")
      (write-region (point-min) (point-max) "local-file-package.el")))
  :init
  (setq +elpaca-env-test "PASS")
  (elpaca `(local-file-package :type file :main
                               "/tmp/local-file-package/local-file-package.el"
                               :build (:before elpaca--queue-dependencies
                                               ,(elpaca-defscript script-test
                                                  '("pwd")
                                                  '("date"))
                                               ,(elpaca-defstep testing nil
                                                                (message "env-test: %S" ,+elpaca-env-test)))
                               :wait t))
  (local-file-package)
  (princ (elpaca-log "| script |")))
Host Env
elpacac4047f8 HEAD -> feat/refactor, origin/feat/refactor
installer0.11
emacsGNU Emacs 31.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.50, cairo version 1.18.4) of 2025-09-12
gitgit version 2.51.0
Output
  INFO     Scraping 17 files for loaddefs...
  INFO     Scraping 17 files for loaddefs...done
  GEN      ../elpaca-autoloads.el
Cloning into '/tmp/elpaca.UdzT1u/elpaca/repos/elpaca'...
Switched to a new branch 'feat/refactor'
branch 'feat/refactor' set up to track 'origin/feat/refactor'.
Checking /tmp/elpaca.UdzT1u/elpaca/repos/elpaca...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca-git.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca-info.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca-local.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca-log.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca-manager.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca-menu-elpa.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca-menu-melpa.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca-menu-org.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca-process.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca-test.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca-ui.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/elpaca.el...
Checking /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/doc...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/doc/early-init.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/doc/init.el...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/doc/installer.el...
Checking /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/extensions...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/extensions/elpaca-use-package.el...
Checking /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/images...
Checking /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/test...
Compiling /tmp/elpaca.UdzT1u/elpaca/repos/elpaca/test/elpaca-tests.el...
Done (Total of 14 files compiled, 3 skipped in 4 directories)

COMMAND TEST: PASS

local-file-package             script               script-test-1                                                                    00.059567
local-file-package             script               $pwd                                                                             00.059820
local-file-package             script                 /tmp/local-file-package                                                        00.060814
local-file-package             script               script-test-2                                                                    00.061370
local-file-package             script               $date                                                                            00.061600
local-file-package             script                 Sun Sep 28 03:17:50 PM EDT 2025                                                00.063221
local-file-package             script               $/usr/bin/emacs-31.0.50 -Q --batch --eval (setq gc-cons-percentage 1.0 print-level nil print-circle nil) --eval (progn nil (message "env-test: %S" "PASS")) 00.064107
local-file-package             script                 env-test: "PASS"                                                               00.107498
local-file-package             script               elpaca-with-emacs-local-file-package complete                                    00.110281

Ideally, I'd like to unify elpaca-defstep and elpaca-defscript. Anyone feeling adventurous can try running the elpaca-test above and playing around with it. Thank you for your patience while I work out the design considerations.

progfolio avatar Sep 28 '25 19:09 progfolio

This looks like a big step forward! I'm glad to see this, both for single-file and local-repo cases. Is it close enough for me to try, or should I wait til it's merged?

garyo avatar Sep 29 '25 12:09 garyo

This looks like a big step forward! I'm glad to see this, both for single-file and local-repo cases. Is it close enough for me to try, or should I wait til it's merged?

I would say hold till merged. I'll ping this thread when ready.

progfolio avatar Oct 15 '25 01:10 progfolio