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

Use cross-compilation flags when checking for system directories #3894

Merged

Conversation

bruce-richardson
Copy link
Contributor

When building 32-bit on a 64-bit system the "system directories" for libraries are the 64-bit ones unless the appropriate CFLAGS e.g. -m32, as passed to the compiler. Doing so is tricky, as the flags seem to be stored in the environment, rather than the compiler object. This draft patch is a first-attempt to implement this, and works ok for 32-bit and 64-bit builds on linux using GCC.

If the problem of incorrect system paths can be solved, we may be able to remove the build checks in find_library() and hopefully fix #3881.

def get_library_dirs(self):
key = tuple(self.exelist)
def get_library_dirs_real(self, env):
code=''
Copy link
Member

Choose a reason for hiding this comment

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

[Flake8]

[E225] missing whitespace around operator

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure. I didn't run flake8 and clean up the code much, as I'm looking for initial feedback as to whether this a correct approach or not to fixing the issues. [Hence "DRAFT" in the title :-)]

Copy link
Member

Choose a reason for hiding this comment

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

These comments are automated...

@ePirat
Copy link
Contributor

ePirat commented Jul 17, 2018

Seems related to bug #3720?

Copy link
Member

@nirbheek nirbheek left a comment

Choose a reason for hiding this comment

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

This approach is good, and is a better fix for #3720 than #3916.

CCing @jpakkane

This also needs a test.

paths = [os.path.realpath(p) for p in libstr.split(':') if os.path.exists(os.path.realpath(p))]
return paths

def get_library_dirs(self, env=None):
Copy link
Member

Choose a reason for hiding this comment

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

