在构建期间覆盖使用 automake/libtool 构建的共享库中的安装路径,以便在无法使用 DYLD_LIBRARY_PATH 时进行测试

Posted

技术标签:

【中文标题】在构建期间覆盖使用 automake/libtool 构建的共享库中的安装路径,以便在无法使用 DYLD_LIBRARY_PATH 时进行测试【英文标题】:Overriding installation path in shared libraries built with automake/libtool during build for testing when DYLD_LIBRARY_PATH cannot be used 【发布时间】:2017-07-07 20:52:01 【问题描述】:

概要

从 OS X 10.11 开始,当系统完整性保护开启时,动态链接器环境变量不会传递给子进程。在一个产生两个库的项目中,一个依赖于另一个库,链接到库的测试代码(可执行文件的外壳包装器)无法运行,因为安装路径硬连线到库中,库尚未安装,并且动态链接器将尝试使用库中的硬连线路径找到它们。因为DYLD_LIBRARY_PATH 没有传递给子进程(例如,由测试包装脚本运行的可执行文件),所以不可能指示链接器到别处查找。

详情

该项目由两个库组成,一个是共享库,另一个是加载共享库的dlopen'able 模块。

这是automake sn-p:

#-------------------
# shared library
lib_LTLIBRARIES           += %D%/liblua_udunits2.la

%C%_liblua_udunits2_la_CFLAGS = $(UDUNITS2_CFLAGS) $(LUA_INCLUDE)
%C%_liblua_udunits2_la_SOURCES = %D%/lua_udunits2.c

#-------------------
# dlopen'able module

luaexec_LTLIBRARIES = %D%/udunits2.la

%C%_udunits2_la_CFLAGS  = $(UDUNITS2_CFLAGS) $(LUA_INCLUDE)
%C%_udunits2_la_LIBADD  = $(UDUNITS2_LIBS) %D%/liblua_udunits2.la
%C%_udunits2_la_SOURCES = %D%/udunits2.c
%C%_udunits2_la_LDFLAGS = -L%D% -module

这会产生

liblua_udunits2.dylib udunits2.so

链接器在构建udunits2.so 时,会记录在liblua_udunits2.dylib 中找到的安装名称,

% otool -D lua_udunits2/.libs/liblua_udunits2.dylib

lua_udunits2/.libs/liblua_udunits2.dylib:
/usr/local/lib/liblua_udunits2.0.dylib

转入udunits2.so

% otool -L lua_udunits2/.libs/udunits2.0.so

lua_udunits2/.libs/udunits2.0.so:
/usr/local/lib/libudunits2.0.dylib (compatibility version 2.0.0, current version 2.0.0)
/usr/lib/libexpat.1.dylib (compatibility version 7.0.0, current version 8.0.0)
/usr/local/lib/liblua_udunits2.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.60.2)

测试代码是一个shell脚本,它调用一个可执行文件,该可执行文件加载udunits2.so,然后尝试加载liblua_udunits2.dylib。这会失败,因为它使用的是 liblua_udunits2.dylib 的安装名称,并且尚未安装。

我已经通过添加解决了以前版本的 OSX 的问题

TESTS_ENVIRONMENT += DYLD_LIBRARY_PATH="$(top_builddir)/$(PACKAGE_NAME)/.libs"

用于测试代码。

但是,从 OS X 10.11 (El Capitan) 开始,如果系统完整性保护已开启,在某些情况下dynamic linker environment variables are not passed to subprocesses。在我的情况下,测试代码是一个调用可执行文件的脚本,因此 DYLD_LIBRARY_PATH 没有传递给可执行文件,我无法覆盖库加载路径。 (该脚本是我的代码外部的第三方测试框架的一部分,因此不能将其修改为显式设置 DYLD_LIBRARY_PATH)。

问题

有没有办法让 automake/libtool 使用构建目录路径来创建库以便继续测试,然后在安装过程中使用安装路径重新链接它们?

【问题讨论】:

使用install_name_tool更改路径... 这可行,但必须小心地将其插入到生成的 Makefile 中,并且我更愿意尽可能将 automake 输出视为一个黑盒子,以保持向前兼容。 【参考方案1】:

这样做的最少侵入性方法似乎是使用 install_name_tool(正如 I'L'I 在问题的 cmets 中提到的那样),希望对 automake 如何实现事物的依赖很少。

我的解决方案是创建一个依赖于dlopen'able 模块的目标,并将动态库依赖项的路径从安装目录更改为构建目录。这允许链接到模块的测试成功加载动态库。安装完成后,依赖路径恢复到安装路径。

修改后的Makefile.am 如下所示:

#-------------------
# shared library
lib_LTLIBRARIES           += %D%/liblua_udunits2.la

%C%_liblua_udunits2_la_VERSION_MAJOR = 0
%C%_liblua_udunits2_la_VERSION_MINOR = 0

%C%_liblua_udunits2_la_CFLAGS = $(UDUNITS2_CFLAGS) $(LUA_INCLUDE)
%C%_liblua_udunits2_la_SOURCES = %D%/lua_udunits2.c
%C%_liblua_udunits2_la_LDFLAGS = -version-info $(%C%_liblua_udunits2_la_VERSION_MAJOR):$(%C%_liblua_udunits2_la_VERSION_MINOR)


#-------------------
# dlopen'able module

luaexec_LTLIBRARIES = %D%/udunits2.la

%C%_udunits2_la_VERSION_MAJOR = 0
%C%_udunits2_la_VERSION_MINOR = 0

%C%_udunits2_la_CFLAGS  = $(UDUNITS2_CFLAGS) $(LUA_INCLUDE)
%C%_udunits2_la_LIBADD  = $(UDUNITS2_LIBS) %D%/liblua_udunits2.la
%C%_udunits2_la_SOURCES = %D%/udunits2.c
%C%_udunits2_la_LDFLAGS = -L%D% -module -version-info $(%C%_udunits2_la_VERSION_MAJOR):$(%C%_udunits2_la_VERSION_MINOR)

if HOST_OS_IS_DARWIN

noinst_DATA = %D%/fix_install_name

%C%_DYLIB_NAME  = liblua_udunits2.$(%C%_liblua_udunits2_la_VERSION_MAJOR).dylib
%C%_MODULE_NAME = udunits2.$(%C%_udunits2_la_VERSION_MAJOR).so

%C%_LIBSDIR = $(abs_builddir)/%D%/.libs/

%D%/fix_install_name : %D%/udunits2.la
    install_name_tool               \
        -change                 \
        $(libdir)/$(%C%_DYLIB_NAME)     \
        $(%C%_LIBSDIR)/$(%C%_DYLIB_NAME)    \
        $(%C%_LIBSDIR)/$(%C%_MODULE_NAME)
    touch $@

install-data-hook :
    install_name_tool                   \
        -change                     \
        $(%C%_LIBSDIR)/$(%C%_DYLIB_NAME)            \
        $(libdir)/$(%C%_DYLIB_NAME)         \
        $(DESTDIR)$(luaexecdir)/$(%C%_MODULE_NAME)

endif

注意事项:

现在明确指定库版本,以便确定动态库和 dlopen'able 模块的名称 代码使用库在.libs 中暂存的事实 代码明确使用.so.dylib 扩展。我不相信有办法从 libtool 中获取这些信息。

【讨论】:

以上是关于在构建期间覆盖使用 automake/libtool 构建的共享库中的安装路径,以便在无法使用 DYLD_LIBRARY_PATH 时进行测试的主要内容,如果未能解决你的问题,请参考以下文章

Maven 在构建期间没有运行 Jacoco

在测试期间,如何覆盖 urlconf 中使用的 Django 设置?

gcov 与 CMake 使用单独的构建目录

如何使用 PlistBuddy 添加设置而不覆盖现有设置?

在 Maven 发布期间覆盖属性失败

在 cp 循环期间覆盖的文件