Skip to content

Conversation

@hebasto
Copy link
Member

@hebasto hebasto commented Jun 18, 2025

While reviewing #32697, I noticed that symlink creation fails when the target subdirectory does not exist. In such cases, file(CREATE_LINK ... COPY_ON_ERROR SYMBOLIC) falls back to copying, which implicitly creates the required path. As a result, a single file is copied instead of symlinked for subdirectories in test/functional.

This PR ensures that necessary subdirectories are created in advance, so that subsequent symlink creation does not fail due to missing paths.

For example:

  • on the master branch:
$ cmake -B build
$ ls -l build/test/functional/mocks/
total 8
-rwxrwxr-x 1 hebasto hebasto 2683 Jul  3 14:11 invalid_signer.py
lrwxrwxrwx 1 hebasto hebasto   64 Jul  3 14:11 multi_signers.py -> /home/hebasto/dev/bitcoin/test/functional/mocks/multi_signers.py
lrwxrwxrwx 1 hebasto hebasto   60 Jul  3 14:11 no_signer.py -> /home/hebasto/dev/bitcoin/test/functional/mocks/no_signer.py
lrwxrwxrwx 1 hebasto hebasto   57 Jul  3 14:11 signer.py -> /home/hebasto/dev/bitcoin/test/functional/mocks/signer.py
  • with this PR:
$ cmake -B build
$ ls -l build/test/functional/mocks/
total 4
lrwxrwxrwx 1 hebasto hebasto 65 Jul  3 13:51 invalid_signer.py -> /home/hebasto/dev/bitcoin/test/functional/mocks/invalid_signer.py
lrwxrwxrwx 1 hebasto hebasto 64 Jul  3 13:51 multi_signers.py -> /home/hebasto/dev/bitcoin/test/functional/mocks/multi_signers.py
lrwxrwxrwx 1 hebasto hebasto 60 Jul  3 13:51 no_signer.py -> /home/hebasto/dev/bitcoin/test/functional/mocks/no_signer.py
lrwxrwxrwx 1 hebasto hebasto 57 Jul  3 13:51 signer.py -> /home/hebasto/dev/bitcoin/test/functional/mocks/signer.py

@DrahtBot
Copy link
Contributor

DrahtBot commented Jun 18, 2025

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage & Benchmarks

For details see: https://corecheck.dev/bitcoin/bitcoin/pulls/32773.

Reviews

See the guideline for information on the review process.

Type Reviewers
ACK m3dwards
Stale ACK BrandonOdiwuor, janb84

If your review is incorrectly listed, please react with 👎 to this comment and the bot will ignore it on the next update.

Conflicts

No conflicts as of last run.

@maflcko
Copy link
Member

maflcko commented Jun 18, 2025

would it make sense to at least replace the recursive glob with a normal glob now, iterating over the given folders, given that they are listed anyway?

@hebasto
Copy link
Member Author

hebasto commented Jun 19, 2025

would it make sense to at least replace the recursive glob with a normal glob now, iterating over the given folders, given that they are listed anyway?

Sure! Added a commit.

@janb84
Copy link
Contributor

janb84 commented Jun 23, 2025

Any hints how to verify / test this ?

Have tried to see any difference between this PR and master but it's not clear to me what difference I should see.
When used on #32697 there is a difference that without this PR build/test/functional/data/util/* is populated with this PR the util directory is not being created (looking at the source this is logical but unsure if there needs to be things done after 32697 is merged or not having the directory is intentional )

@purpleKarrot
Copy link
Contributor

Is this setting up the environment for tests to run? Ideally, things like that should be done as test fixture and not executed during build system generation.

@hebasto
Copy link
Member Author

hebasto commented Jul 3, 2025

Rebased.

@janb84

Any hints how to verify / test this ?

Have tried to see any difference between this PR and master but it's not clear to me what difference I should see. When used on #32697 there is a difference that without this PR build/test/functional/data/util/* is populated with this PR the util directory is not being created (looking at the source this is logical but unsure if there needs to be things done after 32697 is merged or not having the directory is intentional )

I've updated the PR description with the relevant example.

@purpleKarrot

Is this setting up the environment for tests to run? Ideally, things like that should be done as test fixture and not executed during build system generation.

At this moment, functional tests are not managed by ctest/cmake.

Copy link
Contributor

@janb84 janb84 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK 0b7d038

This PR changes :

  • Recursive Glob -> 'explicit' non recursive GLOB
  • Subdirectories now are created in advance, so that there aren't any problems with missing paths for sym-linking.

The PR fixes a "bug" and the code changes make the code communicate the intentions of the code better.

Can reproduce the successful subfolder creation and therefor file sym-linking :

