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

clang-tools: Improve wrapper script, add tests #354755

Open
wants to merge 1 commit into
base: staging
Choose a base branch
from

Conversation

Patryk27
Copy link
Member

@Patryk27 Patryk27 commented Nov 9, 2024

clang-tools invokes clangd through a wrapper script whose sole purpose is to extend CPATH and CPLUS_INCLUDE_PATH envs, so that clangd is later able to find built-in and stdlib headers.

This script does two things wrong:

  1. We extend CPATH et al with entries from both @clang@/nix-support/libc-cflags and @clang@/resource-root/include, which is kind of a self-made problem, since clangd already has some header auto-detection logic which we (used to) circumvent (for more details, see the diff).

  2. We extend CPATH et al unconditionally, breaking the --query-driver option. I'm not sure what's the perfect solution here, but skipping this logic when we detect --query-driver should be good enough. I mean, ideally we wouldn't be playing with CPATH etc. whatsoever, but well.

Closes #351962
Closes #348791
Closes #345704

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandboxing enabled in nix.conf? (See Nix manual)
    • sandbox = relaxed
    • sandbox = true
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 24.11 Release Notes (or backporting 23.11 and 24.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Add a 👍 reaction to pull requests you find important.

@github-actions github-actions bot added the 6.topic: llvm/clang Issues related to llvmPackages, clangStdenv and related label Nov 9, 2024
@Patryk27
Copy link
Member Author

Patryk27 commented Nov 9, 2024

Note that I'm not sure how to test --query-driver, my fix is just a hunch - cc @iniw, @lukash, could you play with it?

@iniw
Copy link

iniw commented Nov 10, 2024

I'm not sure how great of a fix this is. --query-driver doesn't actually do anything all that special, it's only a glob that acts as a whitelist for which programs clangd is allowed to execute to find the appropriate headers. For example, mine is set to **, meaning all paths are allowed, because I only work on projects that I trust. Anyways, my point is that changing behavior depending on the existence of that flag is not ideal, IMO.

My question is: why do we even need the wrapper's shenanigans in the first place? clangd is very good at finding out about system headers and compiler's resource directories given just a compile_commands.json - why do we need anything else? Is the goal for the wrapped package to still find nixos's system headers when a compile_commands.json isn't found? If that's the case I'd prefer just dropping support for that entirely and instead document the fact that nixpkg's clangd will only properly work when given a valid compile_commands.json, which is already required anyways when working with third-party libraries and/or custom compile options such as C++ version, etc.

EDIT: Forgot to mention, the fix did work.

@iniw
Copy link

iniw commented Nov 10, 2024

And as mentioned in the other issue raised on this matter, a clangd dev does seem to agree that adding these implicit paths is at the very least unusual.
clangd/clangd#2181 (comment)

@lukash
Copy link

lukash commented Nov 11, 2024

Thanks for your work on this, @Patryk27.

To me the --query-driver detection also seems sketchy, as @iniw said, it's just a mask. The check will fail at least in the case where the --query-driver won't match the actual compiler that's used. Might work to an extent when the user knows what they're doing, but still will be quirky. There's likely no robust mechanism to achieve what NixOS needs in clangd.

Being ignorant of the circumstances, what's in @clang@/nix-support/libc-cflags (and the C++ equivalent thereof) and why is it needed?

Is there an easy way to test the PR? I have a copy of the clang-tools default.nix I made some time ago and hacked the wrapper, that works. But since then the way that default.nix is called has changed and simple drop-in of the code isn't working for me (I'm calling it like this in a flake: my-clang-tools = nixpkgs.legacyPackages.${system}.callPackage ../clang-tools/clang-tools.nix {llvmPackages = pkgs.llvmPackages_18;};). I'll try to figure it out when I have some time, but not sure when that'll be.

@lukash
Copy link

lukash commented Nov 30, 2024

Well I copy-pasted the code from the PR and hacked at it until it "worked".

clangd seems to work well and the change to install its headers to the place it's looking for them is definitely correct. The workaround with --query-driver seems to improve the situation in general. It is strictly better for most cases and for the case of using --query-driver but not having the actual compiler match, the user has the possibility to fix it by correcting the --query-driver. Current state has broken cases with no workarounds for the user.

So IMO it'd be good to merge it, but a note explaining the workaround with the --query-driver detection should be definitely included in the wrapper script.

With the (hacked up) PR code, though, I'm not able to run clang-format from the package:

$ clang-format
/nix/store/cb77k2bnkb1fvr25pb9chv3baj1x6x1x-clang-tools-18.1.8/bin/clang-format: line 44: /nix/store/nzyxg89vqxhdw884n50c2p5k1vrgibka-clang-unwrapped-with-lib/bin/clang-format: No such file or directory

$ which clang-format
/nix/store/cb77k2bnkb1fvr25pb9chv3baj1x6x1x-clang-tools-18.1.8/bin/clang-format

$ ls -l /nix/store/cb77k2bnkb1fvr25pb9chv3baj1x6x1x-clang-tools-18.1.8/bin/clang-format
lrwxrwxrwx 1 root root 6 led  1  1970 /nix/store/cb77k2bnkb1fvr25pb9chv3baj1x6x1x-clang-tools-18.1.8/bin/clang-format -> clangd

$ ls -l /nix/store/cb77k2bnkb1fvr25pb9chv3baj1x6x1x-clang-tools-18.1.8/bin/clangd
-r-xr-xr-x 1 root root 1499 led  1  1970 /nix/store/cb77k2bnkb1fvr25pb9chv3baj1x6x1x-clang-tools-18.1.8/bin/clangd

$ cat /nix/store/cb77k2bnkb1fvr25pb9chv3baj1x6x1x-clang-tools-18.1.8/bin/clangd
#!/nix/store/1xhds5s320nfp2022yjah1h7dpv8qqns-bash-5.2p32/bin/sh

buildcpath() {
  local path after

  while (( $# )); do
    case $1 in
        -isystem)
            shift
            path=$path${path:+':'}$1
            ;;

        -idirafter)
            shift
            after=$after${after:+':'}$1
            ;;
    esac

    shift
  done

  echo $path${after:+':'}$after
}

# When user passes `--query-driver`, avoid extending `CPATH` et al, since we
# don't want to "infect" user-specified toolchain and headers with our stuff.
extendcpath=true

for arg in "$@"; do
  if [[ "${arg}" == \-\-query\-driver* ]]; then
    extendcpath=false
  fi
done

if [ "$extendcpath" = true ]; then
  export CPATH=${CPATH}${CPATH:+':'}$(buildcpath ${NIX_CFLAGS_COMPILE} \
                                                 $(</nix/store/nr95z75f3hj5z7a0bddqpx9wjm47mvbd-clang-wrapper-17.0.6/nix-support/libc-cflags))

  export CPLUS_INCLUDE_PATH=${CPLUS_INCLUDE_PATH}${CPLUS_INCLUDE_PATH:+':'}$(buildcpath ${NIX_CFLAGS_COMPILE} \
                                                                                        $(</nix/store/nr95z75f3hj5z7a0bddqpx9wjm47mvbd-clang-wrapper-17.0.6/nix-support/libcxx-cxxflags) \
                                                                                        $(</nix/store/nr95z75f3hj5z7a0bddqpx9wjm47mvbd-clang-wrapper-17.0.6/nix-support/libc-cflags))
fi

exec -a "$0" /nix/store/nzyxg89vqxhdw884n50c2p5k1vrgibka-clang-unwrapped-with-lib/bin/$(basename $0) "$@"

$ ls /nix/store/nzyxg89vqxhdw884n50c2p5k1vrgibka-clang-unwrapped-with-lib/bin/
clangd

As you can see, clang-format is a symlink to clangd, which is a script, which calls $(basename $0) (being clang-format) from some unwrapped directory which doesn't contain the binary. I don't think I've broken this in my hacked up version.

Also note that paths passed to the CPATH / CPLUS_INCLUDE_PATH directories are somehow pointing to some clang-tools version 17 store paths. But this may be something I've caused by my crude use of the PR code.

@Patryk27
Copy link
Member Author

Patryk27 commented Dec 1, 2024

I don't think I've broken this in my hacked up version.

You're right, my patch breaks this (including other tools like clang-move) - I'm looking into it now.

