觉得这个网文写得不错,英文的。虽然作者实验的版本有点低,但应该还是很有参考价值的。 http://my.safaribooksonline.com/book/programming/android/9781449327958/4dot-the-build-system/i_sect14_id404209_html
将原文拷贝如下。
Basic AOSP Hacks
You most likely bought this book with one thing in mind: to hack the AOSP to fit your needs. Over the next few pages, we’ll start looking into some of the most obvious hacks you’ll likely want to try. Of course we’re only setting the stage here with the parts that pertain to the build system, which is where you’ll likely want to start anyway.
NOTE
While the following explanations are based on 2.3/Gingerbread, they’ll work just the same on 4.2/Jelly Bean, and likely many versions after that one, too. The fact is, these mechanisms have been constant for quite some time. Still, where relevant, changes in 4.2/Jelly Bean are highlighted.
Adding a Device
Adding a custom device is most likely one of the topmost items (if not the topmost) on your list of reasons for reading this book. I’m about to show you how to do just that, so you’ll likely want to bookmark this section. Of course I’m actually only showing you the build aspects of the work. There are a lot more steps involved in porting Android to new hardware. Still, adding the new device to the build system will definitely be one of the first things you do. Fortunately, doing that is relatively straightforward.
For the purposes of the current exercise, assume you work for a company called ACME and that you’re tasked with delivering its latest gizmo: the CoyotePad, intended to be the best platform for playing all bird games. Let’s get started by creating an entry for our new device in device/:
$cd ~/android/aosp-2.3.x
$. build/envsetup.sh
$mkdir -p device/acme/coyotepad
$cd device/acme/coyotepad
The first thing we’ll need in here is an AndroidProducts.mk file to describe the various AOSP products that could be built for the CoyotePad:
PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/full_coyotepad.mk
While we could describe several products (see build/target/product/AndroidProducts.mk for an example), the typical case is to specify just one, as in this case, and it’s described in full_coyotepad.mk:
$(call inherit-product, $(SRC_TARGET_DIR)/product/languages_full.mk) # If you're using 4.2/Jelly Bean, use full_base.mk instead of full.mk $(call inherit-product, $(SRC_TARGET_DIR)/product/full.mk) DEVICE_PACKAGE_OVERLAYS := PRODUCT_PACKAGES += PRODUCT_COPY_FILES += PRODUCT_NAME := full_coyotepad PRODUCT_DEVICE := coyotepad PRODUCT_MODEL := Full Android on CoyotePad, meep-meep
It’s worth taking a closer look at this makefile. First, we’re using the inherit-product
function to tell the build system to pull in other product descriptions as the basis of ours. This allows us to build on other people’s work and not have to specify from scratch every bit and piece of the AOSP that we’d like to include.languages_full.mk will pull in a vast number of locales, and full.mk will make sure we get the same set of modules as if we had built using the full-eng
combo.
With regard to the other variables:
-
Allows us to specify a directory that will form the basis of an overlay that will be applied onto the AOSP’s sources, thereby allowing us to substitute default package resources with device-specific resources. You’ll find this useful if you’d like to set custom layouts or colors for Launcher2 or other apps, for instance. We’ll look at how to use this in the next section.
-
Allows us to specify packages we’d like to have this product include in addition to those specified in the products we’re already inheriting from. If you have custom apps, binaries, or libraries located within device/acme/coyotepad/, for instance, you’ll want to add them here so that they are included in the final images generated. Notice the use of the
+=
sign. It allows us to append to the existing values in the variable instead of substituting its content. -
Allows us to list specific files we’d like to see copied to the target’s filesystem and the location where they need to be copied. Each source/destination pair is colon-separated, and pairs are space-separated among themselves. This is useful for configuration files and prebuilt binaries such as firmware images or kernel modules.
-
The
TARGET_PRODUCT
, which you can set either by selecting a lunch combo or passing it as part of the combo parameter to lunch, as in:$
lunch full_coyotepad-eng
-
The name of the actual finished product shipped to the customer.
TARGET_DEVICE
derives from this variable.PRODUCT_DEVICE
has to match an entry in device/acme/, since that’s where the build looks for the corresponding BoardConfig.mk. In this case, the variable is the same as the name of the directory we’re already in. -
The name of this product as provided in the “Model number” in the “About the phone” section of the settings. This variable actually gets stored as the
ro.product.model
global property accessible on the device.
DEVICE_PACKAGE_OVERLAYS
PRODUCT_PACKAGES
PRODUCT_COPY_FILES
PRODUCT_NAME
PRODUCT_DEVICE
PRODUCT_MODEL
Version 4.2/Jelly Bean also includes a PRODUCT_BRAND
that is typically set to Android. The value of this variable is then available as the ro.product.brand
global property. The latter is used by some parts of the stack that take action based on the device’s vendor.
Now that we’ve described the product, we must also provide some information regarding the board the device is using through a BoardConfig.mk file:
TARGET_NO_KERNEL := true TARGET_NO_BOOTLOADER := true TARGET_CPU_ABI := armeabi BOARD_USES_GENERIC_AUDIO := true USE_CAMERA_STUB := true
This is a very skinny BoardConfig.mk and ensures that we actually build successfully. For a real-life version of that file, have a look at device/samsung/crespo/BoardConfigCommon.mk in 2.3/Gingerbread, and also at device/asus/grouper/BoardConfigCommon.mk in 4.2/Jelly Bean.
You’ll also need to provide a conventional Android.mk in order to build all the modules that you might have included in this device’s directory:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) ifneq ($(filter coyotepad,$(TARGET_DEVICE)),) include $(call all-makefiles-under,$(LOCAL_PATH)) endif
It’s in fact the preferred modus operandi to put all device-specific apps, binaries, and libraries within the device’s directory instead of globally within the rest of the AOSP. If you do add modules here, don’t forget to also add them to PRODUCT_PACKAGES
as I explained earlier. If you just put them here and provide them validAndroid.mk files, they’ll build, but they won’t be in the final images.
If you have several products sharing the same set of packages, you may want to create a device/acme/common/ directory containing the shared packages. You can see an example of this in 4.2/Jelly Bean’s device/generic/ directory. In that same version, you can also check how device/samsung/maguro/device.mk inherits fromdevice/samsung/tuna/device.mk for an example of how one device can be based on another device.
Lastly, let’s close the loop by making the device we just added visible to envsetup.sh and lunch. To do so, you’ll need to add a vendorsetup.sh in your device’s directory:
add_lunch_combo full_coyotepad-eng
You also need to make sure that it’s executable if it’s to be operational:
$ chmod 755 vendorsetup.sh
We can now go back to the AOSP’s root and take our brand-new ACME CoyotePad for a runchase:
$croot
$. build/envsetup.sh
$lunch
You're building on Linux Lunch menu... pick a combo: 1. generic-eng 2. simulator 3. full_coyotepad-eng 4. full_passion-userdebug 5. full_crespo4g-userdebug 6. full_crespo-userdebug Which would you like? [generic-eng]3
============================================ PLATFORM_VERSION_CODENAME=REL PLATFORM_VERSION=2.3.4 TARGET_PRODUCT=full_coyotepad TARGET_BUILD_VARIANT=eng TARGET_SIMULATOR=false TARGET_BUILD_TYPE=release TARGET_BUILD_APPS= TARGET_ARCH=arm HOST_ARCH=x86 HOST_OS=linux HOST_BUILD_TYPE=release BUILD_ID=GINGERBREAD ============================================ $make -j16
As you can see, the AOSP now recognizes our new device and prints the information correspondingly. When the build is done, we’ll also have the same type of output provided in any other AOSP build, except that it will be a product-specific directory:
$ ls -al out/target/product/coyotepad/
total 89356
drwxr-xr-x 7 karim karim 4096 2011-09-21 19:20 .
drwxr-xr-x 4 karim karim 4096 2011-09-21 19:08 ..
-rw-r--r-- 1 karim karim 7 2011-09-21 19:10 android-info.txt
-rw-r--r-- 1 karim karim 4021 2011-09-21 19:41 clean_steps.mk
drwxr-xr-x 3 karim karim 4096 2011-09-21 19:11 data
-rw-r--r-- 1 karim karim 20366 2011-09-21 19:20 installed-files.txt
drwxr-xr-x 14 karim karim 4096 2011-09-21 19:20 obj
-rw-r--r-- 1 karim karim 327 2011-09-21 19:41 previous_build_config.mk
-rw-r--r-- 1 karim karim 2649750 2011-09-21 19:43 ramdisk.img
drwxr-xr-x 11 karim karim 4096 2011-09-21 19:43 root
drwxr-xr-x 5 karim karim 4096 2011-09-21 19:19 symbols
drwxr-xr-x 12 karim karim 4096 2011-09-21 19:19 system
-rw------- 1 karim karim 87280512 2011-09-21 19:20 system.img
-rw------- 1 karim karim 1505856 2011-09-21 19:14 userdata.img
Also, have a look at the build.prop file in system/. It contains various global properties that will be available at runtime on the target and that relate to our configuration and build:
# begin build properties # autogenerated by buildinfo.sh ro.build.id=GINGERBREAD ro.build.display.id=full_coyotepad-eng 2.3.4 GINGERBREAD eng.karim.20110921.1908 49 test-keys ro.build.version.incremental=eng.karim.20110921.190849 ro.build.version.sdk=10 ro.build.version.codename=REL ro.build.version.release=2.3.4 ro.build.date=Wed Sep 21 19:10:04 EDT 2011 ro.build.date.utc=1316646604 ro.build.type=eng ro.build.user=karim ro.build.host=w520 ro.build.tags=test-keys ro.product.model=Full Android on CoyotePad, meep-meep ro.product.brand=generic ro.product.name=full_coyotepad ro.product.device=coyotepad ro.product.board= ro.product.cpu.abi=armeabi ro.product.manufacturer=unknown ro.product.locale.language=en ro.product.locale.region=US ro.wifi.channels= ro.board.platform= # ro.build.product is obsolete; use ro.product.device ro.build.product=coyotepad # Do not try to parse ro.build.description or .fingerprint ro.build.description=full_coyotepad-eng 2.3.4 GINGERBREAD eng.karim.20110921.190 849 test-keys ro.build.fingerprint=generic/full_coyotepad/coyotepad:2.3.4/GINGERBREAD/eng.kari m.20110921.190849:eng/test-keys # end build properties ...
WARNING
You may want to carefully vet the default properties before using the build on a real device. Some developers have encountered some severe issues due to default values. In both 2.3/Gingerbread and 4.2/Jelly Bean, for instance, ro.com.android.dataroaming
is set to true
in some builds. Hence, if you’re doing development on a device connected to a live cell network, changing the value to false
might save you some money.
As you can imagine, there’s a lot more to be done here to make sure the AOSP runs on our hardware. But the preceding steps give us the starting point. However, by isolating the board-specific changes in a single directory, this configuration will simplify adding support for the CoyotePad to the next version of the AOSP that gets released. Indeed, it’ll just be a matter of copying the corresponding directory to the new AOSP’s device/ directory and adjusting the code therein to use the new APIs.
Adding an App
Adding an app to your board is relatively straightforward. As a starter, try creating a HelloWorld! app with Eclipse and the default SDK; all new Android projects in Eclipse are a HelloWorld! by default. Then copy that app from the Eclipse workspace to its destination:
$ cp -a ~/workspace/HelloWorld ~/android/aosp-2.3.x/device/acme/coyotepad/
You’ll then have to create an Android.mk file in aosp-root
/device/acme/coyotepad/HelloWorld/ to build that app:
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := HelloWorld include $(BUILD_PACKAGE)
Given that we’re tagging this module as optional
, it won’t be included by default in the AOSP build. To include it, you’ll need to add it to the PRODUCT_PACKAGES
listed in the CoyotePad’s full_coyotepad.mk.
If, instead of adding your app for your board only, you would like to add a default app globally to all products generated by the AOSP alongside the existing stock apps, you’ll need to put it in packages/apps/ instead of your board’s directory. You’ll also need to modify one of the built-in .mk files, such as aosp-root/build/target/product/core.mk, to have your app built by default. This is not recommended, though, as it’s not very portable since it will require you to make this modification to every new AOSP release. As I stated earlier, it’s best to keep your custom modifications in device/acme/coyotepad/ in as much as possible.
Adding an App Overlay
Sometimes you don’t actually want to add an app but would rather modify existing ones included by default in the AOSP. That’s what app overlays are for. Overlays are a mechanism included in the AOSP to allow device manufacturers to change the resources provided (such as for apps), without actually modifying the original resources included in the AOSP. To use this capability, you must create an overlay tree and tell the build system about it. The easiest location for an overlay is within a device-specific directory such as the one we created in the previous section:
$cd device/acme/coyotepad/
$mkdir overlay
To tell the build system to take this overlay into account, we need to modify our full_coyotepad.mk such that:
DEVICE_PACKAGE_OVERLAYS := device/acme/coyotepad/overlay
At this point, though, our overlay isn’t doing much. Let’s say we want to modify some of Launcher2’s default strings. We could then do something like this:
$mkdir -p overlay/packages/apps/Launcher2/res/values
$cp
>aosp-root
/packages/apps/Launcher2/res/values/strings.xml \overlay/packages/apps/Launcher2/res/values/
You can then trim your local strings.xml to override only those strings that you need. Most importantly, your device will have a Launcher2 that has your custom strings, but the default Launcher2 will still have its original strings. So if someone relies on the same AOSP sources you’re using to build for another product, they’ll still get the original strings. You can, of course, replace most resources like this, including images and XML files. So long as you put the files in the same hierarchy as they are found in the AOSP but within device/acme/coyotepad/overlay/, they’ll be taken into account by the build system.
WARNING
Overlays can be used only for resources. You can’t overlay source code. If you want to customize parts of Android’s internals, for instance, you’ll still have to make those modifications in situ. There’s no way, currently at least, to isolate those changes to your board.
Adding a Native Tool or Daemon
Like the example above of adding an app for your board, you can add your custom native tools and daemons as subdirectories of device/acme/coyotepad/. Obviously, you’ll need to provide an Android.mk in the directory containing the code to build that module:
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello-world LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := hello-world.cpp LOCAL_SHARED_LIBRARIES := liblog include $(BUILD_EXECUTABLE)
As in the app’s case, you’ll also need to make sure hello-world
is part of the CoyotePad’s PRODUCT_PACKAGES
.
If you intend to add your binary globally for all product builds instead of just locally to your board, you need to know that there are a number of locations in the tree where native tools and daemons are located. Here are the most important ones:
-
system/core/ and system/
-
Custom Android binaries that are meant to be used outside the Android Framework or are standalone pieces.
frameworks/base/cmds/
-
Binaries that are tightly coupled to the Android Framework. This is where the Service Manager and installd are found, for example.
external/
-
Binaries that are generated by an external project that is imported into the AOSP. strace, for instance, is here.
Having identified from the list above where the code generating your binary should go, you’ll also need to add it as part of one of the global .mk files such as aosp-root/build/target/product/core.mk. As I said above, however, such global additions are not recommended since they can’t be transferred as easily to newer AOSP versions.
Adding a Native Library
Like apps and binaries, you can also add native libraries for your board. Assuming, as above, that the sources to build the library are in a subdirectory of device/acme/coyotepad/, you’ll need an Android.mk to build your library:
LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := libmylib LOCAL_MODULE_TAGS := optional LOCAL_PRELINK_MODULE := false LOCAL_SRC_FILES := $(call all-c-files-under,.) include $(BUILD_SHARED_LIBRARY)
NOTE
Note that LOCAL_PRELINK_MODULE
has been removed and is no longer necessary as of 4.0/Ice-Cream Sandwich.
To use this library, you must add it to the libraries listed by the Android.mk file of whichever binaries depend on it:
LOCAL_SHARED_LIBRARIES := libmylib
You’ll also likely need to add relevant headers to an include/ directory located in about the same location as you put your library, so that the code that needs to link against your library can find those headers, such as device/acme/coyotepad/include/.
Should you want to make your library apply globally to all AOSP builds, not just your device, you’ll need a little bit more information regarding the various locations where libraries are typically found in the tree. First, you should know that, unlike binaries, a lot of libraries are used within a single module but nowhere else. Hence, these libraries will typically be placed within that module’s code and not in the usual locations where libraries used systemwide are found. The latter are typically in the following locations:
-
system/core/
-
Libraries used by many parts of the system, including some outside the Android Framework. This is where liblog is, for instance.
frameworks/base/libs/
-
Libraries intimately tied to the framework. This is where libbinder is.
frameworks/native/libs/
-
In 4.2/Jelly Bean, many libraries that were in frameworks/base/libs/ in 2.3/Gingerbread have been moved out and into frameworks/native/libs/.
external/
-
Libraries generated by external projects imported into the AOSP. OpenSSL’s libssl is here.
Similarly, instead of using a CoyotePad-specific include directory, you’d use a global directory such as system/core/include/ or frameworks/base/include/ or, in 4.2/Jelly Bean, frameworks/base/include/. Again, as stated earlier, you should carefully review whether such global additions are truly required, as they’ll represent additional work when you try to port for your device to the next version of Android.
[18] If you do not provide a value, defaults will be used. For instance, all apps are set to optional
by default. Also, some modules are part of GRANDFATHERED_USER_MODULES
in user_tags.mk. No LOCAL_MODULE_TAGS
need be specified for those; they’re always included.
[19] This file contains a set list of variables starting with the string LOCAL_
. If a variable isn’t specifically listed in this file, it won’t be taken into account by CLEAR_VARS
.
[20] This version is cleaned up a little (removed commented code, for instance) and slightly reformatted.
[21] Also slightly modified to remove white space and comments.
[22] This assumes you had already run envsetup.sh and lunch.