如何在使用Automake和libtool的项目中构建*.so模块?

13

我和其他人遇到了同样的问题:

  • 在一个Automake项目中,我有一个由libtool生成的*.la文件(例如module.la),
  • 但我需要它的*.so文件以便在dlopen()中使用它(例如module.so)。

但是: 项目被配置和构建为使用--disable-shared,以确保创建的主二进制文件是一个巨大的静态链接程序,例如main.x(易于部署和调试)。因此,不会创建*.so文件。

程序main.x是一个类似框架的大型应用程序,能够通过dlopen()加载扩展模块,尽管它被静态链接。

手动构建module.so时可以正常工作。但是,在Makefile.am中实现这一点似乎对我来说是不可能的。是的,我可以编写lib_LTLIBRARIES,但是在我的标准--disable-shared下,我得不到*.so 文件。

lib_LTLIBRARIES = module.la
module_so_SOURCES = module.cpp

创建了文件module.la,但是dlopen()拒绝加载它(当然)。

我尝试手动将规则放入Makefile.am中进行构建,这样就可以了:

# Makefile.am (yes, .am)
all: mm_cpp_logger.so

SUFFIXES = .so

%.so: %.cpp
    $(CXX) $(CXXFLAGS) -fPIC -fpic -c -I $(top_srcdir)/include -o $@  $<

%.so: %.o
    $(CXX) $(LDFLAGS) -shared -fPIC -fpic -o $@  $<

但这只能是一个解决方法。我没有获得所有的很好的自动功能,比如依赖检查和安装。

在构建module.so的同时如何仍然使用--disable-shared(或以同样的效果)在Makefile.am中构建主程序?

  • 我能否使用特殊的automake规则将*.la文件后处理为*.so文件?
  • 我能否调整lib_LTLIBRARIES过程以在任何情况下创建*.so文件?
3个回答

14

你要找的东西被称为模块。你可以通过将-all-static添加到应用程序的LDFLAGS来告诉Autotools创建一个静态二进制(可执行文件)。我认为这比使用--disable-shared配置标志更可取(这个标志实际上是针对库而不是可执行文件)。

像这样做应该就可以了:

AM_CPPFLAGS=-I$(top_srcdir)/include

lib_LTLIBRARIES = module.la
module_la_LDFLAGS = -module -avoid-version -shared
module_la_SOURCES = mm_cpp_logger.cpp

bin_PROGRAMS = application
application_LDFLAGS = -all-static
application_SOURCES = main.cpp

通常情况下,.so 文件将会被放置在 .libs/ 子目录中(当然,如果你安装了它,那就不一定了)。

你可以同时构建应用程序和插件(甚至使用单个 Makefile.am),因此无需多次调用 configure

-fPIC (等等参数)的使用应该由 Autotools 自动检测。


更新:这里有一个小技巧,可以让共享库在您期望的位置可用。由于所有共享库最终都会出现在 .libs/ 中,有时将它们放在非隐藏目录中会更好。

以下 makefile 片段创建方便链接(在支持符号链接的平台上;否则将被复制)。只需向您的 makefile 添加此片段(我通常使用一个 -include convenience-link.mk)就足够了(您可能需要在 configure.ac 中添加一个 AC_PROG_LN_S

.PHONY: convenience-link clean-convenience-link

convenience-link: $(lib_LTLIBRARIES)
    @for soname in `echo | $(EGREP) "^dlname=" $^ | $(SED) -e "s|^dlname='\(.*\)'|\1|"`; do  \
        echo "$$soname: creating convenience link from $(abs_builddir)/.libs to $(top_builddir)"; \
        rm -f $(top_builddir)/$$soname ; \
        test -e $(abs_builddir)/.libs/$$soname && \
        cd $(top_builddir) && \
        $(LN_S) $(abs_builddir)/.libs/$$soname $$soname || true;\
    done 

clean-convenience-link:
    @for soname in `echo | $(EGREP) "^dlname=" $(lib_LTLIBRARIES) | $(SED) -e "s|^dlname='\(.*\)'|\1|"`; do  \
        echo "$$soname: cleaning convenience links"; \
        test -L $(top_builddir)/$$soname && rm -f $(top_builddir)/$$soname || true; \
    done 
        
all-local:: convenience-link

clean-local:: clean-convenience-link

哎呀,现在我不得不为了解决libtool问题而费尽心思。我在''application''中用-static-libtool-libs取代了-all-static,同时也在我的其他“方便”库(不是我们正在讨论的那个)上使用了这个方法——因为当我使用-all-static时,dl_blahblah()的东西会导致程序崩溃(我很确定它是罪魁祸首)。无论如何:不幸的是,我仍然可以从.libs/中获取到.libs/mm_cpp_logger.so——当我为模块编写测试时,我发现这有点尴尬。从.libs/中加载似乎...不太对劲。它总是出现在那里,不管我做什么,这正常吗? - towi
我更新了我的答案,提供了一个可能的解决方案,以便在.libs之外获取可用的库。 - umläute
@umläute:不错的解决方案!不幸的是,清理部分没有起作用,因为在开始该部分之前,所有包含“dlname =(.*)”的文件都被删除了(因此grep没有返回任何内容)。我已经将您的片段硬复制到我的Makefile.am文件中。 - Stasik

2

我使用noinst_LTLIBRARIES宏解决了一个类似的问题。

noinst_LTLIBRARIES宏创建静态的、不可安装的库,只能在内部使用。所有的noinst_LTLIBRARIES静态库都会被创建,即使你指定了--disable-static配置选项。

lib_LTLIBRARIES = libtokenclient.la
noinst_LTLIBRARIES = libtokenclient_static.la 

libtokenclient_la_SOURCES = $(TOKEN_SERVER_CLIENT_SOURCES) cDynlib.c cDynlib.h token_mod.h
libtokenclient_la_CFLAGS = @BASE_CFLAGS@
libtokenclient_la_CXXFLAGS = $(libtokenclient_la_CFLAGS)
libtokenclient_la_LIBADD = @B_BASE_OS_LIBS@
libtokenclient_la_LDFLAGS = @LT_PLUGIN_LIBS_FLAGS@ @LIBS_FLAGS@ $(TOKEN_SERVER_CLIENT_EXPORT_SYMBOLS)

libtokenclient_static_la_SOURCES = $(libtokenclient_la_SOURCES)
libtokenclient_static_la_CFLAGS = $(libtokenclient_la_CFLAGS)
libtokenclient_static_la_CXXFLAGS = $(libtokenclient_static_la_CFLAGS)

token_test_SOURCES = $(TEST_SOURCES)
token_test_LDADD = @B_BASE_OS_LIBS@ libtokenclient_static.la
token_test_CFLAGS = @BASE_CFLAGS@
token_test_CXXFLAGS = $(token_test_CFLAGS)

我使用 noinst_LTLIBRARIES 静态库有两个原因:

  1. 为了加快编译时间,我创建静态库作为中间容器来存放需要链接多次的代码:该代码仅编译一次,否则 Automake 将为每个目标编译相同的源文件。
  2. 将代码静态链接到某些可执行文件。

1
根据libtool文档,可能有效的一种方法是将构建分成两个包:主应用程序和插件。这样你就可以(理论上)调用:
./configure --enable-shared=plugins

这样事情就会按照你的期望运作。


你的回答给了我很好的关键词去谷歌搜索,我找到了一个教程。但是,它貌似假设我使用--enable-shared来构建所有内容。尽管其中有一些好的提示,但是它没有提到“插件”。打包?这意味着一个子configure.in,对吗?是的,那可能行得通。带有模块的目录将拥有自己的configure.in,我猜? - towi
1
是的,没错。我所说的“插件”,指的是一种动态加载的扩展。主应用程序和扩展都会得到某种子配置来进行配置。也许你只需要为扩展提供一个子配置就可以了。 - ldav1s

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接