Commit Graph

20915 Commits

Author SHA1 Message Date
Johannes Altmanninger
5e12d4e99c Use sync::OnceCell for terminal modes, fixing memory leak
My

    $ sudo docker/docker_run_tests.sh --shell-after docker/jammy-asan.Dockerfile

shows a lot of complaints about

    Direct leak of 60 byte(s) in 1 object(s) allocated from:

because some unit tests call reader_init() and reader_deinit().  Work around
this by initializing this value only once.  AFAICT, OnceCell is async-signal
safe (unlike Mutex), although I don't think documentation promises that.

It doesn't feel great to change implementation code to accomodate tests but
I think for this specific issue that's what we usually do.  Alternatively,
we could add to lsan_suppressions.txt.
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
f98d1779dd build_tools/check.sh: respect inherited RUSTFLAGS/RUSTDOCFLAGS
No particular motivation. Seems better?
Also, use long options I guess.
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
6f18a1b314 Also namespace target/man -> target/fish-man
If cargo ever wants to write to "target/man", it would collide with our
use of this path.  Let's make this less likely by prefixing the name with
"fish-".  This also makes it more obvious that this is fish's invention.
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
8b102f2571 Stop using Cargo's OUT_DIR
(Note: this commit should technically have preceded the "Fix config paths
for disjoint build-dirs and in-tree installs" one, to make that one easier
to follow, but I wasn't 100% sure if this commit is right.)

From https://doc.rust-lang.org/cargo/reference/environment-variables.html

> OUT_DIR — If the package has a build script, this is set to the folder
> where the build script should place its output. See below for more
> information. (Only set during compilation.)

so OUT_DIR is something like "target/debug/build/fish-41da27d587f48978".
Whenever build.rs is re-run, we get a new one.

I don't think we need this flexibility anywhere.  It wouldn't protect
concurrent "cargo test" from interfering with each other - that's handled
by a file lock taken by Cargo.

Use "target/" instead (or CMAKE_BINARY_DIR if set).
Namespace the files better, so we don't create weird paths like

	target/test/complete_test/...
	target/fish_root/
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
514eebb002 build_tools/update_translations.fish: move po/template.po to /tmp
With the upcoming tests/checks/gettext.fish test from #11583, my

	sudo docker/docker_run_tests.sh --shell-after docker/focal.Dockerfile

fails writing to "po/template.po" because "/fish-source" is mounted as
read-only.  (There should be no need for tests to write to the source tree.)

Since commit 6239cba1e4 (Add dry-run mode to update_translations.fish,
2025-05-30), "build_tools/update_translations.fish" always removes that
template file when done, even without "--dry-run".

I'm not sure if we still have a need for keeping around "po/template.po".
To add a new translation, you can run "build_tools/update_translations
po/xy.po". It could serve as a cache but that would only work if we integrated
it into a build system.

Move it to /tmp, fixing the docker tests.
2025-06-24 12:52:35 +02:00
Johannes Altmanninger
41eb0a2fd0 build_tools/update_translations.fish: protect against universally-set $tmpdir 2025-06-24 12:51:17 +02:00
Johannes Altmanninger
290d957ab6 build_tools/update_translations.fish: remove forward reference
cleanup_exit references variables that are only defined later.  Fix that.
Also move the definition one block up, to help the next commit.
2025-06-24 12:33:28 +02:00
Johannes Altmanninger
08b03a733a docker_run_tests.sh: stop using cmake
Use test_driver directly instead of CMake in the docker tests.

Deal with the read-only "/fish-source" by exporting
"CARGO_TARGET_DIR=$HOME/fish-build".  It seems correct to also inject this
environment variable into the interactive debugging shells.  Add some logging
to make this override more obvious to the user.

Adopt "build_tools/check.sh", because that defines the full set of checks
that we (eventually) want to run in CI.

In particular, this will also run "tests/checks/po-files-up-to-date.fish"
which "cargo b && cargo t && tests/test_driver.py" does not, due to the
REQUIRES clause.

Since most docker images have some lints/warnings today, disable those for
now. Use "docker_run_tests.sh --lint" to override. The default may be changed
in future.
2025-06-24 12:32:42 +02:00
Johannes Altmanninger
19a17fa981 Fix docker warning by using "ENV key=value" syntax
- LegacyKeyValueFormat: "ENV key=value" should be used instead of
	legacy "ENV key value" format (line 4)
2025-06-24 12:32:42 +02:00
Johannes Altmanninger
13c00c9f79 Fix config paths for disjoint build-dirs and in-tree installs
Commit 89282fd9bc (Use CARGO_MANIFEST_DIR to see if we're running from
build dir, 2024-01-20) did

    -if exec_path.starts_with(OUT_DIR)
    +if exec_path.starts_with(CARGO_MANIFEST_DIR)

where OUT_DIR is the cmake build directory ("./build")
and CARGO_MANIFEST_DIR is our top level source tree.

This allowed "target/debug/fish" to work, but it broke
1. CMake build directories outside the source tree, e.g. "docker/docker_run_tests.sh".
   Those incorrectly fall back to the compiled-in-path (/usr/local/share etc)
2. Installations iside the source tree, e.g.
   "mkdir build && cd build && cmake .. -DCMAKE_INSTALL_PREFIX=$PWD/../install".
   These installations incorrectly use "share/" etc. from the source tree.

Fix this by
1. respecting the CMake-specific FISH_BUILD_DIR, ad
2. if that's not set, use $CARGO_MANIFEST_DIR/target
2025-06-24 12:32:42 +02:00
Johannes Altmanninger
19eceff3bc bulid.rs respect CARGO_TARGET_DIR in man output 2025-06-24 12:32:42 +02:00
Johannes Altmanninger
6a4d3a59ab build.rs: extract constant for cargo manifest dir 2025-06-24 12:32:42 +02:00
Johannes Altmanninger
3b0d5c342b build.rs: extract function for canonicalizing paths
This probably means we should enable Rust backtraces.. not sure though.
2025-06-24 12:32:42 +02:00
Johannes Altmanninger
884a2d100c build.rs: remove redundant include dir
This include was added for config.h in 618834c4b5 (Port
UVAR_FILE_SET_MTIME_HACK, 2023-09-15), but that file no longer exists.
Remove it.
2025-06-24 12:04:57 +02:00
Johannes Altmanninger
a4d355634d docker_run_tests: fix failed build exiting prematurely
My bad; the "set +e" is only active inside the subshell.
The outer shell uses "set -e", which means that it will
exit upon seeing the subshell fail.
2025-06-24 12:04:57 +02:00
Johannes Altmanninger
3c620f56ee test_driver.py: fix compatibility with Python 3.8 / Ubuntu Focal 2025-06-24 12:04:57 +02:00
Johannes Altmanninger
7679be3126 test_driver.py: fix confusing help output 2025-06-24 12:02:13 +02:00
Johannes Altmanninger
49926cfbac Standardize shell script indent level
We have a mixture of 2 and 4 space indent.

    4 benchmarks/driver.sh
    2 build_tools/check.sh
    4 build_tools/git_version_gen.sh
    4 build_tools/mac_notarize.sh
    2 build_tools/make_pkg.sh
    2 build_tools/make_tarball.sh
    2 build_tools/make_vendor_tarball.sh
    4 docker/docker_run_tests.sh
    4 osx/install.sh
    2 tests/test_functions/sphinx-shared.sh

Our editorconfig file specifies 2, with no explicit reason.
Our fish and Python scripts use 4, so let's use that.
2025-06-24 12:02:13 +02:00
Johannes Altmanninger
963c3425a3 Merge pull request #11593 2025-06-24 12:02:13 +02:00
ndrew222
55bddac90a added completion for uv and uvx
Closes #11601
2025-06-24 12:02:13 +02:00
Illia Ostapyshyn
20c67692e1 completions/journalctl: Add --pager-end
Closes #11604
2025-06-24 12:02:13 +02:00
Johannes Altmanninger
1e7088fb6b Merge pull request #11605 2025-06-24 12:02:13 +02:00
Johannes Altmanninger
0f75df7c35 history: remove flush()/fsync() and don't write after each appended item
Commit 5c0fddae70 (Refactor history flushing, 2025-03-28) made three changes:
1. call fsync() when we are finished writing the history file.
2. when appending to (as opposed to vacuuming) history, call write(2)
   (followed by flush() and sync()) for each item. Previously, we'd only
   call write(2) if our 64k buffer was full, or after processing the last
   history item.
3. actually check the return value of flush() (which would retry when flushing
   fails -- but std::fs::File::flush() never fails!).

The motivation was to potentially fix #10300 which didn't succeed (see
https://github.com/fish-shell/fish-shell/issues/10300#issuecomment-2876718382).

As for 1 and 2, I don't think the way we use fsync really helps, and flushing
eagerly should not make a difference.

As for 3, there are some explanations in comments, commit message and a [PR
comment](https://github.com/fish-shell/fish-shell/pull/11330#discussion_r2020171339).
To summarize, 5c0fddae70 wants to address the scenario where file.flush()
fails. Prior to that commit we would ostensibly carry on with a corrupted
"first_unwritten_new_item_index" (corrupted because it doesn't match what's
written to disk), which can cause various issues.  However this doesn't
ever happen because std::fs::File::flush() never fails because it doesn't
do anything -- std::fs::File::write() does not buffer writes, it always
delegates to write(2).

There can definitely be scenarios like the one described in
https://github.com/fish-shell/fish-shell/pull/11330#discussion_r2020171339
where the disk is full. In that case, either write(2) fails, which we
already check.  Or close(3p) fails with EIO, which we have never checked. We
should probably check that.

Undo all three changes for now.

Closes #11495
2025-06-24 12:02:13 +02:00
Asuka Minato
edb0617d13 Update git.fish
Closes #11606
2025-06-24 12:02:12 +02:00
Johannes Altmanninger
d2f7d238f3 Remove obsolete translation entries
I doubt these are very helpful; most of them won't be used again.  We can
still find them in history with "git log -Gpart.of.msgid".
2025-06-24 11:44:46 +02:00
Volodymyr Chernetskyi
a7bed39c1e Add info on formatting fish_git_prompt output 2025-06-23 18:46:54 +02:00
Johannes Altmanninger
06646998db fixup! alias: fix indentation 2025-06-23 14:18:15 +02:00
Johannes Altmanninger
096f225579 alias: fix indentation
Fixes #11602
2025-06-23 13:53:43 +02:00
Johannes Altmanninger
ebec8c15ab Merge pull request #11599 2025-06-23 13:46:34 +02:00
Daniel Rainer
92d9646631 Simplify CMake Tests
Remove dependency on CTest. Parallel execution is handled by `test_driver.py`
internally now, so CTest is no longer relevant for performance.

This also removes CMake targets for single tests. As a replacement,
`test_driver.py` can be called directly with the path to the build directory as
the first argument and the path to the desired test as the second argument.
Ensuring that the executables in the build directory are up to date needs to be
done separately.
For a pure cargo build, an example of running a single test would be:
`cargo b && tests/test_driver.py target/debug tests/checks/abbr.fish`

The recommended way of running tests is `build_tools/check.sh`, which runs more
extensive tests and does not depend on CMake. That script does not work in CI
yet, so CMake testing is retained for now.

Update CI config to use the new `FISH_TEST_MAX_CONCURRENCY`.
Also update the FreeBSD version, since the previous one is outdated and does not
support the semaphore logic in `test_driver.py`.
2025-06-22 22:44:46 +02:00
Daniel Rainer
ab1307c63b Do not mmap with len 0
`mmap` should fail when the length argument is 0. Checking this in advance
allows returning early, without performing unnecessary syscalls.

This also fixes an issue observed on FreeBSD 13.2 where `mmap` does not always
fail when the length is 0, resulting in the `assert!(len > 0)` in
`MmapRegion::new` failing.
https://github.com/fish-shell/fish-shell/issues/11595
2025-06-21 22:19:27 +02:00
Johannes Altmanninger
bebf3c129f Merge pull request #11594 2025-06-21 18:54:22 +02:00
Johannes Altmanninger
60881f1195 __fish_complete_list: only unescape "$(commandline -t)"
Commit cd3da62d24 (fix(completion): unescape strings for __fish_complete_list,
2024-09-17) bravely addressed an issue that exists in a lot of completions.
It did so only for __fish_complete_list. Fair enough.

Unfortunately it unescaped more than just "$(commandline -t)".

This causes the problem described at
https://github.com/fish-shell/fish-shell/issues/11508#issuecomment-2889088934
where completion descriptions containing a backslash followed by "n" are
interpreted as newlines, breaking the completion parser.  Fix that.
2025-06-21 18:53:50 +02:00
Johannes Altmanninger
320ebb6859 __fish_complete_list: strip "--foo=" prefix from replacing completions
Given a command line like

	foo --foo=bar=baz=qux\=argument

(the behavior is the same if '=' is substituted with ':').

fish completes arguments starting from the last unescaped separator, i.e.

	foo --foo=bar=baz=qux\=argument
			 ^
__fish_complete_list provides completions like

	printf %s\n (commandline -t)(printf %s\n choice1 choice2 ...)

This means that completions include the "--foo=bar=baz=" prefix.

This is wrong. This wasn't a problem until commit f9febba (Fix replacing
completions with a -foo prefix, 2024-12-14), because prior to that, replacing
completions would replace the entire token.
This made it too hard to writ ecompletions like

	complete -c foo -s s -l long -xa "hello-world goodbye-friend"

that would work with "foo --long fri" as well as "foo --long=frie".
Replacing the entire token would only work if the completion included that
prefix, but the above command is supposed to just work.
So f9febba made us replace only the part after the separator.

Unfortunately that caused the earlier problem.  Work around this.  The change
is not pretty, but it's a compromise until we have a better way of telling
which character fish considers to be the separator.

Fixes #11508
2025-06-21 18:53:50 +02:00
Daniel Rainer
9bf6112b60 Add option to limit concurrency of tests
The main purpose of this is avoiding timeouts in CI.

Passing `--max-concurrency=n` to `test_driver.py` will result in at most `n`
tests running concurrently, where `n` is a positive integer.
Not specifying the argument preserves the old behavior of running all tests
concurrently without a limit.
2025-06-21 02:36:06 +02:00
Daniel Rainer
ef1a6aba26 Remove unused NEVER_MMAP 2025-06-20 00:44:50 +02:00
Peter Ammon
ba00d721f4 Correct statvfs call to statfs
This was missed in the Rust port - C++ had statfs for MNT_LOCAL and not statvfs.
The effect of this is that fish never thought its filesystem was local on macOS
or BSDs (Linux was OK). This caused history race tests to fail, and also could
in rare cases result in history items being dropped with multiple concurrent
sessions.

This fixes the history race tests under macOS and FreeBSD - we weren't locking
because we thought the history was a remote file.
2025-06-19 15:30:36 -07:00
Peter Ammon
fe8909e8f2 Fix an off-by-one error in reporting dropped history items
This was introduced in the Rust port. The original C++ was pretty gnarly to be
fair.
2025-06-19 11:08:50 -07:00
Lorenzo Albano
d369614ad9 Use $GIT_COMMON_DIR for stashes detection.
Within a linked worktree, `$GIT_DIR` and `$GIT_COMMON_DIR` have different
values (see [git-worktree docs](https://git-scm.com/docs/git-worktree#_details)).
The two serve different purposes, in case of stashes `$GIT_COMMON_DIR`
should be used, this way stash detection in the git prompt works also
when inside a `git worktree`.

Closes #11591
2025-06-19 11:13:09 +02:00
Johannes Altmanninger
4f4d941760 Merge pull request #11588 2025-06-19 11:13:09 +02:00
Johannes Altmanninger
9b19db5c5f Merge pull request #11492 2025-06-19 11:13:09 +02:00
Johannes Altmanninger
4f46d369c4 completions/git: fix spurious error when no subcommand is in $PATH
Systems like NixOS might not have "git-receive-pack" or any other "git-*"
executable in in $PATH -- instead they patch git to use absolute paths.
This is weird. But no reason for us to fail. Silence the error.

Fixes #11590
2025-06-19 11:13:09 +02:00
Daniel Rainer
977459949f Obtain history file path in HistoryImpl::save
Eliminates some code duplication between the two different saving
implementations. These changes are based on
https://github.com/fish-shell/fish-shell/pull/11492#discussion_r2149438316.

One extra change is included here, namely the early return on an empty file
name, indicating private mode. Without this, `history_path.unwrap()` fails in
some tests. Returning early is probably what we want in such situations anyway.
2025-06-18 21:05:29 +02:00
Daniel Rainer
8cbcfc0b3a Stop caching whether to lock
The cached information might become outdated. It is important that all fish
processes use the same mutual exclusion logic (either `flock`-based or the
fallback), because the two methods do not provide mutual exclusion from one
another.
Avoiding caching makes the behavior independent on previous system states,
resulting in fish instances performing file operations at the same time to use
the same locking logic.

More detailed discussion in
https://github.com/fish-shell/fish-shell/pull/11492#discussion_r2134543447
2025-06-18 21:05:00 +02:00
Daniel Rainer
77738fd646 Remove obsolete comments
If arguments always have the same value they do not need to be arguments.
For `HistoryImpl::add`, the comments are incorrect (assuming they should
indicate the value of the argument), since both arguments can be true or false
independently.
`History::add` is only called at one location in a test with a constant value of
`false` for `pending`. This might mean that the parameter could be deleted, or
maybe even the entire function, if testing can work without it.
2025-06-18 20:54:38 +02:00
Daniel Rainer
f438e80f9b Use shared file locking logic
Both history files and universal variables files are accessed by multiple
processes, which need a way to synchronize their accesses.
This synchronization logic is mixed in with the logic for reading and updating
the files' contents, which results in messy code, duplicated locking
implementations, and inconsistencies.
Moreover, the existing implementations are flawed which has resulted in file
corruption (e.g. https://github.com/fish-shell/fish-shell/issues/10300).

The new approach separates the synchronization logic from the rest.

There are two approaches to synchronization.
- The primary one is using `flock(2)` to lock the directory containing the file
  which requires synchronized access. We do not lock the file holding the data
  directly because this file might be replaced, which can result in locking
  succeeding when it should block. Locking the directory solves this problem.
  To avoid inconsistent file states, changes are first written to a temporary
  file, which is then renamed into place while still holding the lock.
- In some situations `flock` locks are unavailable or take a long time. This
  mostly applies to remote file systems. If we think that a directory is located
  on a remote file system, we do not attempt to use `flock`.
  As a fallback, we have a lockless approach, which uses file metadata (device,
  inode, size, ctime, mtime) to identify file versions.
  We then read from the file, write the updated data to a temporary file,
  check if the file id for the path is the same as before we started reading,
  and if so we rename the temporary file such that it replaces the old file.
  Note that races are possible between the file id check and the rename syscall.
  If we detect a file id mismatch, we retry, up to a predetermined number of
  attempts.

The operations which should be performed are passed to the functions handling
synchronization as `Fn`s. Because we might have to run these operations
repeatedly when retrying, they should be executable arbitrarily often without
causing side-effects relevant to the program. This requires some changes to
functions accessing these files. In many cases, they have to work with
non-mutable references, which requires that they return the data which should be
updated in the program state, instead of directly assigning to the appropriate
location.

Locking via `O_EXLOCK`, which was used for the universal variable file, is no
longer supported. That version of locking locks the file operated on directly,
instead of its directory. According to the man pages of {Open,Free,Net}BSD
and macOS, these locks have flock semantics. So if flock is available, we can
use it, and if it is not, the `O_EXLOCK` flag does not help.
2025-06-18 20:48:27 +02:00
Daniel Rainer
ccb9c8225f Remove CHAOS_MODE
This is dead code. It is never set to true.
2025-06-18 20:31:28 +02:00
Daniel Rainer
33b651ad91 Get remoteness of correct directory
The history file is stored in the data dir, not the config dir, so the
remoteness of the former is the one which matters.
2025-06-18 20:31:28 +02:00
Daniel Rainer
a20712b51d Avoid mutable references for mmap creation
This is preparatory work for refactoring the file synchronization approach.

`read_exact` will fail if the file length does not match the expected one, which
means zero padding is useless. (On any reasonable OS it was also useless before,
because anonymous memory mapping would be zero-pages.)
2025-06-18 20:31:28 +02:00
Daniel Rainer
da426a1b03 Improve error handling in src/history/file.rs
Use `std::io::Result` instead of `Option` as the return type where appropriate,
to allow for better-informed error handling.

Remove explicit checks about the length to be mapped being 0.
From `mmap(3)`:
> If len is zero, mmap() shall fail and no mapping shall be established.
2025-06-18 20:31:28 +02:00