This is an archive of the discontinued LLVM Phabricator instance.

[lldb] Make sure we use the libc++ from the build dir
ClosedPublic

Authored by JDevlieghere on Jul 5 2022, 4:33 PM.

Details

Summary

Make sure we use the libc++ from the build dir. Currently, by passing -stdlib=libc++, we might pick up the system libc++ on macOS. This change ensures that if LLVM_LIBS_DIR is set, we try to use the libc++ from there.

Diff Detail

Event Timeline

JDevlieghere created this revision.Jul 5 2022, 4:33 PM
Herald added a project: Restricted Project. · View Herald TranscriptJul 5 2022, 4:33 PM
JDevlieghere requested review of this revision.Jul 5 2022, 4:33 PM
labath added a comment.Jul 6 2022, 6:28 AM

I'm afraid this will not work on systems which do not default to libc++ (which includes at least linux and windows), because -stdlib=libc++ is not "equivalent" to -nostdlib++ -lc++ -- the former changes the include paths to use libc++, while the latter doesn't. And, for most of the things that we do here, headers are more important than the library itself.

If the goal here is to preclude any possibility of picking up a different library (it's not clear to me what kind of situation you're referring to in the description), then the most principled approach would be to also use -nostdinc++ and manually add the appropriate include paths. One nice property of that is that it would work with compilers (gcc) which do not understand the -stdlib flag.

The simple solution would be to use the -stdlib=libc++ -nostdlib++ as a combo, but I am not sure if that would solve what it is supposed to solve (as it still relies on clang to autodetect the header path).

Another thing to consider is that historically (back when we were running the tests against the system compiler by default), the USE_LIBCPP flag meant "use the system libc++" (or rather, the default library of the compiler in use). Now we use the in-tree compiler (by default), but our libc++ story is not so clear, as it mostly relies on clang. If the build contains libc++ then we will use that (and sometimes fail due to incorrect usage). If it doesn't, but there's a libc++ on the system somewhere, then we will use that one (and be non-hermetic). Otherwise, we will skip tests which depend on this (and the skipping code uses its own logic, which is not touched here).

Overall, I think it makes sense to use libc++ when we have it available. I am not so sure about the other modes. While I think it would be nice to support running the tests against a random c++ library (similar to how we do it for compilers), I don't think we need to support it unless there someone actively interested in running it. I do think we should support a mode where we don't have a libc++ in the build tree, but I'd be fine if that just causes all of the relevant tests to be skipped.

The thing where that gets interesting is with remote tests. Unlike system libc++, I think we have people (including myself, to some extent) whose job is to make sure those work. Right now, we don't make any special effort to copy the c++ library to the remote system. If it works, it works because the system already has a compatible c++ library lying around. The reason these two are related is that the system libc++ is more likely to be present on a remote system than the one we just built. Messing with the way use libc++ may break some of the remote use cases, so it might be good to take some position on that. Do we not support that? Do we expect the user to make sure the libc++ is on the system already? Do we expect the user to link libc++ statically (I think that's what happens in all of our use cases)?

I realize this isn't very helpful, but I'm trying to explain why I fear this may be more complicated than it may seem at first. Maybe it is not necessary to answer all of these questions for the problem at hand, but these are the questions that have stopped me from doing anything in this area in the past.

I'm afraid this will not work on systems which do not default to libc++ (which includes at least linux and windows), because -stdlib=libc++ is not "equivalent" to -nostdlib++ -lc++ -- the former changes the include paths to use libc++, while the latter doesn't. And, for most of the things that we do here, headers are more important than the library itself.

If the goal here is to preclude any possibility of picking up a different library (it's not clear to me what kind of situation you're referring to in the description), then the most principled approach would be to also use -nostdinc++ and manually add the appropriate include paths. One nice property of that is that it would work with compilers (gcc) which do not understand the -stdlib flag.

The simple solution would be to use the -stdlib=libc++ -nostdlib++ as a combo, but I am not sure if that would solve what it is supposed to solve (as it still relies on clang to autodetect the header path).

Another thing to consider is that historically (back when we were running the tests against the system compiler by default), the USE_LIBCPP flag meant "use the system libc++" (or rather, the default library of the compiler in use). Now we use the in-tree compiler (by default), but our libc++ story is not so clear, as it mostly relies on clang. If the build contains libc++ then we will use that (and sometimes fail due to incorrect usage). If it doesn't, but there's a libc++ on the system somewhere, then we will use that one (and be non-hermetic). Otherwise, we will skip tests which depend on this (and the skipping code uses its own logic, which is not touched here).

Overall, I think it makes sense to use libc++ when we have it available. I am not so sure about the other modes. While I think it would be nice to support running the tests against a random c++ library (similar to how we do it for compilers), I don't think we need to support it unless there someone actively interested in running it. I do think we should support a mode where we don't have a libc++ in the build tree, but I'd be fine if that just causes all of the relevant tests to be skipped.

The thing where that gets interesting is with remote tests. Unlike system libc++, I think we have people (including myself, to some extent) whose job is to make sure those work. Right now, we don't make any special effort to copy the c++ library to the remote system. If it works, it works because the system already has a compatible c++ library lying around. The reason these two are related is that the system libc++ is more likely to be present on a remote system than the one we just built. Messing with the way use libc++ may break some of the remote use cases, so it might be good to take some position on that. Do we not support that? Do we expect the user to make sure the libc++ is on the system already? Do we expect the user to link libc++ statically (I think that's what happens in all of our use cases)?

