zoukankan      html  css  js  c++  java
  • Android编译命令

    说在前面

    从最开始接触Android系统开始,每次进行代码编译都需要网上搜索编译指令。后来大致熟悉了Android的编译体系,加深了对Android编译的理解。

    编译流程

    编译 android 系统的流程,首先执行 source build/envsetup.sh,然后执行 lunch 选择板级配置,最后执行 make 编译

    • source build/envsetup.sh 流程

    脚本中的第一个函数是hmm,介绍了脚本的一些功能,cat <<EOF 表示把 EOF 中的内容当做文件打印到标准输出,包含了脚本的使用说明和功能介绍。

    function hmm() {
    cat <<EOF
    Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
    - lunch:     lunch <product_name>-<build_variant>
    - tapas:     tapas [<App1> <App2> ...] [arm|x86|mips|armv5|arm64|x86_64|mips64] [eng|userdebug|user]
    - croot:     Changes directory to the top of the tree.
    - m:         Makes from the top of the tree.
    - mm:        Builds all of the modules in the current directory, but not their dependencies.
    - mmm:       Builds all of the modules in the supplied directories, but not their dependencies.
                 To limit the modules being built use the syntax: mmm dir/:target1,target2.
    - mma:       Builds all of the modules in the current directory, and their dependencies.
    - mmma:      Builds all of the modules in the supplied directories, and their dependencies.
    - provision: Flash device with all required partitions. Options will be passed on to fastboot.
    - cgrep:     Greps on all local C/C++ files.
    - ggrep:     Greps on all local Gradle files.
    - jgrep:     Greps on all local Java files.
    - resgrep:   Greps on all local res/*.xml files.
    - mangrep:   Greps on all local AndroidManifest.xml files.
    - mgrep:     Greps on all local Makefiles files.
    - sepgrep:   Greps on all local sepolicy files.
    - sgrep:     Greps on all local source files.
    - godir:     Go to the directory containing a file.
    
    Environment options:
    - SANITIZE_HOST: Set to 'true' to use ASAN for all host modules. Note that
                     ASAN_OPTIONS=detect_leaks=0 will be set by default until the
                     build is leak-check clean.
    
    Look at the source to view more functions. The complete list is:
    EOF
        local T=$(gettop)
        local A=""
        local i
        for i in `cat $T/build/envsetup.sh | sed -n "/^[[:blank:]]*function /s/function ([a-z_]*).*/1/p" | sort | uniq`; do
          A="$A $i"
        done
        echo $A
    }
    

    函数只有在调用时才会执行,在执行 source build/envsetup.sh 时该函数并不会被调用,而只有在终端运行 hmm 时才会输出,输出使用说明和脚本中所有函数名称,但显然不够准确。

    [android_8.1]$ source build/envsetup.sh
    including device/rockchip/rk3399/vendorsetup.sh
    including sdk/bash_completion/adb.bash
    [android_8.1]$ hmm
    Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
    - lunch:     lunch <product_name>-<build_variant>
    - tapas:     tapas [<App1> <App2> ...] [arm|x86|mips|armv5|arm64|x86_64|mips64] [eng|userdebug|user]
    - croot:     Changes directory to the top of the tree.
    - m:         Makes from the top of the tree.
    - mm:        Builds all of the modules in the current directory, but not their dependencies.
    - mmm:       Builds all of the modules in the supplied directories, but not their dependencies.
                 To limit the modules being built use the syntax: mmm dir/:target1,target2.
    - mma:       Builds all of the modules in the current directory, and their dependencies.
    - mmma:      Builds all of the modules in the supplied directories, and their dependencies.
    - provision: Flash device with all required partitions. Options will be passed on to fastboot.
    - cgrep:     Greps on all local C/C++ files.
    - ggrep:     Greps on all local Gradle files.
    - jgrep:     Greps on all local Java files.
    - resgrep:   Greps on all local res/*.xml files.
    - mangrep:   Greps on all local AndroidManifest.xml files.
    - mgrep:     Greps on all local Makefiles files.
    - sepgrep:   Greps on all local sepolicy files.
    - sgrep:     Greps on all local source files.
    - godir:     Go to the directory containing a file.
    
    Environment options:
    - SANITIZE_HOST: Set to 'true' to use ASAN for all host modules. Note that
                     ASAN_OPTIONS=detect_leaks=0 will be set by default until the
                     build is leak-check clean.
    
    Look at the source to view more functions. The complete list is:
    addcompletions add_lunch_combo build_build_var_cache cgrep check_product check_variant choosecombo chooseproduct choosetype choosevariant core coredump_enable coredump_setup cproj croot destroy_build_var_cache findmakefile get_abs_build_var getbugreports get_build_var getdriver getlastscreenshot get_make_command getprebuilt getscreenshotpath getsdcardpath gettargetarch gettop ggrep godir hmm is isviewserverstarted jgrep key_back key_home key_menu lunch _lunch m make mangrep mgrep mm mma mmm mmma pez pid printconfig print_lunch_menu provision qpid rcgrep resgrep runhat runtest sepgrep set_java_home setpaths set_sequence_number set_stuff_for_environment settitle sgrep smoketest stacks startviewserver stopviewserver systemstack tapas tracedmdump treegrep _wrap_build
    

    下面先挑出只在函数外的内容进行分析

    (1)
    VARIANT_CHOICES=(user userdebug eng)
    
    (2)
    unset LUNCH_MENU_CHOICES
    
    (3)
    # add the default one here
    add_lunch_combo aosp_arm-eng
    add_lunch_combo aosp_arm64-eng
    add_lunch_combo aosp_mips-eng
    add_lunch_combo aosp_mips64-eng
    add_lunch_combo aosp_x86-eng
    add_lunch_combo aosp_x86_64-eng
    
    (4)
    # Execute the contents of any vendorsetup.sh files we can find.
    for f in `test -d device && find -L device -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` 
             `test -d vendor && find -L vendor -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort` 
             `test -d product && find -L product -maxdepth 4 -name 'vendorsetup.sh' 2> /dev/null | sort`
    do
        echo "including $f"
        . $f
    done
    unset f
    
    (5) addcompletions
    

    (1) VARIANT_CHOICES 定义为一个数组,包含3种模式可选,在某些脚本函数中会使用到。

    (2) unset LUNCH_MENU_CHOICES 删除给定的环境变量内容,重新进行设置

    (3) 调用 add_lunch_combo 函数, 并传入参数,看下函数的具体实现

    function add_lunch_combo()
    {
        local new_combo=$1
        local c
        for c in ${LUNCH_MENU_CHOICES[@]} ; do
            if [ "$new_combo" = "$c" ] ; then
                return
            fi
        done
        LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
    }
    

    这里对 LUNCH_MENU_CHOICES 数组的内容进行循环,与函数后传入的参数进行对比,如果数组中已经包含传入的参数项,直接返回;否则就把传入的参数加入到数组项中。

    (4)检查是否存在 devicevendorproduct 目录,如果存在的话,就在目录下查找脚本 vendorsetup.sh,并设定了查找深度为4层目录,2> /dev/null 这里将标准错误重定向到/dev/null,也就是不输出,然后把找到的脚本文件进行排序,最后调用找到的脚本文件。当前的脚本文件内容如下:

    including device/rockchip/rk3399/vendorsetup.sh
    
    add_lunch_combo rk3399-userdebug
    add_lunch_combo rk3399-user
    add_lunch_combo rk3399_mid-userdebug
    add_lunch_combo rk3399_mid-user
    add_lunch_combo rk3399pro-userdebug
    add_lunch_combo rk3399pro-user
    add_lunch_combo rk3399_box-userdebug
    add_lunch_combo rk3399_box-user
    

    (5) 查找 sdk/bash_completion 目录下所有以 .bash 结尾的文件,当前仅有一个

    including sdk/bash_completion/adb.bash
    

    前面已经分析过这个函数了,实际上还是把这些板级配置添加到数组中,而通过运行 lunch 命令时进行选择。

    • lunch 流程

    通过执行 lunch 选择合适的板级配置

    function lunch()
    {
        local answer
    
        if [ "$1" ] ; then
            answer=$1
        else
            print_lunch_menu
            echo -n "Which would you like? [aosp_arm-eng] "
            read answer
        fi
    
        local selection=
    
        if [ -z "$answer" ]
        then
            selection=aosp_arm-eng
        elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
        then
            if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
            then
                selection=${LUNCH_MENU_CHOICES[$(($answer-1))]}
            fi
        else
            selection=$answer
        fi
    
        export TARGET_BUILD_APPS=
    
        local product variant_and_version variant version
    
        product=${selection%%-*} # Trim everything after first dash
        variant_and_version=${selection#*-} # Trim everything up to first dash
        if [ "$variant_and_version" != "$selection" ]; then
            variant=${variant_and_version%%-*}
            if [ "$variant" != "$variant_and_version" ]; then
                version=${variant_and_version#*-}
            fi
        fi
        
        if [ -z "$product" ]
        then
            echo
            echo "Invalid lunch combo: $selection"
            return 1
        fi
    
        TARGET_PRODUCT=$product 
        TARGET_BUILD_VARIANT=$variant 
        TARGET_PLATFORM_VERSION=$version 
        build_build_var_cache
        if [ $? -ne 0 ]
        then
            return 1
        fi
    
        export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT)
        export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT)
        if [ -n "$version" ]; then
          export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION)
        else
          unset TARGET_PLATFORM_VERSION
        fi
        export TARGET_BUILD_TYPE=release
    
        echo
    
        set_stuff_for_environment
        printconfig
        destroy_build_var_cache
    }
    

    首先判断有没有传入参数,如果有参数就把值赋给answer变量,否则调用 print_lunch_menu 打印板级配置菜单项,并接受用户终端输入。

    function print_lunch_menu()
    {
        local uname=$(uname)
        echo
        echo "You're building on" $uname
        echo
        echo "Lunch menu... pick a combo:"
    
        local i=1
        local choice
        for choice in ${LUNCH_MENU_CHOICES[@]}
        do
            echo "     $i. $choice"
            i=$(($i+1))
        done
    
        echo
    }
    

    该函数遍历 LUNCH_MENU_CHOICES 数组,输出每一项内容,而这些数组项则是通过调用 add_lunch_combo 函数添加进来的。

    如果传入的参数为零,selection 变量就会赋值为 aosp_arm-eng,否则会输出answer的值,并从该值中搜索以2位数开始的字符串,如果找到的话,再进一步对这个数字进行数组越界的检查。如果这个数字小于 LUNCH_MENU_CHOICES 的数组项,就会把 LUNCH_MENU_CHOICES 的这一项赋给 selection 变量。

    如果 selection 为空,则直接报错退出。

    然后根据获取的数组项,进行字符串截取,从 selection 中取出 - 前面的字符串保存到 product,后面的字符串保存到 variant_and_version,然后再对 variant_and_version 进行字符串截取,保存到 variant

    然后根据以上的数据赋值给 TARGET_PRODUCTTARGET_BUILD_VARIANTTARGET_PLATFORM_VERSION等,然后调用 build_build_var_cache 对编译时必需的环境变量进行赋值和处理。

    最后
    调用 set_stuff_for_environment 函数设置编译环境的变量,包括 ANDROID_BUILD_PATHSJAVA_HOME等。
    调用 printconfig 打印最终准备好的环境变量。参考如下:

    ============================================
    PLATFORM_VERSION_CODENAME=REL
    PLATFORM_VERSION=8.1.0
    TARGET_PRODUCT=rk3399
    TARGET_BUILD_VARIANT=userdebug
    TARGET_BUILD_TYPE=release
    TARGET_PLATFORM_VERSION=OPM1
    TARGET_BUILD_APPS=
    TARGET_ARCH=arm64
    TARGET_ARCH_VARIANT=armv8-a
    TARGET_CPU_VARIANT=cortex-a53
    TARGET_2ND_ARCH=arm
    TARGET_2ND_ARCH_VARIANT=armv7-a-neon
    TARGET_2ND_CPU_VARIANT=cortex-a15
    HOST_ARCH=x86_64
    HOST_2ND_ARCH=x86
    HOST_OS=linux
    HOST_OS_EXTRA=Linux-3.10.0-862.el7.x86_64-x86_64-with-centos-7.5.1804-Core
    HOST_CROSS_OS=windows
    HOST_CROSS_ARCH=x86
    HOST_CROSS_2ND_ARCH=x86_64
    HOST_BUILD_TYPE=release
    BUILD_ID=OPM8.181205.001
    OUT_DIR=out
    AUX_OS_VARIANT_LIST=
    ============================================
    

    调用 destroy_build_var_cache 清除不需要的中间环节产生的变量值。

    • make 流程

    谷歌在 Android 7.0 开始引入了 ninja 进行系统编译,相对于 Makefile 组织编译系统来说,ninja 在大的项目管理的速度和并行方面有突出的优势。但是现有的 Android 还是由 Makefile 组织,所以谷歌引入了 kati soong,将 Android.mk 文件转化成 ninja 文件,使用 ninja 文件对编译系统进行管理。

    但从 Android 8.0 开始,引入了 Android.bp 文件来替代之前的 Android.mk 文件。与 Android.mk 文件不同,Android.bp 只是纯粹的配置文件,不包括分支、循环等流程控制,它使用 blueprint 框架来解析,最终转换成 ninja 文件。

    Android 9.0 则强制使用 Android.bp 来构建编译。

    blueprint 是生成、解析 Android.bp 的工具,是 soong 的一部分。 soong 则是专为 Android 编译而设计的工具,blueprint 只是解析文件的形式,而 soong 则解释内容的含义。

    Android.mk 可以通过 soong 提供的 androidmk 转换成 Android.bp,但仅限简单配置。目前 Android 8.0 的编译流程中,仍然是使用 kati 来做的转换。

    Android.bp --> blueprint --> soong --> ninja
    Makefile && Android.mk --> kati --> ninja
    Android.mk --> soong --> blueprint --> Android.bp
    

    现存的 Android.mkAndroid.bp 都会被转换为 ninjaAndroid.mk 和其他的 Makefile,会生成out/build-<product_name>.ninja文件,而Android.bp则会生成out/soong/build.ninja。此外,还会生成一个较小的out/combined-<product_name>.ninja文件,负责把二者结合起来,作为执行入口。

    当执行 make 的时候,会查找当前目录价的 Makefile 文件并执行,内容如下:

    ### DO NOT EDIT THIS FILE ###
    include build/core/main.mk
    ### DO NOT EDIT THIS FILE ###
    

    所以,真正的 Makefile 入口是 build/core/main.mk

    而我们在执行编译的时候并没有传入目标,那么就一定会有一个默认的目标,在 build/core/main.mk 能够找到如下内容:

    ifndef KATI
    
    host_prebuilts := linux-x86
    ifeq ($(shell uname),Darwin)
    host_prebuilts := darwin-x86
    endif
    
    .PHONY: run_soong_ui
    run_soong_ui:
        +@prebuilts/build-tools/$(host_prebuilts)/bin/makeparallel --ninja build/soong/soong_ui.bash --make-mode $(MAKECMDGOALS)
    
    .PHONY: $(MAKECMDGOALS)
    $(sort $(MAKECMDGOALS)) : run_soong_ui
        @#empty
    
    else # KATI
    

    其中 MAKECMDGOALS 这个命名是 make 执行时后面的参数赋值,也就是我们执行任何 make 的时候都是执行 run_soong_ui 这个目标,这样 androidMakefile 切换为 soong 进行了编译流程,后面跟 make 就没有关系了。

    • mm 流程

    当我们单独编译某个模块时,可以在这个模块目录下输入 mm 命令进行编译,流程和make差不多,只不过目标是单独模块组成,会生成独立的ninja文件。

    function mm()
    {
        local T=$(gettop)
        local DRV=$(getdriver $T)
        # If we're sitting in the root of the build tree, just do a
        # normal build.
        if [ -f build/soong/soong_ui.bash ]; then
            _wrap_build $DRV $T/build/soong/soong_ui.bash --make-mode $@
        else
            # Find the closest Android.mk file.
            local M=$(findmakefile)
            local MODULES=
            local GET_INSTALL_PATH=
            local ARGS=
            # Remove the path to top as the makefilepath needs to be relative
            local M=`echo $M|sed 's:'$T'/::'`
            if [ ! "$T" ]; then
                echo "Couldn't locate the top of the tree.  Try setting TOP."
                return 1
            elif [ ! "$M" ]; then
                echo "Couldn't locate a makefile from the current directory."
                return 1
            else
                local ARG
                for ARG in $@; do
                    case $ARG in
                      GET-INSTALL-PATH) GET_INSTALL_PATH=$ARG;;
                    esac
                done
                if [ -n "$GET_INSTALL_PATH" ]; then
                  MODULES=
                  ARGS=GET-INSTALL-PATH-IN-$(dirname ${M})
                  ARGS=${ARGS////-}
                else
                  MODULES=MODULES-IN-$(dirname ${M})
                  # Convert "/" to "-".
                  MODULES=${MODULES////-}
                  ARGS=$@
                fi
                if [ "1" = "${WITH_TIDY_ONLY}" -o "true" = "${WITH_TIDY_ONLY}" ]; then
                  MODULES=tidy_only
                fi
                ONE_SHOT_MAKEFILE=$M _wrap_build $DRV $T/build/soong/soong_ui.bash --make-mode $MODULES $ARGS
            fi
        fi
    }
    

    整个编译体系的Makefile大致可区分为:系统核心的Makefile,产品级的Makefile,具体模块的Makefile.

    • Android.mk语法

    在源码树中每一个模块的根目录下都有一个Android.mk文件,编译系统正是以模块为单位进行编译的,每个模块有唯一的模块名,一个模块可以依赖多个其他模块,模块之间的依赖关系是通过模块名来引用的(熟悉OpenWRT或者Buildroot编译体系的同学对此应该不会陌生)。

    frameworks/native/cmds/servicemanager/Android.mk 为例,解析下具体的语法格式:

    LOCAL_PATH:= $(call my-dir)	//设置为当前文件夹所在路径
    
    svc_c_flags =   
        -Wall -Wextra -Werror 	//设置svc_c_flags编译选项
    
    ifneq ($(TARGET_USES_64_BIT_BINDER),true)
    ifneq ($(TARGET_IS_64_BIT),true)
    svc_c_flags += -DBINDER_IPC_32BIT=1
    endif
    endif
    
    include $(CLEAR_VARS)	//清空编译环境的变量,可能由其他模块设置过的变量
    LOCAL_SHARED_LIBRARIES := liblog	//当前模块在运行时依赖的动态库
    LOCAL_SRC_FILES := bctest.c binder.c		//当前模块包含的所有源码
    LOCAL_CFLAGS += $(svc_c_flags)	//设置CFLAGS编译选项
    LOCAL_MODULE := bctest	//当前模块的名称,具有唯一性
    LOCAL_MODULE_TAGS := optional	//当前模块的标签,设置为默认值(可能为debug,eng,user等)
    include $(BUILD_EXECUTABLE)	//编译成可执行程序
    
    include $(CLEAR_VARS)
    LOCAL_SHARED_LIBRARIES := liblog libcutils libselinux
    LOCAL_SRC_FILES := service_manager.c binder.c
    LOCAL_CFLAGS += $(svc_c_flags)
    LOCAL_MODULE := servicemanager
    LOCAL_INIT_RC := servicemanager.rc
    include $(BUILD_EXECUTABLE)
    

    除了上面的环境变量,编译系统还设置了很多其他的环境变量,如下:

    环境变量 说明
    LOCAL_PACKAGE_NAME 当前APK应用的名称(具有唯一性)
    LOCAL_C_INCLUDES C/C++所需的头文件路径
    LOCAL_STATIC_LIBRARIES 当前模块静态连接需要的库
    LOCAL_STATIC_JAVA_LIBRARIES 当前模块依赖的Java静态库
    LOCAL_JAVA_LIBRARIES 当前模块依赖的Java动态库
    LOCAL_CERTIFICATE 签署当前应用的证书名称
    BUILD_EXECUTABLE 编译成可执行程序
    BUILD_STATIC_JAVA_LIBRARY 编译成Java静态库
    BUILD_SHARED_LIBRARY 编译成C/C++动态库

    此外,编译系统还定义了一些函数,如下:

    	$(call all-java-files-under, ):获取指定目录下的所有Java文件;
    	$(call all-c-files-under, ):获取指定目录下的所有C文件;
    	$(call all-Iaidl-files-under, ) :获取指定目录下的所有AIDL文件;
    	$(call all-makefiles-under, ):获取指定目录下的所有Make文件;
    
    • Android.bp语法

    Android.bp 是一种配置文件,语法类似于JSON,文件中仅记录当前模块的信息,没有条件判断或者控制流语句,控制逻辑需要Go来处理。bp文件的语法类似于Bazel,由Go语言进行解析,转换为Ninja文件。

    Bazel的参考文档

    frameworks/base/Android.bp 文件为例,具体如下:

    // ====  c++ proto device library  ==============================
    cc_library {
        name: "libplatformprotos",
        host_supported: true,
        proto: {
            export_proto_headers: true,
            include_dirs: ["external/protobuf/src"],
        },
    
        target: {
            host: {
                proto: {
                    type: "full",
                },
                srcs: [
                    "core/proto/**/*.proto",
                    "libs/incident/**/*.proto",
                ],
            },
            android: {
                proto: {
                    type: "lite",
                },
                // We only build the protos that are optimized for the lite
                // runtime, as well as the only protos that are actually
                // needed by the device.
                srcs: [
                    "core/proto/android/service/graphicsstats.proto",
                ],
                shared: {
                    enabled: false,
                },
            },
        },
    }
    
    subdirs = [
        "core/jni",
        "libs/*",
        "media/*",
        "tools/*",
        "native/android",
        "native/graphics/jni",
    ]
    
    optional_subdirs = [
        "core/tests/utiltests/jni",
    ]
    
    

    定义一个模块从模块的类型开始,如例子中的"cc_library",模块会包含一些属性,格式为"{name: value}".

    编译指令

    代码编译

    编译指令 说明
    m 在源码树的根目录执行编译
    mm 编译当前路径下所有模块,不包含依赖
    mmm [module_path] 编译指定路径下所有模块,不包含依赖
    mma 编译当前路径下所有模块,包含依赖
    mmma [module_path] 编译指定路径下所有模块,包含依赖
    make [module_name] 无参数,则表示编译整个Android代码

    部分模块的编译指令:

    # make help
    
    Common make targets:
    --------------------------------------------------------------------------------
    droid                   Default target
    clean                   (aka clobber) equivalent to rm -rf out/
    snod                    Quickly rebuild the system image from built packages
    vnod                    Quickly rebuild the vendor image from built packages
    offline-sdk-docs        Generate the HTML for the developer SDK docs
    doc-comment-check-docs  Check HTML doc links & validity, without generating HTML
    libandroid_runtime      All the JNI framework stuff
    framework               All the java framework stuff
    services                The system server (Java) and friends
    help                    You're reading it right now
    
    #### build completed successfully  ####
    
    
    
    模块 make mmm
    init make init mmm system/core/init
    zygote make app_process mmm frameworks/base/cmds/app_process
    system_services make services mmm frameworks/base/services
    java framework make framework mmm framworks/base
    framework-res make framework-res mmm frameworks/base/core/res
    jni framework make libandroid_runtime mmm frameworks/base/core/jni
    binder make libbinder mmm frameworks/native/libs/binder

    上述mmm命令同样适用于mm/mma/mmma,编译系统采用的是增量编译,只会编译发生变化的目标文件。

    代码检索

    Android源码非常庞大,直接使用grep/ag来搜索代码,可能会耗费大量时间。根据具体需求,可以选择合适的检索指令,有效节省代码搜索时间,提高准确度。

    指令 说明
    cgrep Greps on all local C/C++ files.
    ggrep Greps on all local Gradle files.
    jgrep Greps on all local Java files.
    resgrep Greps on all local res/*.xml files.
    mangrep Greps on all local AndroidManifest.xml files.
    mgrep Greps on all local Makefiles files.
    sepgrep Greps on all local sepolicy files.
    sgrep Greps on all local source files.

    其他指令

    上面介绍了常用的编译和检索指令,还有一些其他指令,可以执行 hmm 查询指令的帮助信息。

    指令 说明
    hmm 查询所有指令的帮助信息
    gettop 查询Android源码的根目录
    printconfig 打印设置的编译参数配置
    print_lunch_menu 打印lunch可选的板级配置
    godir 跳转到包含某个文件的目录
    findmakefile 查询当前目录所在工程的Android.mk文件路径
    Author: xujinlong Email: xyxujinlong@163.com
  • 相关阅读:
    Linux设备驱动程序 之 度量时间差
    mysql外键的使用
    tomcat常用配置详解和优化方法
    什么是跨域?跨域解决方法
    springboot+vue项目实战
    利用MySQL数据库如何解决大数据量存储问题?
    在MySQL中存储大文件
    web开发用到的技术
    网络100个知识点
    jetty使用教程
  • 原文地址:https://www.cnblogs.com/tinylaker/p/10573377.html
Copyright © 2011-2022 走看看