4

I have customized tramp-remote-path in my .emacs.d/init.el with

(custom-set-variables  '(tramp-remote-path
    (quote
     (tramp-own-remote-path))))

but when tramp is loaded the value of tramp-remote-path is set to the default value. For example after I open a remote file the value of tramp-remote-path is (tramp-default-remote-path "/bin" "/usr/bin" "/sbin" "/usr/sbin" ...)

I have verified this behaviour even when the above customization is the only code in init.el with both 24.5 and 25.0.

Why does this happen?

How can I prevent it and make my customization stick?

Since tramp-remote-path is defined using defcustom, it seems like it should be customizable with custom-set-variables

Daniel Mahler
  • 267
  • 1
  • 12

3 Answers3

5

Short answer: Don't edit custom-set-variables form by hand.

Tramp must be loaded for custom-set-variables to work. Either load tramp before custom-set-variables:

(require 'tramp)
(custom-set-variables  '(tramp-remote-path
    (quote
     (tramp-own-remote-path))))

Or see Michael's answer for a way to tell customize to load tramp first. Note that using M-x customize-variable RET tramp-remote-path RET creates a correct custom-set-variables entry, of the form suggested by Michael.


Longer answer

The root of the problem seems to be in tramp's custom ###;;;tramp-autoload annotation. This tramp-specific mechanism more or less breaks basic custom-set-variables forms.

In normal operation, when you run customize-set-variables, Emacs does one of two things: if the variable already exists in context it sets it, and if it doesn't, it records the value (using (set symbol value)), relying on the customize machinery to use that saved value after the variable is declared (through a defcustom).

Then, when Emacs evaluates the corresponding defcustom (when loading the corresponding package, for example), the customize machinery checks whether the symbol being defcustomed already has a value pending (using (get symbol 'saved-value)), and sets the symbol to that value if so. If there is no such pending value, it uses the value provided by the defcustom form (the default value).

As an exception, however, the defcustom implementation also checks that it isn't overriding an already set value. That is, if the variable has already been declared and given a value, then it's value is preserved. To check whether such a default value exists, customize calls default-toplevel-value. If there already is a default value set, it leaves the variable untouched.

The function that does this is custom-initialize-reset.

Well, in your case, the problem is precisely this exception. If you open emacs/lisp/net/tramp-loaddefs.el, you'll see the following:

(defvar tramp-remote-path '(tramp-default-remote-path "/bin" "/usr/bin" "/sbin" "/usr/sbin" "/usr/local/bin" "/usr/local/sbin" "/local/bin" "/local/freeware/bin" "/local/gnu/bin" "/usr/freeware/bin" "/usr/pkg/bin" "/usr/contrib/bin" "/opt/bin" "/opt/sbin" "/opt/local/bin") "\

This is a way to forward declare tramp-remote-path, but as a side effect it declares and gives a default value to tramp-remote-path. That's fine as far as custom-set-variables is concerned, but not as far as defcustom is concerned. Thus, when the actual defcustom for tramp-remote-path is loaded, the aforementioned exception kicks in, and customize politely preserves the value set by the defvar, thereby ignoring your customization.

You can verify that this is the issue by adding

(defvar tramp-remote-path nil)

to your .emacs, before the actual call to custom-set-variables. That should fix it (because now custom-set-variable will think the variable exists), but I don't know enough about customize to recommend this solution.

Clément
  • 3,924
  • 1
  • 22
  • 37
3

tramp.el must be loaded indeed when a Tramp user option shall be set. The proper way to do this is

(custom-set-variables
 '(tramp-remote-path (quote (tramp-own-remote-path)) nil (tramp)))

Maybe this shall be described in the Tramp manual.

Michael Albinus
  • 6,647
  • 14
  • 20
  • Michael, I upvoted your answer to get it closer to the top, but isn't that arguably a bug? Also, is there a way to get such a form from the customize interface (with the extra `nil (tramp)`? Normally custom-set-variables forms are not written by hand. – Clément Apr 06 '17 at 20:34
  • I suppose if you customize the variable in the customize interface, and if you save your settings then, you will get such a `custom-set-variables` call in your `.emacs`. I've described it this way, because many people hesitate to use the customize interface. – Michael Albinus Apr 07 '17 at 11:18
  • Oh, you're right! Thanks. I'll update my own answer. – Clément Apr 07 '17 at 16:50
  • I think this is a bug. The `defcustom` declarations in tramp.el should have a `:require` clause so that `customize-variable` knows to add this. – rptb1 Jan 16 '21 at 14:29
  • Likely, it is a hen-and-egg problem. The additional `:require` argument in the `defcustom` declaration is active only, when tramp.el is already loaded. – Michael Albinus Jan 16 '21 at 14:36
  • I believe you can't use customize-variable on unloaded modules. – rptb1 Jan 16 '21 at 14:45
  • Sure. That's why I have proposed the respective call in my answer. – Michael Albinus Jan 16 '21 at 14:50
2

Fascinating. I can exactly reproduce what you're running in to if I try to use the customization interface. However! I can successfully change the value of tramp-remote-path by skipping the customization interface, either of two different ways.

First, tramp expects tramp-remote-path to be a list, so my inclination is to do this (in my init.el):

(require 'tramp)
(add-to-list 'tramp-remote-path "/home/gastove/")

Or, setting to a single directory:

(require 'tramp)
(setq tramp-remote-path '("/home/gastove"))
Gastove
  • 1,511
  • 9
  • 15
  • This solves the main problem, but I would still like to understand why/how tramp resets customizations on load. Currently I am trying to keep configuration in customize when I can. – Daniel Mahler Jul 03 '16 at 01:49