I realize this isn't very helpful, but I'm trying to explain why I fear this may be more complicated than it may seem at first. Maybe it is not necessary to answer all of these questions for the problem at hand, but these are the questions that have stopped me from doing anything in this area in the past.

Thanks for the thoughtful reply Pavel. The remote tests are something we care about as well, so I'd like to have a solution for that. What do you think about adding a "stdlib" mode to dotest.py which allows you to pick between "system libc++", "system libstdc++" and "just built libc++". The latter would be hermetic, and the former would match what we do today.

labath added a comment.Jul 6 2022, 9:45 AM

Thanks for the thoughtful reply Pavel. The remote tests are something we care about as well, so I'd like to have a solution for that. What do you think about adding a "stdlib" mode to dotest.py which allows you to pick between "system libc++", "system libstdc++" and "just built libc++". The latter would be hermetic, and the former would match what we do today.

Well.. I think that specifying some of this explicitly would be great, but I don't think a choice between a libc++ and libstdc++ makes sense.

The way I see it, we have three kinds of tests:

  1. Tests which don't care which library we use. This is the vast majority of them. The only need it to be there, but the actual test result should not depend on the library used in any way. For these tests, we can use any library we like. (except maybe for the gmodules test variant, but I don't actually know how that one works). I don't think we need to offer a choice here. Ideally we would be able to just pick the option that works (it may not be the same option for each config).
  2. Tests which explicitly require libc++. There shouldn't be too many of these, and ideally these would be limited to tests for the libc++ pretty printers and such. It doesn't make sense to run these against libstdc++. In fact, that would be harmful, because it might actually work, but test the wrong thing. Ideally, we'd give the user the option to choose between the system libc++, just-built libc++ or a way to specify the arguments needed to build&run these kinds of executables.
  3. Tests which explicitly require libstdc++. These is the same thing, except for libstdc++ pretty printers. And that we obviously don't have an in-tree version of libstdc++. And I don't think we have many people interested in running tests against libstdc++, so we probably don't have to go overboard on this one, but it would be nice to be able to keep running the existing tests against system libstdc++ on systems which have one.

Thanks for the thoughtful reply Pavel. The remote tests are something we care about as well, so I'd like to have a solution for that. What do you think about adding a "stdlib" mode to dotest.py which allows you to pick between "system libc++", "system libstdc++" and "just built libc++". The latter would be hermetic, and the former would match what we do today.

Well.. I think that specifying some of this explicitly would be great, but I don't think a choice between a libc++ and libstdc++ makes sense.

The way I see it, we have three kinds of tests:

  1. Tests which don't care which library we use. This is the vast majority of them. The only need it to be there, but the actual test result should not depend on the library used in any way. For these tests, we can use any library we like. (except maybe for the gmodules test variant, but I don't actually know how that one works). I don't think we need to offer a choice here. Ideally we would be able to just pick the option that works (it may not be the same option for each config).
  2. Tests which explicitly require libc++. There shouldn't be too many of these, and ideally these would be limited to tests for the libc++ pretty printers and such. It doesn't make sense to run these against libstdc++. In fact, that would be harmful, because it might actually work, but test the wrong thing. Ideally, we'd give the user the option to choose between the system libc++, just-built libc++ or a way to specify the arguments needed to build&run these kinds of executables.
  3. Tests which explicitly require libstdc++. These is the same thing, except for libstdc++ pretty printers. And that we obviously don't have an in-tree version of libstdc++. And I don't think we have many people interested in running tests against libstdc++, so we probably don't have to go overboard on this one, but it would be nice to be able to keep running the existing tests against system libstdc++ on systems which have one.

Okay, so a binary option to specify the system libc++ or the just-built libc++. The libstd+++ stuff remains unchanged.

I still have to test different configurations, but putting it up here for feedback about the general direction. This doesn't account yet for remote runs. Should be easy to detect based on whether --platform-name is set in dotest.py.

labath added a comment.Jul 7 2022, 3:26 AM

The overall logic and the include and library paths make sense to me. The rpath thingy will not work on windows as it (afaik) has no equivalent feature (it has the equivalent of (DY)LD_LIBRARY_PATH though). Do you just want to make that unsupported. I don't think we're running libc++ tests on windows right now, so I guess nothing will be lost there, though we will need to implement this differently at some point.

And I suppose that the plan is to not use this path at all for remote runs (?) That makes sense to me, since the build tree is unlikely to contain a usable libc++ (unless the remote machine has the same architecture).

lldb/packages/Python/lldbsuite/test/dotest.py
284

libcxx

lldb/packages/Python/lldbsuite/test/make/Makefile.rules
394 ↗(On Diff #442635)

I think this should be set in the "hermetic" case as well (or, given that it is unused, we might delete it completely).

JDevlieghere marked 2 inline comments as done.

The overall logic and the include and library paths make sense to me. The rpath thingy will not work on windows as it (afaik) has no equivalent feature (it has the equivalent of (DY)LD_LIBRARY_PATH though).

Any idea what the libc++ tests do on Windows then? (on linux they seem to use rpath to ensure the tests load the just-built libc++)

labath accepted this revision.Jul 11 2022, 2:01 AM

The overall logic and the include and library paths make sense to me. The rpath thingy will not work on windows as it (afaik) has no equivalent feature (it has the equivalent of (DY)LD_LIBRARY_PATH though).

Any idea what the libc++ tests do on Windows then? (on linux they seem to use rpath to ensure the tests load the just-built libc++)

I believe the libc++ tests don't run at all on windows.

This revision is now accepted and ready to land.Jul 11 2022, 2:01 AM
Herald added a project: Restricted Project. · View Herald TranscriptJul 11 2022, 2:49 PM