As for the overall approach to the fix, I'm not 100% sold on it either - I just cobbled together something that works better than the current approach; I'm not sure why we need the CPATH and CPLUS_INCLUDE_PATH thingies 🤔

@Patryk27
Copy link
Member Author

Patryk27 commented Dec 1, 2024

Alright, I pushed a new commit that uses a bit less hacky approach - clang-format etc. should work as well now. Let me know what you think!

I'm also open to other approaches, just don't have any other idea myself.

(btw, I'll squash the commits later - I've pushed it as a separate commit to make it easier to review)

@lukash
Copy link

lukash commented Dec 8, 2024

Just loaded the changes, both clangd and clang-format seem to work, I'll report back if I find an issue. Thank you!

Regarding an overall better / "correct" approach, ideally you could work with clangd upstream to figure out a proper solution. FWIW they were very helpful in my bugreports. But I guess someone would need to analyze the situation on Nix side and approach them with a clear description of the situation and what Nix needs... I can chime in especially with the arm compilation side, but lack a general in-depth insight.

@iniw
Copy link

iniw commented Dec 17, 2024

I'm personally happy with the changes here. Would like to see this merged :)

Copy link
Member

@alyssais alyssais left a comment

Choose a reason for hiding this comment

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

Can you please squash the commits and update the commit message for the new approach? Otherwise this is quite hard to review.

Copy link
Member

@alyssais alyssais left a comment

Choose a reason for hiding this comment

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

The commit message doesn't explain anything at the moment. The details you put in the PR body would be a good start, but the addition of the lib symlink also requires explanation.

@Patryk27 Patryk27 force-pushed the improve-clang-tools branch from 3264fa9 to fd38da8 Compare January 25, 2025 10:22
@iniw
Copy link

iniw commented Jan 25, 2025

So glad to see progress being made here! Really useful change for embedded developers using nix :)

@Patryk27 Patryk27 force-pushed the improve-clang-tools branch 2 times, most recently from 081b1d7 to 6337c6a Compare January 25, 2025 17:06
@Patryk27 Patryk27 force-pushed the improve-clang-tools branch from 6337c6a to 248d843 Compare January 26, 2025 19:32
clang-tools invokes clangd through a wrapper script whose sole purpose
is to extend CPATH and CPLUS_INCLUDE_PATH envs, so that clangd is later
able to find built-in headers.

This script does two things wrong:

- We extend CPATH et al with both `@clang@/nix-support/libc-cflags` and
`@clang@/resource-root/include`, which is a self-made problem, since
clangd already has header-detection logic.

-We extend CPATH et al unconditionally, breaking the `--query-driver`
option. I'm not sure what's the perfect solution here, but skipping this
logic when we detect `--query-driver` should be good enough in practice.
@Patryk27 Patryk27 force-pushed the improve-clang-tools branch from 248d843 to 7a5ea9f Compare January 26, 2025 19:33
@Patryk27 Patryk27 requested a review from alyssais February 1, 2025 09:56
Copy link
Member

@alyssais alyssais left a comment

Choose a reason for hiding this comment

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

Quite a lot of rebuilds so should go to staging.

@Patryk27 Patryk27 changed the base branch from master to staging February 2, 2025 18:24
@Patryk27
Copy link
Member Author

Patryk27 commented Feb 2, 2025

Fair enough - changed!

@Patryk27
Copy link
Member Author

Patryk27 commented Feb 2, 2025

N.B. it's kinda weird it causes so many rebuilds imo

@wegank wegank added 12.approvals: 1 This PR was reviewed and approved by one reputable person 12.approved-by: package-maintainer This PR was reviewed and approved by a maintainer listed in the package labels Feb 4, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
6.topic: llvm/clang Issues related to llvmPackages, clangStdenv and related 10.rebuild-darwin: 11-100 10.rebuild-linux: 501+ 10.rebuild-linux: 1001-2500 11.by: package-maintainer This PR was created by the maintainer of the package it changes 12.approvals: 1 This PR was reviewed and approved by one reputable person 12.approved-by: package-maintainer This PR was reviewed and approved by a maintainer listed in the package
Projects
None yet
6 participants