编写Android.mk中的LOCAL_SRC_FILES的终极技巧

本文详细介绍了如何使用高级方法简化Android.mk文件中源文件的引入过程,包括引入单个目录及其子目录下的所有cpp和c源文件,以及多个目录下的多种后缀名的源文件。通过使用shell命令、模式替换等技术,极大地提高了开发效率。
转载: http://blog.youkuaiyun.com/xiangzilv1987/article/details/16805701

问题的引入

在使用NDK编译C/C++项目的过程中,免不了要编写Android.mk文件,其中最重要的就是LOCAL_SRC_FILES源文件列表.
考虑有如下源文件分布的情况:

cpp文件全部位于android项目下的jni文件夹下,结构如下

    jni    
     |---1.cpp
     |---2.cpp
     |---Android.mk
     |---Application.mk
     |---ndk_test.cpp
     |---src    
     |    |---core
     |    |    |---core1.cpp
     |    |    |---core2.cpp
     |    |---src1.cpp
     |    |---src2.cpp

按照通常的写法,在android.mk中,应该写入

LOCAL_SRC_FILES := ndk_test.cpp \
                1.cpp \
                2.cpp \
                src/src1.cpp \
                src/src2.cpp \
                src/core/core1.cpp \
                src/core/core2.cpp

繁琐不堪!

初步解法:一句话引入单个目录(不包括子目录)下的所有cpp源文件


继续上面的情况为例,我可以这样写

MY_CPP_LIST := $(wildcard $(LOCAL_PATH)/*.cpp)
MY_CPP_LIST += $(wildcard $(LOCAL_PATH)/src/*.cpp)
MY_CPP_LIST += $(wildcard $(LOCAL_PATH)/src/core/*.cpp)

LOCAL_SRC_FILES := $(MY_CPP_LIST:$(LOCAL_PATH)/%=%)

问题解决.
简单解释一下上面的几句话

  1. MY_CPP_LIST := $(wildcard $(LOCAL_PATH)/*.cpp),这句话的意思是使用wildcard函数获取$(LOCAL_PATH)目录也就是jni目录下的所有后缀名为cpp的文件,并把结果放到变量MY_CPP_LIST里.我们知道$(LOCAL_PATH)指的是当前Android.mk文件所在目录,所以通过这句话,MY_CPP_LIST中的值应该是jni/1.cpp jni/2.cpp jni/ndk_test.cpp.
  2. MY_CPP_LIST += $(wildcard $(LOCAL_PATH)/src/*.cpp), 获取jni/src目录下的源文件,并追加到变量MY_CPP_LIST
  3. MY_CPP_LIST += $(wildcard $(LOCAL_PATH)/src/core/*.cpp),同上,获取jni/src/core目录下的源文件
  4. 通过以上几步,得到MY_CPP_LIST中内容是jni/1.cpp jni/2.cpp jni/ndk_test.cpp jni/src/src1.cpp jni/src/src2.cpp jni/src/core/core1.cpp jni/src/core/core2.cpp
  5. LOCAL_SRC_FILES := $(MY_CPP_LIST:$(LOCAL_PATH)/%=%),前面我们获取的文件都是以jni开头的,而真正编译所需要的文件都应该是直接从jni目录开始的,所以我们使用模式替换把所有文件名前面的jni/去掉.

这里我解释一下$(MY_CPP_LIST:$(LOCAL_PATH)/%=%)的语法含义,它的意思是对MY_CPP_LIST中每一项,应用冒号后面的规则,规则是什么呢?规则是$(LOCAL_PATH)/%=%,意思是,查找所有$(LOCAL_PATH)/开头的项,并截取后面部分

最后一句话也可以使用subst函数写成:

#替换每一项中的 "$(LOCAL_PATH)/"""(空)
LOCAL_SRC_FILES := $(subst $(LOCAL_PATH)/, , $(MY_CPP_LIST))  

或使用patsubst函数写成

#同模式替换,这里使用patsubst函数
LOCAL_SRC_FILES := $(patsubst $(LOCAL_PATH)/%, %, $(MY_CPP_LIST))  

具体语法请参考:Functions for String Substitution and Analysis

实际使用中,可以把代码放在jni目录以外的目录里,这时只要修改wildcard函数里的相对路径就可以了,甚至也可以使用绝对路径,只要你愿意.

以上代码已经足以应付大多数情况了,不过人的懒惰是无极限的,像上面的情况我的所有源文件都在jni目录下,为什么还要把每个子目录都写一行呢,不太优雅呀,最好能写一句话把jni目录下的所有源文件都引入.

进阶:引入单个目录(包括子目录)下的所有cpp源文件

为了达到引入目录下的所有源文件,包括子目录这个目标,我在android.mk中这样写

#声明一个变量MY_CPP_PATH表示源码目录
MY_CPP_PATH := $(LOCAL_PATH)/ 

#获取目录下的所有文件    
My_All_Files := $(shell find $(MY_CPP_PATH)/.)
My_All_Files := $(My_All_Files:$(MY_CPP_PATH)/./%=$(MY_CPP_PATH)%)

#从My_All_Files中再次提取所有的cpp文件,这里也可以使用filter函数
MY_CPP_LIST := $(foreach c_file,$(My_All_Files), $(wildcard $(c_file)/*.cpp) ) 
MY_CPP_LIST := $(MY_CPP_LIST:$(LOCAL_PATH)/%=%)

LOCAL_SRC_FILES := $(MY_CPP_LIST)

通过以上几行,成功得到了jni目录包含它的子目录下的所有cpp源文件,并正确编译.实际使用中,代码不一定存放在jni目录下,修改MY_CPP_PATH就可以了,注意:MY_CPP_PATH最好使用以$(LOCAL_PATH)开头的相对目录

这种写法极大的方便了项目的开发,以前在源码目录下新建cpp源文件,新建目录都不需要再来修改android.mk文件了.

还有一个问题,上面代码里只是引入cpp文件,如果源码文件夹下还有c文件呢,怎么办?再多写几行?

进阶2.0:引入单个目录(包括子目录)下的所有*.cpp和*.c源文件

这里,我直接给出代码

MY_CPP_PATH  := $(LOCAL_PATH)/
My_All_Files := $(shell find $(MY_CPP_PATH)/.)
My_All_Files := $(My_All_Files:$(MY_CPP_PATH)/./%=$(MY_CPP_PATH)%)
MY_CPP_LIST  := $(filter %.cpp %.c,$(My_All_Files)) 
MY_CPP_LIST  := $(MY_CPP_LIST:$(LOCAL_PATH)/%=%)

LOCAL_SRC_FILES := $(MY_CPP_LIST)

代码中用到了filter函数.

还不满足?如果项目的源码有多个目录放在不同的地方,而且有多个后缀,怎么办?

终极进阶:引入多个目录(包括子目录)下的多个后缀名的源文件

上代码(2013年10月9日修正):

# 扫描目录下的所有源文件
MY_FILES_PATH  :=  $(LOCAL_PATH) \
                   $(LOCAL_PATH)/../../Classes

MY_FILES_SUFFIX := %.cpp %.c %.cc

My_All_Files := $(foreach src_path,$(MY_FILES_PATH), $(shell find $(src_path) -type f) ) 
My_All_Files := $(My_All_Files:$(MY_CPP_PATH)/./%=$(MY_CPP_PATH)%)
MY_SRC_LIST  := $(filter $(MY_FILES_SUFFIX),$(My_All_Files)) 
MY_SRC_LIST  := $(MY_SRC_LIST:$(LOCAL_PATH)/%=%)
LOCAL_SRC_FILES := $(MY_SRC_LIST)

以上代码中,变量MY_FILES_PATH保存源文件所在目录,MY_FILES_SUFFIX保存源文件的后缀名

原创文章,转载请注明,谢谢!

PS:如何debug 一个android.mk文件

有一个办法,那就是在编译过程输出android.mk文件中变量的值,就可以观察分析问题所在了,使用代码

$(warning $(LOCAL_SRC_FILES))

就可以在编译过程中从终端窗口中观察到变量LOCAL_SRC_FILES的值

<think>我们正在讨论Android.mk中的LOCAL_SRC_FILES变量,它用于指定要编译的源文件。用户希望只选择部分文件进行编译,而不是全部文件。根据提供的引用,有几种方法可以实现: 1. **手动指定文件路径**(引用[1]和[2]): 在LOCAL_SRC_FILES中显式列出所需的文件,每个文件前可以加上`$(LOCAL_PATH)/`前缀。 例如: ```makefile LOCAL_SRC_FILES := $(LOCAL_PATH)/src/com/example/test/MainActivity.java \ $(LOCAL_PATH)/src/com/example/test/Demo1.java ``` 2. **使用wildcard函数匹配特定模式**(引用[5]): 使用Makefile的wildcard函数来匹配特定目录下满足条件的文件,然后进行筛选。 例如,只编译jni目录下所有的cpp文件: ```makefile MY_CPP_LIST := $(wildcard $(LOCAL_PATH)/*.cpp) LOCAL_SRC_FILES := $(MY_CPP_LIST:$(LOCAL_PATH)/%=%) ``` 这种方法可以方便地选择某个目录下同一类型的文件。 3. **结合使用wildcard和filter函数**(引用[3]和[5]): 如果需要更精细的控制,比如排除某些文件,可以使用filter函数。 例如,排除特定文件(比如test.cpp): ```makefile ALL_CPP := $(wildcard $(LOCAL_PATH)/*.cpp) # 过滤掉test.cpp MY_CPP_LIST := $(filter-out $(LOCAL_PATH)/test.cpp, $(ALL_CPP)) LOCAL_SRC_FILES := $(MY_CPP_LIST:$(LOCAL_PATH)/%=%) ``` 4. **使用shell命令(复杂情况)**(引用[3]): 如果需要递归目录并筛选,可以使用shell命令(如find)结合Makefile的函数。 例如,只选择src/core目录下所有的cpp文件: ```makefile LOCAL_SRC_FILES := $(shell find $(LOCAL_PATH)/src/core -name '*.cpp' | sed 's:^$(LOCAL_PATH)/::') ``` **注意事项**: - 使用wildcard函数时,注意文件路径的书写。`$(LOCAL_PATH)`代表当前Android.mk文件所在目录。 - 如果文件分布在多个目录,可以多次使用wildcard并追加到同一个变量中(如引用[5]所示)。 - 避免使用绝对路径,通常使用相对于`$(LOCAL_PATH)`的相对路径。 综上,选择部分文件的方法主要是手动列出或使用模式匹配(wildcard)结合过滤(filter)来实现。 为了清晰,下面给出一个示例,假设我们只需要编译jni目录下除了test.cpp以外的所有cpp文件,以及src目录下的部分文件(比如a.cpp和b.cpp): ```makefile # 获取jni目录下所有的cpp文件(不包括子目录) JNI_CPP := $(wildcard $(LOCAL_PATH)/*.cpp) # 排除test.cpp JNI_CPP := $(filter-out $(LOCAL_PATH)/test.cpp, $(JNI_CPP)) # 获取src目录下的a.cpp和b.cpp SRC_CPP := $(LOCAL_PATH)/src/a.cpp $(LOCAL_PATH)/src/b.cpp # 合并 MY_SRC_FILES := $(JNI_CPP) $(SRC_CPP) # 去掉路径前面的$(LOCAL_PATH)/ LOCAL_SRC_FILES := $(MY_SRC_FILES:$(LOCAL_PATH)/%=%) ``` 这样,LOCAL_SRC_FILES就只包含了我们选定的部分文件。 §§相关问题§§ 1.Android.mk中如何排除特定的源文件? 2. 如何递归地添加多个目录下的源文件到LOCAL_SRC_FILES? 3. Android.mkLOCAL_SRC_FILES支持哪些通配符? </think>### Android.mkLOCAL_SRC_FILES选择部分文件的方法 在Android.mk文件中,通过`LOCAL_SRC_FILES`选择部分源文件有几种常用方法: #### 1. **手动指定文件路径** 显式列出需要的文件路径(引用中最基础的方法): ```makefile LOCAL_SRC_FILES := $(LOCAL_PATH)/src/main.cpp \ $(LOCAL_PATH)/utils/helper.cpp \ $(LOCAL_PATH)/core/algorithm.cpp ``` > 适合文件数量少且路径明确的情况[^1][^2]。 #### 2. **通配符模式匹配** 使用`wildcard`函数按模式筛选文件(高效方法): ```makefile # 选择jni目录下所有.cpp文件 MY_SRC := $(wildcard $(LOCAL_PATH)/*.cpp) # 选择src/core目录下所有.cpp文件 MY_SRC += $(wildcard $(LOCAL_PATH)/src/core/*.cpp) LOCAL_SRC_FILES := $(MY_SRC:$(LOCAL_PATH)/%=%) ``` > 通过路径模式批量选择文件,避免手动枚举[^5]。 #### 3. **文件过滤** 用`filter`/`filter-out`动态排除文件: ```makefile ALL_SRC := $(wildcard $(LOCAL_PATH)/src/*.cpp) # 排除不需要的test文件 LOCAL_SRC_FILES := $(filter-out %_test.cpp, $(ALL_SRC)) ``` > 适合需要排除特定类型文件(如测试文件)的场景[^3]。 #### 4. **混合模式** 结合通配符和显式声明: ```makefile LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/core/*.cpp) \ $(LOCAL_PATH)/legacy/module.cpp ``` ### 关键注意事项 1. **路径处理** 使用`$(LOCAL_PATH)/`作为前缀确保路径正确性,最终通过`:%=$`移除冗余路径(见引用[5])。 2. **文件类型** C/C++文件通常用`.c`/`.cpp`,Java用`.java`,需保持后缀一致。 3. **性能优化** 避免使用`find`等复杂Shell命令(如引用[3]),优先使用Makefile内置函数(`wildcard`)。 > 示例:只编译`src/`目录下非测试文件 > ```makefile > SRC_DIR := $(LOCAL_PATH)/src > ALL_FILES := $(wildcard $(SRC_DIR)/*.cpp) > LOCAL_SRC_FILES := $(filter-out %_test.cpp, $(ALL_FILES):$(LOCAL_PATH)/%=%) > ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值