18

所以我有一个巨大的现有 C 项目,我已经放置在$PROJECT/jni目录中。该项目通常是通过运行配置脚本来创建的,该脚本创建 Makefiles,然后允许通过make.

这个项目比较大,有很多包含源文件和头文件的目录。

我想我在这里缺少对Android.mk应该如何工作的基本理解。是否应该替换当前用于编译项目的配置和生成文件?或者我会将我的配置脚本中生成的 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 个源文件。我的问题是这是一个庞大的项目,配置复杂,有 4 个顶级文件夹,每个文件夹都有许多子目录。我不能简单地将源代码移动到 jni 文件夹并运行 ndk 编译器。

4

3 回答 3

17

要回答您的问题,是的,Android.mk Android 构建系统。谷歌几乎没有提到这个文件的“语言”是作为 GNU make 宏实现的。文档希望您根据这些宏来描述您的项目。他们处理所有蹩脚的交叉编译细节。我很确定Android.mk随着开发工具的发展,Google 已经采用这种方法来提高文件的前向可移植性。

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

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

不幸的是,构建一个在没有自动工具的情况下使用的库意味着为目标环境手动configure构建自己的库。config.h这可能不像听起来那么糟糕。IME 系统往往在其configure环境中定义的内容比实际使用的要多得多。清楚地了解真正的依赖关系可能会在未来的重构中回报繁琐的工作。

文章的摘要声明说明了一切:

Autotool 仅适用于 GNU 系统,使用它进行交叉编译可能非常乏味、令人困惑、容易出错甚至是不可能的。此处描述的方法是一种黑客行为,使用时应自担风险。

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

于 2013-07-02T03:21:33.037 回答
5

我的回答与Gene的回答配合得最好。

./configure的配置文件的创建基于C为每个测试编译(并可能运行)小段代码。每个测试的成功都会在config.h.in模板中设置一个相应的变量来创建config.h. 仅编译测试可以在交叉编译环境中成功测试。但是,编译运行测试不可能在交叉编译环境中运行。

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

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

以文件为例(5.11 版),我使用以下选项运行配置,

./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上获得。

于 2013-07-05T12:19:14.267 回答
4

这里有一个相反的解决方案:从标准 Makefile 构建外部库和 Android 包。

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

  • 一个独立的工具链,请参阅 Android NDK 中包含的文档;
  • 蚂蚁。

该示例的结构是:外部库的目录和同级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文件来包装对 android 的调用mylib并提供特定的东西;
  • 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

Android.mk文件提供了将静态库作为预构建包含在构建中的规则。我们mylib使用LOCAL_EXPORT_C_INCLUDES.

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 源的任何更改都将触发包的重建。

于 2013-06-27T20:32:06.833 回答