Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Add ~/.jq.d to default module search path #2966

Open
systemmonkey42 opened this issue Dec 7, 2023 · 8 comments
Open

[Feature] Add ~/.jq.d to default module search path #2966

systemmonkey42 opened this issue Dec 7, 2023 · 8 comments
Assignees

Comments

@systemmonkey42
Copy link

I have a number of "safe" macro's I load in ~/.jq because it will autoload and is super convenient for interactive use.
Unfortunately this makes it impossible to have a fixed location in $HOME for dynamically loaded macros.

If I use the -L option, the standard search path is cleared, so its not as helpful as it sounds.

Can the directory ~/.jq.d be added to the start of the default module search path, so that fixed location is available for
dynamic loading which does not require root privileges or conflict with autoloaded macros?

Also, but less important, is it possible to add $XDG_CONFIG_HOME support? Something like adding ~/.config/jq/jq (yeah, its a mess, configs in $XDG_CONFIG_HOME tend to drop the leading .) as an autoading module, and adding ~/.config/jq or ~/.configs/jq/lib to the module search path? I try to move configs to ~/.config whenever possible to reduce clutter in $HOME.

Thanks.

@emanuele6
Copy link
Member

~/.jq can be a directory with .jq files in it instead of a file.

@systemmonkey42
Copy link
Author

~/.jq can be a directory with .jq files in it instead of a file.

My apologies for not being explicit with my meaning.

~/.jq is BOTH an "on-startup-macro-file" and an "on-demand-module-directory". Having a single name for two entirely different use cases is a problem.

I would like a number of macros autoloaded on startup (hence ~/.jq the file), and a number of modules available "on demand" when they are needed (hence ~/.jq the directory).

Unless I'm mistaken, I cannot create a file and a directory of the same name, so I'm suggesting adding ~/.jq.d to the module search path so both features can be made available. (~/.jq as a directory should continue working if its used)

Sorry for not simply saying that in the beginning.

@wader
Copy link
Member

wader commented Dec 7, 2023

~/.jq can be a directory with .jq files in it instead of a file.

Sure it should work? i can't manage to get it working. Also tried main.jq which seem to be a thing but with no success. Looking at the code around here

jq/src/linker.c

Lines 178 to 203 in 5029328

// Try ${search_dir}/${rel_path}.jq
jv testpath = jq_realpath(jv_string_fmt("%s/%s%s",
jv_string_value(spath),
jv_string_value(rel_path),
suffix));
ret = stat(jv_string_value(testpath),&st);
if (ret == -1 && errno == ENOENT) {
jv_free(testpath);
// Try ${search_dir}/$(dirname ${rel_path})/jq/main.jq
testpath = jq_realpath(jv_string_fmt("%s/%s/%s%s",
jv_string_value(spath),
jv_string_value(rel_path),
"jq/main",
suffix));
ret = stat(jv_string_value(testpath),&st);
}
if (ret == -1 && errno == ENOENT) {
jv_free(testpath);
// Try ${search_dir}/${rel_path}/$(basename ${rel_path}).jq
testpath = jq_realpath(jv_string_fmt("%s/%s/%s%s",
jv_string_value(spath),
jv_string_value(rel_path),
jv_string_value(bname),
suffix));
ret = stat(jv_string_value(testpath),&st);
}
i get a feeling if ~/.jq is a directory stat will succeed and not end up trying main.jq and .jq. I'm also confused by the comments Try ${search_dir}/$(dirname ${rel_path})/jq/main.jq and Try ${search_dir}/${rel_path}/$(basename ${rel_path}).jq but don't see any dirname-ish call?

Get a feeling all this could be clarified a bit in the documentation also :)

@emanuele6
Copy link
Member

emanuele6 commented Dec 7, 2023

Hmm, you are right; having .jq files in ~/.jq is different from having a ~/.jq that is a file.

Only if ~/.jq is a file, its content is sourced:

jq/src/linker.c

Lines 423 to 431 in 5029328

char* home = getenv("HOME");
if (home) { // silently ignore no $HOME
/* Import ~/.jq as a library named "" found in $HOME */
block import = gen_import_meta(gen_import("", NULL, 0),
gen_const(JV_OBJECT(
jv_string("optional"), jv_true(),
jv_string("search"), jv_string(home))));
program = BLOCK(import, program);
}

~/.jq as a directory is just a directory that happens to be in the default search path, it has no other special behaviour.

I don't know what the main.jq thing is, it looks like a change a made after jq 1.6 was released, and never documented.
It was actually introduced in 1.6, but never documented.

@systemmonkey42
Copy link
Author

systemmonkey42 commented Dec 7, 2023

Also tried main.jq which seem to be a thing but with no success. Looking at the code around here

100% if the ~/.jq directory could contain an autoloading "main" module I would be very happy.

The main.jq business is to support loading ./MODULE/jq/main.jq when you call include "MODULE". This allows main.jq to import more files and have them sourced locally.

EDIT: I take that back. A quick test shows when module "X" calls include "Y", Y is sourced locally (relative to my currect directory) instead of locally to "X". This is a bug, but its tangential to this thread.

@systemmonkey42
Copy link
Author

systemmonkey42 commented Dec 7, 2023

This is a bug, but its tangential to this thread.

To clarify what I mean by this..

As a test, I created the file ~/.jq/test/jq/main.jq which simply contains include "second";

This is the result of some linuxy magic show how "second" is located.

$ strace -f jq -n 'include "test"; .'|&grep -E 'test|second'
newfstatat(AT_FDCWD, "./test.jq", 0x7ffc384aa420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "./test/jq/main.jq", 0x7ffc384aa420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "./test/test.jq", 0x7ffc384aa420, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/home/user/.jq/test.jq", 0x7ffc384aa420, 0) = -1 ENOENT (No such file or directory)
readlink("/home/user/.jq/test", 0x7ffc384a9ab0, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/user/.jq/test/jq", 0x7ffc384a9ab0, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/user/.jq/test/jq/main.jq", 0x7ffc384a9ab0, 1023) = -1 EINVAL (Invalid argument)
newfstatat(AT_FDCWD, "/home/user/.jq/test/jq/main.jq", {st_mode=S_IFREG|0644, st_size=19, ...}, 0) = 0
openat(AT_FDCWD, "/home/user/.jq/test/jq/main.jq", O_RDONLY) = 3
read(3, "\ninclude \"second\";\n", 4096) = 19
newfstatat(AT_FDCWD, "./second.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "./second/jq/main.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "./second/second.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/home/user/.jq/second.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/home/user/.jq/second/jq/main.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "/home/user/.jq/second/second.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "./../lib/jq/second.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "./../lib/jq/second/jq/main.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "./../lib/jq/second/second.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "./../lib/second.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "./../lib/second/jq/main.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
newfstatat(AT_FDCWD, "./../lib/second/second.jq", 0x7ffc384aa250, 0) = -1 ENOENT (No such file or directory)
write(2, "jq: error: module not found: sec"..., 37jq: error: module not found: second

Note that it will search for ./second instead of ~/.jq/test/jq/second.jq...

Since it is being included by "test/jq/main.jq" is should be sourced there first.

This will help ensure any package containing multiple files can get sourced correctly without namespacing issues.

@aberezin

This comment was marked as duplicate.

@kirkoman
Copy link

kirkoman commented May 1, 2024

Found related past issues #1501 and #2208.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants