From bc8a7e134d194f93e58d46bd7ed1c125b8c72194 Mon Sep 17 00:00:00 2001 From: Aditya Vidyadhar Kamath Date: Tue, 10 Oct 2023 08:13:14 -0500 Subject: [PATCH 1/2] Use target.aix_so_archive to decide to archive shared library in AIX Previously, AIX support was updated to archive shared libraries per AIX platform conventions, which expect .a files that contain .so files. This is usually correct, but an edge case occurs for loadable plugins, e.g. what meson creates for `shared_module()`. A notable example is python extensions (SciPy, for example). These should *not* be archived, because the .so file itself needs to be loaded as a plugin. For example, SciPy fails to import in the python interpreter. Handle this by differentiating between plugins and regular libraries, and only archiving when safe to do so. Fixes #12219 (cherry picked from commit f4d19db25e3183833a5e2d77ef5ab40b9b523144) --- mesonbuild/backend/backends.py | 3 +++ mesonbuild/backend/ninjabackend.py | 12 ++++-------- mesonbuild/build.py | 6 ++++++ mesonbuild/minstall.py | 3 ++- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 73741a4412ee..6c0505d65ca6 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -339,6 +339,9 @@ def get_target_filename_for_linking(self, target: T.Union[build.Target, build.Cu # On all other platforms, we link to the library directly. if isinstance(target, build.SharedLibrary): link_lib = target.get_import_filename() or target.get_filename() + # In AIX, if we archive .so, the blibpath must link to archived shared library otherwise to the .so file. + if mesonlib.is_aix() and target.aix_so_archive: + link_lib = re.sub('[.][a]([.]?([0-9]+))*([.]?([a-z]+))*', '.a', link_lib.replace('.so', '.a')) return os.path.join(self.get_target_dir(target), link_lib) elif isinstance(target, build.StaticLibrary): return os.path.join(self.get_target_dir(target), target.get_filename()) diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index bc86355847c1..1ae264188e75 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -1054,8 +1054,9 @@ def generate_target(self, target): #In AIX, we archive shared libraries. If the instance is a shared library, we add a command to archive the shared library #object and create the build element. if isinstance(target, build.SharedLibrary) and self.environment.machines[target.for_machine].is_aix(): - elem = NinjaBuildElement(self.all_outputs, linker.get_archive_name(outname), 'AIX_LINKER', [outname]) - self.add_build(elem) + if target.aix_so_archive: + elem = NinjaBuildElement(self.all_outputs, linker.get_archive_name(outname), 'AIX_LINKER', [outname]) + self.add_build(elem) def should_use_dyndeps_for_target(self, target: 'build.BuildTarget') -> bool: if mesonlib.version_compare(self.ninja_version, '<1.10.0'): @@ -3505,11 +3506,6 @@ def generate_link(self, target: build.BuildTarget, outname, obj_list, linker: T. else: dependencies = target.get_dependencies() internal = self.build_target_link_arguments(linker, dependencies) - #In AIX since shared libraries are archived the dependencies must - #depend on .a file with the .so and not directly on the .so file. - if self.environment.machines[target.for_machine].is_aix(): - for i, val in enumerate(internal): - internal[i] = linker.get_archive_name(val) commands += internal # Only non-static built targets need link args and link dependencies if not isinstance(target, build.StaticLibrary): @@ -3714,7 +3710,7 @@ def generate_ending(self): # Add the first output of each target to the 'all' target so that # they are all built #Add archive file if shared library in AIX for build all. - if isinstance(t, build.SharedLibrary): + if isinstance(t, build.SharedLibrary) and t.aix_so_archive: if self.environment.machines[t.for_machine].is_aix(): linker, stdlib_args = self.determine_linker_and_stdlib_args(t) t.get_outputs()[0] = linker.get_archive_name(t.get_outputs()[0]) diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 3c62fe8b9ee3..4e42620e7595 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -2148,6 +2148,9 @@ class SharedLibrary(BuildTarget): typename = 'shared library' + # Used by AIX to decide whether to archive shared library or not. + aix_so_archive = True + def __init__( self, name: str, @@ -2495,6 +2498,9 @@ class SharedModule(SharedLibrary): typename = 'shared module' + # Used by AIX to not archive shared library for dlopen mechanism + aix_so_archive = False + def __init__( self, name: str, diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 49006917e914..e9e84fd99fe2 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -713,8 +713,9 @@ def install_targets(self, d: InstallData, dm: DirMaker, destdir: str, fullprefix # In AIX, we archive our shared libraries. When we install any package in AIX we need to # install the archive in which the shared library exists. The below code does the same. # We change the .so files having lt_version or so_version to archive file install. + # If .so does not exist then it means it is in the archive. Otherwise it is a .so that exists. if is_aix(): - if '.so' in t.fname: + if not os.path.exists(t.fname) and '.so' in t.fname: t.fname = re.sub('[.][a]([.]?([0-9]+))*([.]?([a-z]+))*', '.a', t.fname.replace('.so', '.a')) if not self.should_install(t): continue From c51cfcb1b589a2347c4eb2a79706bfd18348fbf8 Mon Sep 17 00:00:00 2001 From: Aditya Vidyadhar Kamath Date: Sun, 29 Oct 2023 23:02:24 -0500 Subject: [PATCH 2/2] Fix traceback on AIX in shlib archiving code A compiler may not have a linker: ``` 'NoneType' object has no attribute 'get_command_to_archive_shlib' ``` (cherry picked from commit cfec255027ca7c928defcc665a12e91b439c7c3f) --- mesonbuild/compilers/compilers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index cb8eae56ac4e..8e29816c0a92 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -960,6 +960,8 @@ def get_archive_name(self, filename: str) -> str: return self.linker.get_archive_name(filename) def get_command_to_archive_shlib(self) -> T.List[str]: + if not self.linker: + return [] return self.linker.get_command_to_archive_shlib() def thread_flags(self, env: 'Environment') -> T.List[str]: