Android——編譯體系中的 Copy

對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 :[:].
#  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 : 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 :=

 

這就是 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:

 

$(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 把:換成 : 添加進去轉義符號,好像是文件又找不到,不知道有沒有遇到過這種情況…..

 

 

 

 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *