Android——编译体系中的 Copy

本文介绍了Android系统层中常见的几种文件复制方法,包括PRODUCT_COPY_FILES的使用方式、copy_from和copy_to配合ALL_PREBUILT的注意事项以及通过BUILD_PREBUILT进行文件复制的过程。此外,还详细讲解了如何复制整个目录。

对andriod系统层进行开发,或者进行移植时,时常需要添加文件到编译体系中,在最终的编译中复制到out中,最后打包成镜像,这里总结一下Copy File 方法,这里以我的 android 4.2.1为例

一.PRODUCT_COPY_FILES :

这个变量就是用来标记Copy操作的,比较常见的形式如下:

#jscese cp 3g script and 
PRODUCT_COPY_FILES += 
     $(DEVICE_SOURCES)/3g-script/ip-up-datakey:system/etc/ppp/ip-up-datakey 
     $(DEVICE_SOURCES)/3g-script/ip-down-datakey:system/etc/ppp/ip-down-datakey 
     $(DEVICE_SOURCES)/3g-script/init.gprs-pppd:system/etc/ppp/init.gprs-pppd 
     device/sample/etc/apns-full-conf.xml:system/etc/apns-conf.xml 
     #external/usb-modeswitch/usb_modeswitch.d:system/etc/usb_modeswitch.d
#PRODUCT_COPY_FILES +=   
     #$(DEVICE_SOURCES)/3g-script/ip-down-datakey:system/etc/ppp/ip-down-datakey 
      
#end
可以看到 格式: 中间用 “ : ” 隔开!

 


编译过源码的都知道在最开始 编译的时候 都会出现:

 

PRODUCT_COPY_FILES frameworks/base/data/sounds/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg ignored.
PRODUCT_COPY_FILES frameworks/base/data/sounds/effects/ogg/KeypressStandard.ogg:system/media/audio/ui/KeypressStandard.ogg ignored.
PRODUCT_COPY_FILES frameworks/base/data/sounds/effects/ogg/KeypressSpacebar.ogg:system/media/audio/ui/KeypressSpacebar.ogg ignored.
PRODUCT_COPY_FILES frameworks/base/data/sounds/effects/ogg/KeypressDelete.ogg:system/media/audio/ui/KeypressDelete.ogg ignored.
PRODUCT_COPY_FILES frameworks/base/data/sounds/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg ignored.
...

这样的打印,现在告诉你这东西在哪里打出来的~ /build/core/Makefile 中最开始的:

 

# -----------------------------------------------------------------
# Define rules to copy PRODUCT_COPY_FILES defined by the product.
# PRODUCT_COPY_FILES contains words like <SOURCE file= "" >:<DEST file= "" >[:<OWNER>].
# <DEST file= "" > is relative to $(PRODUCT_OUT), so it should look like,
# e.g., system/etc/file.xml.
# The filter part means only eval the copy-one-file rule if this
# src:dest pair is the first one to match the same dest
#$( 1 ): the src:dest pair
define check-product-copy-files
$( if $(filter %.apk, $( 1 )),$(error 
     Prebuilt apk found in PRODUCT_COPY_FILES: $( 1 ), use BUILD_PREBUILT instead!))
endef
# filter out the duplicate <SOURCE file= "" >:<DEST file= "" > pairs.
unique_product_copy_files_pairs :=
$(foreach cf,$(PRODUCT_COPY_FILES), 
     $( if $(filter $(unique_product_copy_files_pairs),$(cf)),,
         $(eval unique_product_copy_files_pairs += $(cf))))
unique_product_copy_files_destinations :=
$(foreach cf,$(unique_product_copy_files_pairs), 
     $(eval _src := $(call word-colon, 1 ,$(cf))) 
     $(eval _dest := $(call word-colon, 2 ,$(cf))) 
     $(call check-product-copy-files,$(cf)) 
     $( if $(filter $(unique_product_copy_files_destinations),$(_dest)), 
         $(info PRODUCT_COPY_FILES $(cf) ignored.), 
         $(eval _fulldest := $(call append-path,$(PRODUCT_OUT),$(_dest))) 
         $( if $(filter %.xml,$(_dest)),
             $(eval $(call copy-xml-file-checked,$(_src),$(_fulldest))),
             $(eval $(call copy-one-file,$(_src),$(_fulldest)))) 
         $(eval ALL_DEFAULT_INSTALLED_MODULES += $(_fulldest)) 
         $(eval unique_product_copy_files_destinations += $(_dest))))
unique_product_copy_files_pairs :=
unique_product_copy_files_destinations :=</DEST></SOURCE></DEST></OWNER></DEST></SOURCE

 

这就是 PRODUCT_COPY_FILES 起作用的地方! 可以看上面注释,描述了规则用法,只能copy file

也就是上面编译打印的出处,代表忽略项. 详细的规则可跟进去细看,无非是依赖Copy之类的.

 

 

这里需要注意一点, PRODUCT_COPY_FILES 不能在 Android.mk 中使用 添加新的Copy 项!

 

# Can't use first-makefiles-under here because
# --mindepth= 2 makes the prunes not work.
subdir_makefiles := 
     $(shell build/tools/findleaves.py --prune=out --prune=.repo --prune=.git $(subdirs) Android.mk)
  
include $(subdir_makefiles)
  
endif # ONE_SHOT_MAKEFILE
  
# Now with all Android.mks loaded we can do post cleaning steps.
include $(BUILD_SYSTEM)/post_clean.mk
  
ifeq ($(stash_product_vars), true )
   $(call assert -product-vars, __STASHED)
endif

 

在这里加载所有的Android.mk ,重点在后面的 assert-product-vars 函数:

 

#
# Assert that the the variable stashed by stash-product-vars remains untouched.
# $( 1 ): The prefix as supplied to stash-product-vars
#
define assert -product-vars
$(strip 
   $(eval changed_variables:=)
   $(foreach v,$(_product_stash_var_list), 
     $( if $(call streq,$($(v)),$($(strip $( 1 ))_$(call rot13,$(v)))),, 
         $(eval $(warning $(v) has been modified: $($(v)))) 
         $(eval $(warning previous value: $($(strip $( 1 ))_$(call rot13,$(v))))) 
         $(eval changed_variables := $(changed_variables) $(v))) 
   
   $( if $(changed_variables),
     $(eval $(error The following variables have been changed: $(changed_variables))),)
)
endef

如果有改变就会报错的,编译出错如下:

 build/core/main.mk:528: *** The following variables have been changed: PRODUCT_COPY_FILES 

使用 PRODUCT_COPY_FILES 应该算是最常用的Copy File 的方法了,一般可直接加在 device.mk 中!

 

 

 

二 .copy_to copy_from ALL_PREBUILT:

这个方法用在Android.mk中,可参考 /system/core/rootdir/Android.mk

 

copy_from += etc/init.goldfish.sh
  
copy_to := $(addprefix $(TARGET_OUT)/,$(copy_from))
copy_from := $(addprefix $(LOCAL_PATH)/,$(copy_from))
  
$(copy_to) : PRIVATE_MODULE := system_etcdir
$(copy_to) : $(TARGET_OUT)/% : $(LOCAL_PATH)/% | $(ACP)
     $(transform-prebuilt-to-target)
  
ALL_PREBUILT += $(copy_to)


 

可以看到copy_from 就是需要copy的,copy_to 就是目的地!

 

可以看下规则,定义在/build/core/definitions.mk中:

 

# Copy a prebuilt file to a target location.
define transform-prebuilt-to-target
@echo $( if $(PRIVATE_IS_HOST_MODULE),host,target) Prebuilt: $(PRIVATE_MODULE) ($@)
$(copy-file-to-target)
endef


 

define copy-file-to-target
@mkdir -p $(dir $@)
$(hide) $(ACP) -fp $< $@
endef


 

需要注意的是,如果我们自己添加一些文件到 copy_from中,就会出现 :

 

 

build/core/main.mk: 533 : *** Some files have been added to ALL_PREBUILT.
build/core/main.mk: 534 : *
build/core/main.mk: 535 : * ALL_PREBUILT is a deprecated mechanism that
build/core/main.mk: 536 : * should not be used for new files.
build/core/main.mk: 537 : * As an alternative, use PRODUCT_COPY_FILES in
build/core/main.mk: 538 : * the appropriate product definition.
build/core/main.mk: 539 : * build/target/product/core.mk is the product
build/core/main.mk: 540 : * definition used in all products.
build/core/main.mk: 541 : *
build/core/main.mk: 542 : * unexpected usb_modeswitch.h in ALL_PREBUILT
build/core/main.mk: 542 : * unexpected usb_modeswitch.sh in ALL_PREBUILT
build/core/main.mk: 542 : * unexpected usb_modeswitch.tcl in ALL_PREBUILT
build/core/main.mk: 543 : *
build/core/main.mk: 544 : *** ALL_PREBUILT contains unexpected files。 停止。

错误发生在 /build/core/main.mk

 

 

include $(BUILD_SYSTEM)/legacy_prebuilts.mk
ifneq ($(filter-out $(GRANDFATHERED_ALL_PREBUILT),$(strip $(notdir $(ALL_PREBUILT)))),)
   $(warning *** Some files have been added to ALL_PREBUILT.)
   $(warning *)
   $(warning * ALL_PREBUILT is a deprecated mechanism that)
   $(warning * should not be used for new files.)
   $(warning * As an alternative, use PRODUCT_COPY_FILES in)
   $(warning * the appropriate product definition.)
   $(warning * build/target/product/core.mk is the product)
   $(warning * definition used in all products.)
   $(warning *)
   $(foreach bad_prebuilt,$(filter-out $(GRANDFATHERED_ALL_PREBUILT),$(strip $(notdir $(ALL_PREBUILT)))),$(warning * unexpected $(bad_prebuilt) in ALL_PREBUILT))
   $(warning *)
   $(error ALL_PREBUILT contains unexpected files)
endif
 

上面的打印信息告诉我们 ALL_PREBUILT 是一种过时的机制,已经不让用于copy新的文件了,推荐使用PRODUCT_COPY_FILES !

 

而可以添加进 ALL_PREBUILT 变量的成员定义在 legacy_prebuilts.mk中:

# This is the list of modules grandfathered to use ALL_PREBUILT
  
# DO NOT ADD ANY NEW MODULE TO THIS FILE
#
# ALL_PREBUILT modules are hard to control and audit and we don't want
# to add any new such module in the system
  
GRANDFATHERED_ALL_PREBUILT := 
     akmd2 
     am 
     ap_gain.bin 
  
...

 

注释写的很明白了!

所以如果我们自己想加个文件编译copy ,就不能使用 copy_from copy_to ALL_prebuilt 这种机制 !

 

 

 

三 .BUILD_PREBUILT :

这种方式把文件当成编译项目,在Android.mk中copy一个file:

LOCAL_PATH := $(call my-dir)
  
include $(CLEAR_VARS) 
LOCAL_MODULE := usb_modeswitch.conf 
LOCAL_MODULE_CLASS := ETC  
LOCAL_MODULE_PATH := $(TARGET_OUT)/etc 
LOCAL_SRC_FILES :=$(LOCAL_MODULE)  
include $(BUILD_PREBUILT)

上面的就是copy usb_modeswitch.conf 文件到 OUT 下面的 etc目录,这个目录常用来存放配置相关文件。

上面所有的都说的是Copy File 但是如果需要 Copy 一个文件目录下所有就需要另做操作了!

 

 

 

四 .Copy Directory

以我前面的博客 Android——4.2 - 3G移植之路之usb-modeswitch (二) 中copy usb-modewitch.d 数据目录 为例.

在那里的Android.mk 中我使用了shell命令Copy:

 

?
1
$(shell cp -rf $(LOCAL_PATH)/usb_modeswitch.d $(TARGET_OUT)/etc/usb_modeswitch.d)


这样做在源码已经编译好了的情况下,是没有问题的,因为$(TARGET_OUT)/etc 目录已经存在,但是作为新编译是不会Copy的,

 

所以说在android的编译体系中 还得按照android提供的机制来进行操作,像这种shell取巧,是方便,但不是正途!

我现在的处理:

在上面的shell的地方 include 处理Copy usb-modewitch.d 的mk文件,内容如下:

# $( 1 ): module name
# $( 2 ): source file
# $( 3 ): destination directory
define include-prebuilt-with-destination-directory
include $$(CLEAR_VARS)
LOCAL_MODULE := $( 1 )
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/usb_modeswitch_data.mk
LOCAL_MODULE_STEM := $(notdir $( 2 ))
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $( 3 )
LOCAL_SRC_FILES := $( 2 )
include $$(BUILD_PREBUILT)
endef
  
#== rename 's/:/_/' * ==#
  
  
usb_modeswitch_data := $(notdir $(wildcard $(LOCAL_PATH)/usb_modeswitch.d/*))
  
$(warning jscese display -usb_modeswitch_data--$(usb_modeswitch_data))
  
usb_modeswitch_data_target_directory := $(TARGET_OUT)/etc/usb_modeswitch.d
$(foreach data, $(usb_modeswitch_data), $(eval $(call include-prebuilt-with-destination-directory,target-data-$(notdir $(data)),usb_modeswitch.d/$(data),$(usb_modeswitch_data_target_directory))))
usb_modeswitch_data_target := $(addprefix $(usb_modeswitch_data_target_directory)/,$(foreach cacert,$(usb_modeswitch_data),$(notdir $(usb_modeswitch_data))))
.PHONY: usb_modeswitch_data_target
usb_modeswitch_data: $(usb_modeswitch_data_target)
  
# This is so that build/target/product/core.mk can use usb_modeswitch_data in PRODUCT_PACKAGES
ALL_MODULES.usb_modeswitch_data.INSTALLED := $(usb_modeswitch_data_target)

可以看到原理同 Copy 一个单独的 File是一样的 BUILD_PREBUILT ,只不过是遍历了一下文件夹,而且把这个当成一个module来处理 写进 PRODUCT_PACKAGES

关于PRODUCT_PACKAGES 变量的作用可参考我之前的博文 Android——编译安装Module的控制因素

 

另外因为文件名称都是 XXXX:XXXX类型,我这里先在直接使用reanme命令把:全替换成 _

如果不替换,导致传入文件的时候 makefile 把中间的 : 可能当成了依赖符号~ 会报错

我如果使用subst 把:换成 : 添加进去转义符号,好像是文件又找不到,不知道有没有遇到过这种情况.....

<think>我们正在处理一个关于Android编译系统的问题,具体是如何在.mk文件中实现复制文件的功能。根据用户需求,我们需要参考站内引用,并结合Android编译系统的知识来解答。 根据引用[1]和[2]我们知道,Android.mk文件是向编译系统描述如何编译源代码的,它本质上是一个Makefile。在Android编译系统中,我们可以通过定义特定的规则来实现文件复制。 在Android.mk中,复制文件通常使用`$(shell cp ...)`或者定义自定义目标(custom target)和命令(command)来实现。但是,更推荐使用Android编译系统提供的预定义函数和变量,例如使用`LOCAL_PREBUILT`模块类型,或者使用`$(copy-file-to-target)`等函数。 然而,在Android.mk中,我们也可以使用`LOCAL_POST_INSTALL_CMD`来在模块安装后执行一些命令,包括复制文件。另外,对于简单的复制,我们还可以使用`$(shell)`函数在Makefile解析阶段执行复制,但要注意这会在解析阶段立即执行,而不是在构建阶段。 另外,在Android编译系统中,还有一个常用的方法是使用`PRODUCT_COPY_FILES`(在设备配置的makefile中,如device.mk)来复制文件,但这个通常不在Android.mk中使用,而是在产品配置文件中使用。 不过,用户明确要求在mk文件(Android.mk)中实现复制文件。因此,我们可以考虑以下方法: 1. 使用`$(shell)`函数:在Android.mk中直接使用shell命令复制文件。但是这种方法会在解析mk文件时立即执行,而不是在构建过程中执行。因此,如果复制的源文件还没有被构建出来,可能会导致复制失败。 2. 使用自定义目标:定义一个自定义目标,并添加命令来实现复制。这样可以确保复制操作在构建过程中执行,并且可以指定依赖关系。 3. 使用`LOCAL_POST_INSTALL_CMD`:如果复制操作与某个模块(如一个可执行文件或库)相关,可以在该模块的构建后执行复制命令。 下面我们分别举例说明: 方法一:使用`$(shell)`函数(不推荐,因为立即执行) ```makefile # 在Android.mk的任意位置 $(shell cp $(LOCAL_PATH)/file_to_copy $(TARGET_OUT)/destination) ``` 方法二:使用自定义目标(推荐) 我们可以在Android.mk中定义一个自定义目标,然后使用`cp`命令复制文件。同时,我们可以设置依赖关系,例如在某个模块构建完成后执行复制。 ```makefile include $(CLEAR_VARS) LOCAL_MODULE := my_copy_target # 假设我们要复制的文件是prebuilt_file,我们可以将它声明为一个预编译模块 LOCAL_SRC_FILES := file_to_copy LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT)/destination include $(BUILD_PREBUILT) # 或者,我们也可以定义一个自定义目标,不依赖于预编译模块 include $(CLEAR_VARS) LOCAL_MODULE := my_custom_copy # 我们添加自定义命令 MY_SRC_FILE := $(LOCAL_PATH)/file_to_copy MY_DEST_FILE := $(TARGET_OUT)/destination/file_name # 定义自定义命令 $(MY_DEST_FILE): $(MY_SRC_FILE) $(hide) cp $< $@ # 将这个目标加入到ALL_MODULES中 ALL_MODULES.$(LOCAL_MODULE).INSTALLED := $(MY_DEST_FILE) include $(BUILD_PHONY_PACKAGE) ``` 但是,上面的自定义目标方法比较复杂。在Android.mk中,更简单的方法是使用`BUILD_PREBUILT`来复制单个文件,或者使用`BUILD_MULTI_PREBUILT`复制多个文件。对于单个文件,我们可以这样: ```makefile include $(CLEAR_VARS) LOCAL_MODULE := my_file LOCAL_SRC_FILES := file_to_copy LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/my_directory # 目标目录 include $(BUILD_PREBUILT) ``` 这样,当构建`my_file`模块时,文件会被复制到`$(TARGET_OUT_ETC)/my_directory`目录下。 方法三:使用`LOCAL_POST_INSTALL_CMD` 如果我们有一个已经存在的模块(比如一个可执行文件),我们可以在该模块的构建后添加复制命令。 ```makefile include $(CLEAR_VARS) LOCAL_MODULE := my_binary LOCAL_SRC_FILES := main.c # ... 其他变量设置 # 在安装该模块后,执行复制命令 LOCAL_POST_INSTALL_CMD := $(hide) cp $(LOCAL_PATH)/file_to_copy $(TARGET_OUT)/destination include $(BUILD_EXECUTABLE) ``` 综上所述,最常用的方法是使用`BUILD_PREBUILT`来复制文件,因为它简单且符合Android编译系统的模块化思想。当然,也可以根据具体情况选择其他方法。 注意:以上示例中的`$(TARGET_OUT)`等变量是Android编译系统定义的,代表不同的目录,如`TARGET_OUT`通常是`/system`,`TARGET_OUT_ETC`是`/system/etc`等。 因此,针对用户的问题,我们给出两种常用方法: 方法A:使用预构建模块(推荐) ```makefile include $(CLEAR_VARS) LOCAL_MODULE := my_file_copy LOCAL_SRC_FILES := source_file LOCAL_MODULE_CLASS := ETC LOCAL_MODULE_PATH := $(TARGET_OUT)/destination_directory include $(BUILD_PREBUILT) ``` 方法B:在已有模块中使用`LOCAL_POST_INSTALL_CMD` ```makefile include $(CLEAR_VARS) LOCAL_MODULE := some_existing_module ... # 原有模块定义 LOCAL_POST_INSTALL_CMD := $(hide) cp $(LOCAL_PATH)/source_file $(TARGET_OUT)/destination_directory include $(BUILD_EXECUTABLE) # 或 BUILD_SHARED_LIBRARY 等 ``` 请根据实际需求选择适合的方法。 参考文献: [^1]: Android编译脚本(.mk文件)解析 [^2]: Android.mk编译c语言 [^3]: Android编译系统——.mk文件分析(六) §§相关问题§§ 1. 如何在Android.mk中定义多个文件复制? 2. Android编译系统中TARGET_OUT变量具体指代什么路径? 3. 除了复制文件,Android.mk还支持哪些文件操作? </think>在Android编译系统中,通过`.mk`文件实现文件复制主要有以下三种方法,这些方法均基于Makefile语法实现: ### 方法一:使用`PRODUCT_COPY_FILES`(推荐) 在设备级配置文件(如`device.mk`)中使用此全局变量,系统会在编译阶段自动处理复制操作: ```makefile PRODUCT_COPY_FILES += \ $(LOCAL_PATH)/src/file.txt:$(TARGET_COPY_OUT_SYSTEM)/dest/file.txt ``` - **说明**: - `LOCAL_PATH`:当前文件目录路径 - `TARGET_COPY_OUT_SYSTEM`:目标分区路径(如`/system`),可选其他分区如`TARGET_COPY_OUT_VENDOR` - 冒号分隔源文件和目标路径 - **优势**:编译系统自动处理依赖关系和执行时机[^1][^2] ### 方法二:自定义模块使用`BUILD_PREBUILT` 在`Android.mk`中创建预编译模块实现精细控制: ```makefile include $(CLEAR_VARS) LOCAL_MODULE := my_file_copy LOCAL_MODULE_CLASS := ETC # 文件类型(ETC表示系统配置) LOCAL_MODULE_PATH := $(TARGET_OUT_SYSTEM)/etc # 目标路径 LOCAL_SRC_FILES := src_file.conf # 源文件(相对路径) include $(BUILD_PREBUILT) ``` - **关键参数**: - `LOCAL_MODULE_PATH`:定义目标绝对路径 - `BUILD_PREBUILT`:声明为预构建模块 - **适用场景**:需精确控制文件属性的复制操作[^2] ### 方法三:Shell命令直接复制 使用`$(shell)`执行复制命令(需谨慎使用): ```makefile $(shell cp -f $(LOCAL_PATH)/src/*.so $(TARGET_OUT_SHARED_LIBRARIES)) ``` - **注意**: - 在Makefile解析阶段**立即执行**(非构建阶段) - 可能导致时序问题(如源文件未生成时失败) - 仅建议用于静态文件的复制[^3] --- ### 关键路径变量 | 变量名 | 对应路径 | |-------------------------|----------------------| | `TARGET_COPY_OUT_SYSTEM` | `/system` | | `TARGET_OUT_SYSTEM` | `/system` | | `TARGET_OUT_VENDOR` | `/vendor` | | `TARGET_OUT_PRODUCT` | `/product` | | `TARGET_OUT_ODM` | `/odm` | > **最佳实践**:优先使用`PRODUCT_COPY_FILES`,需要精细控制时用预编译模块,避免直接`shell cp`[^1][^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值