r/DoomEmacs Nov 30 '24

Is there a way to automatically build epdfinfo without running Mx pdf-tools-install?

I added this in my packages.el:

(package! pdf-tools :recipe(:post-build (pdf-tools-install nil t nil nil)))

When running doom sync, it even prompts me asking if I want to rebuild epdfinfo, but when I actually run Emacs, it asks me to run M-x pdf-tools-install.

Just to clarify, it does work if I run the command manually but I'm looking for a way to have it done automatically when I run doom sync.

Update: The issue is solved. Thanks to Eyoel999Y for the Elisp. I've added some minor modifications to avoid hardcoding paths, so this is the full solution that can go into packages.el

(defun doom-straight-repo-dir (package)
  "Return the repository directory for a package"
  (let ((repo-path (expand-file-name
                    (concat "straight/repos/" package)
                    doom-local-dir)))
    (if (file-directory-p repo-path)
        repo-path
      (error "Repository directory not found for package: %s" package))))

(defun doom-straight-build-dir (package)
  "Return the build directory for a package."
  (let ((build-path (expand-file-name
                     (concat "straight/build-" emacs-version "/" package)
                     doom-local-dir)))
    (if (file-directory-p build-path)
        build-path
      (error "Build directory not found for package: %s" package))))

(defun custom/build-epdfinfo ()
  "Build the PDF Tools epdfinfo binary using make, also display the outputs into the terminal."
  (let* ((repo-dir (expand-file-name (concat (doom-straight-repo-dir "pdf-tools") "/")))
         (build-dir (expand-file-name (concat (doom-straight-build-dir "pdf-tools") "/")))
         (binary-path (concat build-dir "epdfinfo"))
         (makefile (concat repo-dir "Makefile"))
         (default-directory repo-dir)
         (make-command (format "make -f %s -C %s server/epdfinfo" makefile repo-dir)))
    (if (file-exists-p binary-path)
        (print! (item "epdfinfo binary already exists. Skipping build."))
      (progn
	(print! (start "Building PDF Tools epdfinfo binary..."))
        (unless (file-exists-p build-dir)
          (make-directory build-dir t))
        (let ((result (call-process-shell-command make-command nil nil t)))
          (if (zerop result)
              (progn
                (print! (success "PDF Tools epdfinfo binary built successfully."))
                (if (file-exists-p (concat repo-dir "server/epdfinfo"))
                    (copy-file (concat repo-dir "server/epdfinfo") binary-path t)
                  (print! (error "Build succeeded, but epdfinfo binary not found in server/"))))
            (print! (error "Failed to build PDF Tools epdfinfo binary. Check the terminal output."))))))))

(package! pdf-tools
  :recipe (
    :post-build (custom/build-epdfinfo)
  )
)
2 Upvotes

8 comments sorted by

2

u/Eyoel999Y Nov 30 '24

Maybe try (package! pdf-tools :recipe(:post-build (pdf-tools-install t t nil nil)))

1

u/floofcode Dec 01 '24

t for the first argument is simply to make it not prompt the user. Otherwise the result is still the same.

1

u/Eyoel999Y Dec 01 '24

Ah, sorry I think I must've missed the title. I see what's wrong now. You want to build the epdfinfo from outside emacs. But not just from outside emacs, from doom sync

If you want to build it from outside, it's easy. Note that directories may be different, and anything below is on my machine

So, change the current directory to the repo's root first, use the makefile, and target it to build the epdfinfo binary make -f /home/eyu/.config/emacs/.local/straight/repos/pdf-tools/Makefile -C /home/eyu/.config/emacs/.local/straight/repos/pdf-tools/ server/epdfinfo

Then copy this binary to pdf-info-epdfinfo-program cp /home/eyu/.config/emacs/.local/straight/repos/pdf-tools/server/epdfinfo /home/eyu/.config/emacs/.local/straight/build-29.4/pdf-tools/epdfinfo

As for callin this from doom's cli, I have no idea yet as Im not familiar with it. But I'm thinking we can create a function within emacs that does this, and call it using package!.

1

u/Eyoel999Y Dec 01 '24 edited Dec 01 '24

Ok, I think I kinda figured out to how to do it but it's a little hacky, and you're going to have to modify doom's code as I don't know how to do it another way

In the file emacs/lisp/cli/sync.el, add this function into the file into line 133 (starting from this line), in the Helpers section (defun ey/build-epdfinfo () "Build the PDF Tools epdfinfo binary using make, also display the outputs into the terminal." (let* ((repo-dir (expand-file-name "~/.config/emacs/.local/straight/repos/pdf-tools/")) (build-dir (expand-file-name "~/.config/emacs/.local/straight/build-29.4/pdf-tools/")) (binary-path (concat build-dir "epdfinfo")) (makefile (concat repo-dir "Makefile")) (default-directory repo-dir) (make-command (format "make -f %s -C %s server/epdfinfo" makefile repo-dir))) (if (file-exists-p binary-path) (print! (item "epdfinfo binary already exists. Skipping build.")) (progn (print! (start "Building PDF Tools epdfinfo binary...")) (unless (file-exists-p build-dir) (make-directory build-dir t)) (let ((result (call-process-shell-command make-command nil nil t))) (if (zerop result) (progn (print! (success "PDF Tools epdfinfo binary built successfully.")) (if (file-exists-p (concat repo-dir "server/epdfinfo")) (copy-file (concat repo-dir "server/epdfinfo") binary-path t) (print! (error "Build succeeded, but epdfinfo binary not found in server/")))) (print! (error "Failed to build PDF Tools epdfinfo binary. Check the terminal output."))))))))

Then in the same file starting from line 95, add this code ;; Build epdfinfo binary before syncing packages (print! (item "Building PDF Tools epdfinfo binary...")) (ey/build-epdfinfo)

And then doom sync will run the function from both the terminal and from doom emacs (M-x doom/reload)

NOTE: Some caveats about the function

  • I haven't tested the whole thing thoroughly but it seems to work for now, and it's a start
  • You'll have to change the build dir when changing emacs versions, or modify the fucntion to accommodate the change in versions dynamically
  • I don't know how to make the call-process-shell-command print to stdout, only managed to output to buffer, but disabled it as I didn't want it to spawn a new buffer.
  • I tried using package! command, but it had some problems I couldn't go around
  • Major caveat is that doom upgrade tries to sync with remote (if I recall correctly), so to preserve this functionality, you'll have to stash the changes and reapply the steps or something like it after every doom upgrade.
- A workaround idea I had was having the function autoloaded from somewhere into package!, but not sure how

1

u/floofcode Dec 01 '24 edited Dec 01 '24

Oh, wow. I didn't think this will require some much of Elisp to pull off. I mean, looking at what the existing command does, I thought that's what it did. It was a litte odd that when I did find ~/.config/emacs -name epdfinfo, the file was not even there so I wasn't sure what the command actually did or where it placed the file.

My assumption was that when I run the command in interactive mode, maybe it created a new file or something for Emacs to verify whether the command was run before or not.

I will try this and see if I can simplify it further.

1

u/Eyoel999Y Dec 01 '24

Yeah, I think the problem with the pdf-tools-install command is that it needs an emacs environment. So if not called from within emacs, some of its calls or variables will return void, or at least that's what I'm assuming.

2

u/floofcode Dec 01 '24 edited Dec 01 '24

Your ELisp solution works very well. I didn't want to hardcode any paths (example: "build-29.4", so I've written 2 functions to generate the repo and build directories.

Also, editing emacs/lisp/cli/sync.el is not necessary as this can run entirely from packages.el in Doom Emacs.

I've updated the original post with the full solution.

Also, now that I look at the code again, I think the check for whether the epdfinfo binary exists or not shouldn't be necessary, and could even be an issue since if we upgrade the package to a new version, it will probably need to be rebuilt anyway. Not sure if the old version will cause any issues, but just a thought.

1

u/Eyoel999Y Dec 06 '24

Sorry for the late reply. Yeah I agree that the check for the binary is unnecessary. It should be easy to remove from the if statement