Details Master (fails see `invalid_signer.py` ):
$ ls -l
total 4
-rwxr-xr-x 1 jan staff 2683 Jul  3 15:57 invalid_signer.py
lrwxr-xr-x 1 jan staff   73 Jul  3 15:57 multi_signers.py -> /Users/jan/Projects/bitcoin/test/functional/mocks/multi_signers.py
lrwxr-xr-x 1 jan staff   69 Jul  3 15:57 no_signer.py -> /Users/jan/Projects/bitcoin/test/functional/mocks/no_signer.py
lrwxr-xr-x 1 jan staff   66 Jul  3 15:57 signer.py -> /Users/jan/Projects/bitcoin/test/functional/mocks/signer.py

This PR (works see invalid_signer.py):

ls -l
total 0
lrwxr-xr-x 1 jan staff 74 Jul  3 16:05 invalid_signer.py -> /Users/jan/Projects/bitcoin/test/functional/mocks/invalid_signer.py
lrwxr-xr-x 1 jan staff 73 Jul  3 16:05 multi_signers.py -> /Users/jan/Projects/bitcoin/test/functional/mocks/multi_signers.py
lrwxr-xr-x 1 jan staff 69 Jul  3 16:05 no_signer.py -> /Users/jan/Projects/bitcoin/test/functional/mocks/no_signer.py
lrwxr-xr-x 1 jan staff 66 Jul  3 16:05 signer.py -> /Users/jan/Projects/bitcoin/test/functional/mocks/signer.py
  • code review ✅
  • building & tests ✅

@hebasto Thank you for supplying the additional information how to test the PR.

Copy link
Contributor

@BrandonOdiwuor BrandonOdiwuor left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested ACK 0b7d038

Confirmed that the subdirectories are created in the build tree before the symlinks preventing accidental COPY_ON_ERRORs like invalid_signer.py

Commit: 0b7d038
Screenshot 2025-07-11 at 20 51 32

Branch: Master
Screenshot 2025-07-11 at 20 50 40

I also agree that using explicit file(GLOB ...) for specific file types (e.g., *.py, *.json, *.csv, *.html) is more robust than the original file(GLOB_RECURSE ...)

This change ensures that subsequent symlink creation does not fail due
to a missing path.
@hebasto
Copy link
Member Author

hebasto commented Jul 17, 2025

Rebased to resolve conflict with merged #32881.

Copy link
Contributor

@janb84 janb84 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re ACK fa3beb6

Changes sinds last ACK:

  • Solved Merge conflict.

Retested, still worked as described. ✅
Code review ✅

@DrahtBot DrahtBot requested a review from BrandonOdiwuor July 18, 2025 08:44
@achow101 achow101 requested review from m3dwards and removed request for BrandonOdiwuor October 22, 2025 15:41
@hebasto
Copy link
Member Author

hebasto commented Nov 4, 2025

The feedback from @m3dwards has been addressed.

@m3dwards
Copy link
Contributor

m3dwards commented Nov 7, 2025

ACK 76dae5d

Tested on arm Mac.

@DrahtBot DrahtBot requested a review from janb84 November 7, 2025 12:37
@fanquake
Copy link
Member

fanquake commented Nov 7, 2025

cc @purpleKarrot

@purpleKarrot
Copy link
Contributor

My position is that code like this is not something that belongs in a CMakeLists.txt file.

Ideally, tests should have their environment set up in a way that they find necessary files in the source tree, without any symlinks or copies.

If directories/symlinks are needed by tests that are executed with ctest, a temporary workaround could be to create them in a test fixture, like I mentioned here.

As long as functional tests are launched by some other means (like a shell script) the directories and symlinks should be created by that script.

@maflcko
Copy link
Member

maflcko commented Nov 7, 2025

Ideally, tests should have their environment set up in a way that they find necessary files in the source tree, without any symlinks or copies.

I think there is no explanation in the code, but the reason why the copies/symlinks are created is that it keeps the source dir clean of .pyc files.

If directories/symlinks are needed by tests that are executed with ctest, a temporary workaround could be to create them in a test fixture, like I mentioned here.

They are not executed with ctest, so a fixture can not be used.

As long as functional tests are launched by some other means (like a shell script) the directories and symlinks should be created by that script.

The functional tests are launched by a runner. However, it is also possible to run individual tests. So they are stand-alone scripts and so they can't create their own symlink.

@purpleKarrot
Copy link
Contributor

I think there is no explanation in the code, but the reason why the copies/symlinks are created is that it keeps the source dir clean of .pyc files.

The generation of .pyc files can be suppressed with python's -B option or PYTHONDONTWRITEBYTECODE env var.
See: https://docs.python.org/3/using/cmdline.html#envvar-PYTHONDONTWRITEBYTECODE

@maflcko
Copy link
Member

maflcko commented Nov 7, 2025

I know, but -B does not help to fix the other problems: (1) They are not run via ctest, and (2) they are stand-alone scripts, where the user/dev would have to specify it every time, or set the env var.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants