如何将现有的make文件与Android NDK集成

18

我有一个巨大的C语言项目,位于$PROJECT/jni目录中。通常通过运行配置脚本创建Makefile,然后使用make编译该项目。

这个项目非常庞大,并包含许多包含源文件和头文件的目录。

我想我在理解Android.mk如何工作方面缺少了基本的理解。它是用来替换当前用于编译项目的配置和Makefile的吗?还是我会把从我的配置脚本生成的Makefile并入到Android.mk中?他们提供的示例非常简单,只有几个源文件。我的jni目录看起来更像:

jni/
  folder1/subfolder1
  folder1/subfolder2
  folder1/source
  folder2/source
  .....
  foldern/source
  configure/
  configure/configure.sh
  Makefile
  Android.mk
生成的 makefile 很详细(有很多配置,并且每个目录下都有一个),所以我不太清楚该如何处理它。
编辑:
主要问题是随 NDK 一起提供的示例非常简单。它们在顶级 jni 目录中有 3-5 个源文件。我的问题是,这是一个具有复杂配置的大型项目,有四个顶级文件夹,每个文件夹下又有许多子目录。我不能简单地将源代码移动到 jni 文件夹中并运行 ndk 编译器。
3个回答

17
回答你的问题,是的,Android.mk就是Android构建系统。谷歌几乎没有提到该文件的“语言”实际上是使用GNU make宏实现的。文档要求您使用这些宏描述您的项目。它们处理所有麻烦的交叉编译细节。我相信谷歌采取这种方法是为了改善随着开发工具的演进而前向可移植性。

结果是(我知道您不想听这个),可能最好的答案是从头开始编写一个适合您的大型项目的NDK Android.mk

这篇文章阐述了我在移植一个包含大约800个文件和300k SLOC的库时做出的相同观察。不幸的是,我花了将近两周时间得出了相同的结论:交叉编译会导致至少一些configure脚本失败(产生错误的config.h文件)。我“发明”了几乎与他在文章中使用的相同技术。但即使我得到了一个干净的构建,生成的静态库仍然无法完全工作。数小时的调试没有得到有用的信息。[警告:我不是配置工具专家。一个大师可能会发现我的错误。就这样吧。]我花了几天时间创建了一个干净的Android.mk。生成的库第一次通过运行了所有测试。它已经干净地移植到了几个开发工具版本。

很不幸,如果要构建一个使用configure而没有自动工具的库,则需要手动为目标环境构建自己的config.h。这可能比听起来的要糟糕。IME系统倾向于在其configure环境中定义比实际使用的更多内容。清楚地了解真正的依赖关系可能会在未来的重构中回报乏味的努力。
文章的摘要陈述了一切:

Autotool仅适用于GNU系统,在交叉编译中使用它可能非常乏味、混淆、容易出错,甚至不可能。本文所述方法是一种hack,应该自行承担风险。

很抱歉我没有更积极的建议。

我完全同意你的观察。在交叉编译环境中,从config.h.in生成config.h是不可能的。然而,在本地运行配置可能会起作用,但那是另一回事 :). - Samveen

5

Gene的答案相结合,我的答案效果最佳。

./configure创建配置文件是基于编译(及可能运行)每个测试的小段C代码。每个测试的成功设置了对应变量在config.h.in模板中来创建config.h。仅编译的测试可以在交叉编译环境中成功测试。然而,编译和运行测试无法在交叉编译环境中运行。

因此,要开始转换过程,您需要将环境变量CPPCCLD和其他工具别名设置为交叉编译器一组工具(可能来自NDK),然后运行./configure。完成此操作后,然后您需要更正config.h以匹配目标环境。这是你最关键和最容易出错的步骤。

至于Android.mk,它遵循一个非常接近Makefile.am的格式,可以轻松转换成它。您可以忽略Makefile.inMakefile,因为它们是从Makefile.am生成的。

file(版本5.11)为例,我使用以下选项运行configure:

./configure --host arm-toshiba-linux-androideabi --build x86_64-linux-gnu \
            --prefix=/data/local/ host_alias=arm-linux-androideabi \
           "CFLAGS=--sysroot=~/ndk/platforms/android-8/arch-arm  -Wall -Wextra" \
           "CPPFLAGS=--sysroot=~/ndk/platforms/android-8/arch-arm" \
            CPP=arm-linux-androideabi-cpp

下一步是采取如下所示的 src/Makefile.am
MAGIC = $(pkgdatadir)/magic
lib_LTLIBRARIES = libmagic.la
include_HEADERS = magic.h

bin_PROGRAMS = file

AM_CPPFLAGS = -DMAGIC='"$(MAGIC)"'
AM_CFLAGS = $(CFLAG_VISIBILITY) @WARNINGS@

libmagic_la_SOURCES = magic.c apprentice.c softmagic.c ascmagic.c \
        encoding.c compress.c is_tar.c readelf.c print.c fsmagic.c \
        funcs.c file.h readelf.h tar.h apptype.c \
        file_opts.h elfclass.h mygetopt.h cdf.c cdf_time.c readcdf.c cdf.h
libmagic_la_LDFLAGS = -no-undefined -version-info 1:0:0
if MINGW
MINGWLIBS = -lgnurx -lshlwapi
else
MINGWLIBS =
endif
libmagic_la_LIBADD = $(LTLIBOBJS) $(MINGWLIBS)

file_SOURCES = file.c
file_LDADD = libmagic.la
CLEANFILES = magic.h
EXTRA_DIST = magic.h.in
HDR= $(top_srcdir)/src/magic.h.in
BUILT_SOURCES = magic.h

magic.h:        ${HDR}
        sed -e "s/X.YY/$$(echo @VERSION@ | tr -d .)/" < ${HDR} > $@

从这里创建Android.mk

最后,也是最重要的一步是修改config.h,以准确反映目标系统的状态。这将是一个手动过程,我无法提供解决方法,主要涉及查看configure.log、查看头文件和“调用”Google。这项工作的结果可以在XDA上找到。


4
以下是翻译的结果:

这里有一个解决方案,可以从标准 Makefile 中构建外部库和 Android 应用程序包:

作为先决条件,您需要安装一切用于命令行 Android 开发所需的工具:

  • 一个独立的工具链,请参阅 Android NDK 中附带的文档;
  • ant。

示例的结构为:一个外部库目录和一个 Android 源代码目录位于同一级别,每个目录中都有一个 Makefile 和一个顶层递归 Makefile:

Makefile
mylib/
    Makefile
android/
    Makefile

mylib/Makefile 是用来构建静态库的:

AR=/path/to/standalone/bin/arm-linux-androideabi-ar
CC=/path/to/standalone/bin/arm-linux-androideabi-gcc

libmylib.a: mylib.o
    $(AR) rcs libmylib.a mylib.o

mylib.o: mylib.c
    $(CC) -c mylib.c -o mylib.o

android/Makefile提供了构建Android包的规则:

  • mylib被修改时,我们需要依赖项来复制它;
  • 我们使用jni/ndkmake.c文件来包装对mylib的调用并提供Android特定的内容;
  • Android软件包依赖于Java源代码和共享库。

Makefile提供了两个目标:release(默认)和debug,以构建发布包或调试包。

NDK_BUILD=/path/to/ndk-build
JAVASRC=src/com/example/ndkmake/NdkMake.java

release: bin/NdkMake-release-unsigned.apk

debug: bin/NdkMake-debug.apk

bin/NdkMake-release-unsigned.apk: libs/armeabi/libndkmake.so $(JAVASRC)
ant release

bin/NdkMake-debug.apk: libs/armeabi/libndkmake.so $(JAVASRC)
ant debug

libs/armeabi/libndkmake.so: jni/ndkmake.c jni/libmylib.a
$(NDK_BUILD)

jni/libmylib.a: ../mylib/libmylib.a
cp ../mylib/libmylib.a jni/libmylib.a
< p > Android.mk 文件提供规则以将静态库作为预构建项包含在项目中。 使用 LOCAL_EXPORT_C_INCLUDES ,我们正在包含 mylib 库的头文件。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := ndkmake
LOCAL_SRC_FILES := ndkmake.c
LOCAL_STATIC_LIBRARIES := mylib-prebuilt
include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := mylib-prebuilt
LOCAL_SRC_FILES := libmylib.a
LOCAL_EXPORT_C_INCLUDES := ../mylib/
include $(PREBUILT_STATIC_LIBRARY)

现在我们只需要一个顶层的Makefile来构建这两个子目录:
all: libmylib package

libmylib:
    cd mylib && $(MAKE)

package:
    cd android && $(MAKE)

任何对库、jni源码或Java源码的更改都会触发包的重新构建。

谢谢您的建议。您能否澄清一些问题:1)您所说的“为NDK添加构建规则”是什么构建规则?您是指在Android.mk文件中通常指定的命令吗?2)我在哪里可以找到有关这个独立工具链的信息?谢谢! - thatidiotguy
你可以在随 NDK 一起提供的文档中找到有关独立工具链的更多信息,这里有一个在线副本:http://www.kandroid.org/ndk/docs/STANDALONE-TOOLCHAIN.html 至于 NDK 的构建规则,我是指保留 Android.mk,但从你的 Makefile 运行 ndk-build(请参见我的示例)。 - Guillaume
只是想告诉你,我正在提供500声望值的赏金,以换取一个非常详细的答案,如果你想参与的话。 - thatidiotguy
@thatidiotguy 嘿...好的,我现在不能给出完全详细的答案,但我会尽快回复。再说一遍,我的答案可能不是你想要的,因为它是关于从Makefiles运行Android构建过程,而不是反过来...但这并不重要,我还是会发布它。 - Guillaume
使用已有的静态库构建代码比链接动态库更容易,因为您的代码不可避免地会链接到glibc(而Android使用Bionic libc)。因此,将现有库构建为静态**.a**,并在其中静态链接所有依赖项更加简单。根据我的个人经验,这样的设置可以完美地工作。 - TheCodeArtist
显示剩余3条评论

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