Is get_library_dirs() called from anywhere that doesn't have access to the environment? If no, we should make this a required argument so we don't have hidden bugs. Then you can also remove the conditional below in _get_compiler_check_args().

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is generally available, except in the case for mesonbuild/backend/backends.py, where we are filtering out rpath dirs which are in the system directory path. There _libdir_is_system calls get_library_dirs() without access to any environment. [That in turn is called by rpaths_for_bundled_shared_libraries which similarly doesn't have any env info]

As an aside: Is there a reason why the cross build compiler flags are stored in an environment object, rather than in a compiler object for the cross-compiler in question? I would have thought that for cross builds we would have multiple compiler objects, each tracking it's own args.

Copy link
Member

Choose a reason for hiding this comment

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

As an aside: Is there a reason why the cross build compiler flags are stored in an environment object, rather than in a compiler object for the cross-compiler in question?

Just cruft from ages past. I think someone recently opened a PR to change this.

Copy link
Member

Choose a reason for hiding this comment

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

mesonbuild/backend/backends.py

The Backend object has the environment object available as self.environment, so you should pass that.

def get_library_dirs(self, env=None):
if env is None:
env = os.environ.copy()
env['LC_ALL'] = 'C'
Copy link
Member

Choose a reason for hiding this comment

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

This is a totally different environment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. However, in the case we don't have an environment available, we need some fallback scheme.

Copy link
Member

Choose a reason for hiding this comment

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

What I mean is, that the env passed to get_library_dirs() in compilers/c.py is an Environment object, while this is os.environ (a dict). We should not conflate the two. Just rename this to osenv or something.

Copy link
Member

Choose a reason for hiding this comment

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

Or, better yet, if you change this function to use _build_wrapper() like c.py, this is no longer a problem.

def get_library_dirs(self, env=None):
if env is None:
env = os.environ.copy()
env['LC_ALL'] = 'C'
stdo = Popen_safe(self.exelist + ['--print-search-dirs'], env=env)[1]
Copy link
Member

Choose a reason for hiding this comment

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

You will want to change this to also use _build_wrapper() and the get_program_dirs() below too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok.

return paths

def get_library_dirs(self, env=None):
key = tuple(self.exelist) # todo include environment here
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Help requested as to how to manage the environment here. Is it possible to hash it in with the compiler to save the results for later?

Copy link
Member

Choose a reason for hiding this comment

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

The Environment object is hashable, afair

@nirbheek
Copy link
Member

Well, this is fun. The unit tests are failing in a spectacular fashion, but only if they are run one after the other. Not if they are run individually. Probably because we're messing up some global state.

This means that we will take into account all the flags set in the
cross file when fetching the list of library dirs, which means we
won't incorrectly look for 64-bit libraries when building for 32-bit.

Signed-off-by: Nirbheek Chauhan <nirbheek@centricular.com>

Closes mesonbuild#3881
@nirbheek nirbheek force-pushed the fix_32-bit_library_search_path branch 3 times, most recently from e586202 to 0c974c2 Compare August 19, 2018 18:08
Shared libraries may not always be linkable without the use of other
libraries, so don't make it a linker error.
@nirbheek nirbheek force-pushed the fix_32-bit_library_search_path branch 2 times, most recently from 14f8deb to fc54046 Compare August 19, 2018 19:28
@nirbheek nirbheek changed the title DRAFT: use cross-compilation flags when checking for system directories Use cross-compilation flags when checking for system directories Aug 19, 2018
@nirbheek nirbheek force-pushed the fix_32-bit_library_search_path branch from fc54046 to 388d086 Compare August 19, 2018 22:17
@nirbheek
Copy link
Member

@mtp401 @ChuckDaniels87 @ignatenkobrain Can you please test this branch to see if that fixes your issues?

@jpakkane
Copy link
Member

Am I correct in understanding that the underlying problem was that compile/link arguments from the cross file were not being used in library lookup tests?

If yes then that is strange because it should do that. Is it only for find_library or are the args missing also from other tests?

@ismaelgv
Copy link
Contributor

@nirbheek this fixes #3958. Thanks!

@nirbheek
Copy link
Member

nirbheek commented Aug 21, 2018

Am I correct in understanding that the underlying problem was that compile/link arguments from the cross file were not being used in library lookup tests?

The algorithm for cc.find_library() used to be: "if not extra_dirs: try linking to -lfoo, if that fails: search manually in extra_dirs, if that fails: search in system dirs ($CC --print-search-dirs)".

The "try linking to -lfoo" part has always read cross-args from the cross file or CFLAGS/LDFLAGS if compiling natively, and still does. $CC --print-search-dirs never did, but no one reported a problem with that.

Then, in 0.47, I made PkgConfigDependency start passing extra_dirs to find_library so that we skip the link check completely and only do manual searching. This is good, except that since we don't pass cross-compile args such as -m64 or -m32 to $CC --print-search-dirs, it prints the location to both 32-bit and 64-bit args, which is what @bruce-richardson reported.

We merged his fix which does a link check against the library found after manually searching to ensure that it's the right arch, but that broke searching for pkg-config libraries that have missing symbols, so I papered around it by only doing the link check for libraries found in the system paths, not extra_dirs.

Then people reported #3881 and #3958, where libraries in the system paths had undefined symbols, which meant we couldn't do a link check at all.

That leads us to this fix. Now we use _build_wrapper (the same function used by all compiler checks) to call $CC --print-search-dirs, so cross-compile and CFLAGS/LDFLAGS are passed to it, and we always get the right library search dirs. No need for a link check against the found library.

HOWEVER, there is still one piece missing in all this. cc.find_library() looks for -L flags specified in LDFLAGS, but cc.find_library(..., dirs: ...) does not since it only does manual searching.

The next step (which can't go into the stable release, and hence isn't in this PR) which will complete the fix for #3720 is to also collect -L flags from the cross file or from LDFLAGS for native and add them to the end of extra_dirs.

Note that has_function() has always used _build_wrapper() so it reads link flags correctly. Not sure what that's about. Would need a test case to see that one.

@jpakkane
Copy link
Member

Gaah! I really wish the library situation was not so completely terrible as it is. But given that it is and we can't change it, we need to work with it. Feel free to merge once you are convinced that this works.

@nirbheek
Copy link
Member

I agree, I really wish we had an introspection API or something for compilers/linkers instead of having to guess/emulate/duplicate their behaviour. Something like "just tell me where this library resolves to".

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

Successfully merging this pull request may close these issues.

Meson 0.47.1 breaks cross builds using Boost Python
7 participants