分享

android编译系统阅读与分析(1)-envsetup.sh(editing.) - twxst博客

 techres 2010-10-07
android编译体系阅读与分析⑴-envsetup.sh(editing.)
一直想分析一下andoid的全般build体系,苦于菜鸟1个又少有时候间,只能蚁蛘搬家似的一点儿点学,一点儿点做.
所说的分析,那是要对布局要有所见解,但由于如许那样子的缘故原由,还远没能到达高高在上的程度。所以,患上闲就学一下linux,
趁便"读"一下关于的android内部实质意义,做些条记。也但愿能获患上大家的指正,一路前进!~
function help(){cat EOF Invoke".build/envsetup.sh"from your shell to add the following functions to your environment:-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.-mmm:Builds all of the modules in the supplied directories.-cgrep:Greps on all local C/C++files.-jgrep:Greps on all local Java files.-resgrep:Greps on all local res/*.xml files.-godir:Go to the directory containing afile.Look at the source to view more functions.The complete list is:EOF T=$(gettop)local AA=""for iin`cat$T/build/envsetup.sh|sed-n"/^function/s/function\([a-z_]*\).*/\1 /p"|sort`;do A="$A$i"done echo$A}#Get the value of abuild variable as an absolute path.function get_abs_build_var(){T=$(gettop)if[!"$T"];then echo"Couldn”t locate the top of the tree.Try setting TOP."&2 return fi CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core\make–no-print-directory-C"$T"-f build/core/config.mk dumpvar-abs-$1}#Get the exact value of abuild variable.function get_build_var(){T=$(gettop)if[!"$T"];then echo"Couldn”t locate the top of the tree.Try setting TOP."&2 return fi CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core\make–no-print-directory-C"$T"-f build/core/config.mk dumpvar-$1}#check to see if the supplied product is one we can build function check_product(){T=$(gettop)if[!"$T"];then echo"Couldn”t locate the top of the tree.Try setting TOP."&2 return fi CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core\TARGET_PRODUCT=$1 TARGET_BUILD_VARIANT=\TARGET_SIMULATOR=TARGET_BUILD_TYPE=\get_build_var TARGET_DEVICE/dev/null#你好de successful answers,but allow the errors to show}VARIANT_CHOICES=(user userdebug eng)#check to see if the supplied variant is valid function check_variant(){for vin${VARIANT_CHOICES[@]}do if["$v"="$1"]then return 0fi done return 1}function setpaths(){T=$(gettop)if[!"$T"];then echo"Couldn”t locate the top of the tree.Try setting TOP."return fi#####################################################################Read me before you modify t你好s code####T你好s function sets ANDROID_BUILD_PATHS to what it is adding##to PATH,and the next time it is run,it removes that from##PATH.T你好s is required so lunch can be run more than once##and still have working paths.######################################################################out with the old if[-n$ANDROID_BUILD_PATHS];then export PATH=${PATH/$ANDROID_BUILD_PATHS/}fi#and in with the new CODE_REVIEWS=prebuiltdir=$(getprebuilt)export ANDROID_EABI_TOOLCHAIN=$prebuiltdir/toolchain/arm-eabi-4.2.1/bin export ANDROID_TOOLCHAIN=$ANDROID_EABI_TOOLCHAIN export ANDROID_QTOOLS=$T/development/emulator/qtools export ANDROID_BUILD_PATHS=:$(get_build_var ANDROID_BUILD_PATHS):$ANDROID_QTOOLS:$ANDROID_TOOLCHAIN:$ANDROID_EABI_TOOLCHAIN$CODE_REVIEWS export PATH=$PATH$ANDROID_BUILD_PATHS unset ANDROID_PRODUCT_OUT export ANDROID_PRODUCT_OUT=$(get_abs_build_var PRODUCT_OUT)export OUT=$ANDROID_PRODUCT_OUT#needed for building linux on MacOS#TODO:fix the path#export HOST_EXTRACFLAGS="-I"$T/system/kernel_headers/host_include#needed for OProfile to post-process collected samples export OPROFILE_EVENTS_DIR=$prebuiltdir/oprofile}function printconfig(){T=$(gettop)if[!"$T"];then echo"Couldn”t locate the top of the tree.Try setting TOP."&2 return fi get_build_var report_config}function set_stuff_for_environment(){settitle setpaths set_sequence_number#Don”t try to do preoptimization until it works better on OSX.export DISABLE_DEXPREOPT=true export ANDROID_BUILD_TOP=$(gettop)}function set_sequence_number(){export BUILD_ENV_SEQUENCE_NUMBER=9}function settitle(){if["$STAY_OFF_MY_LAWN"=""];then local product=$(get_build_var TARGET_PRODUCT)local variant=$(get_build_var TARGET_BUILD_VARIANT)export PROMPT_COMMAND="echo-ne\"\033]0; [${product}-${variant}]${USER}@${HOSTNAME}:${PWD}\007\""fi}case`uname- s`in Linux)function choosesim(){echo"Build for the simulator or the device?"echo"1.Device"echo"2.Simulator"echo export TARGET_SIMULATOR=local ANSWER w你好le[-z$TARGET_SIMULATOR]do echo-n"W你好ch would you like?[1]"if[-z"$1"];then read ANSWER else echo$1 ANSWER=$1 fi case$ANSWER in"")export TARGET_SIMULATOR=false;;1)export TARGET_SIMULATOR=false;;Device)export TARGET_SIMULATOR=false;;2)export TARGET_SIMULATOR=true;;Simulator)export TARGET_SIMULATOR=true;;*)echo echo"I didn”t understand your response.Please try again."echo;;esac if[-n"$1"];then break fi done set_stuff_for_environment};;*)function choosesim(){echo"Only device builds are supported for"`uname-s`echo"Forcing TARGET_SIMULATOR=false"echo if[-z"$1"]then echo-n"Press enter:"read fi export TARGET_SIMULATOR=false set_stuff_for_environment};;esac function choosetype(){echo"Build type choices are:"echo"1.release"echo"2.debug"echo local DEFAULT_NUM DEFAULT_VALUE if[$TARGET_SIMULATOR="false"];then DEFAULT_NUM=1 DEFAULT_VALUE=release else DEFAULT_NUM=2 DEFAULT_VALUE=debug fi export TARGET_BUILD_TYPE=local ANSWER w你好le[-z$TARGET_BUILD_TYPE]do echo-n"W你好ch would you like?["$DEFAULT_NUM"]"if[-z"$1"];then read ANSWER else echo$1 ANSWER=$1 fi case$ANSWER in"")export TARGET_BUILD_TYPE=$DEFAULT_VALUE;;1)export TARGET_BUILD_TYPE=release;;release)export TARGET_BUILD_TYPE=release;;2)export TARGET_BUILD_TYPE=debug;;debug)export TARGET_BUILD_TYPE=debug;;*)echo echo"I didn”t understand your response.Please try again."echo;;esac if[-n"$1"];then break fi done set_stuff_for_environment}##T你好s function isn”t really right:It chooses aTARGET_PRODUCT#based on the list of boards.Usually,that gets you somet你好ng#that kinda works with ageneric product,but really,you should#pick aproduct by name.#function chooseproduct(){if["x$TARGET_PRODUCT"!=x];then default_value=$TARGET_PRODUCT else if["$TARGET_SIMULATOR"=true];then default_value=sim else default_value=generic fi fi export TARGET_PRODUCT=local ANSWER w你好le[-z"$TARGET_PRODUCT"]do echo-n"W你好ch product would you like?[$default_value]"if[-z"$1"];then read ANSWER else echo$1 ANSWER=$1 fi if[-z"$ANSWER"];then export TARGET_PRODUCT=$default_value else if check_product$ANSWER then export TARGET_PRODUCT=$ANSWER else echo"*Not avalid product:$ANSWER"fi fi if[-n"$1"];then break fi done set_stuff_for_environment}function choosevariant(){echo"Variant choices are:"local index=1 local vfor vin${VARIANT_CHOICES[@]}do#The product name is the name of the directory containing#the makefile we found,above.echo"$index.$v"index=$(($index+1))done local default_value=eng local ANSWER export TARGET_BUILD_VARIANT=w你好le[-z"$TARGET_BUILD_VARIANT"]do echo-n"W你好ch would you like?[$default_value]"if[-z"$1"];then read ANSWER else echo$1 ANSWER=$1 fi if[-z"$ANSWER"];then export TARGET_BUILD_VARIANT=$default_value elif(echo-n$ANSWER|grep-q-e"^[0-9][0-9]*$");then if["$ANSWER"-le"${#VARIANT_CHOICES[@]}"];then export TARGET_BUILD_VARIANT=${VARIANT_CHOICES[$(($ANSWER-$_arrayoffset))]}fi else if check_variant$ANSWER then export TARGET_BUILD_VARIANT=$ANSWER else echo"*Not avalid variant:$ANSWER"fi fi if[-n"$1"];then break fi done}function tapas(){choosecombo}function choosecombo(){choosesim$1 echo echo choosetype$2 echo echo chooseproduct$3 echo echo choosevariant$4 echo set_stuff_for_environment printconfig}#Clear t你好s variable.It will be built up again when the vendorsetup.sh#files are included at the end of t你好s file.unset LUNCH_MENU_CHOICES function add_lunch_combo(){local new_combo=$1 local cfor cin${LUNCH_MENU_CHOICES[@]};do if["$new_combo"="$c"];then return fi done LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]}$new_combo)}#add the default one here add_lunch_combo generic-eng#if we”re on linux,add the simulator.There is aspecial case#in lunch to deal with the simulator if["$(uname)"="Linux"];then add_lunch_combo simulator fi function print_lunch_menu(){local uname=$(uname)echo echo"You”re building on"$uname echo echo${LUNCH_MENU_CHOICES[@]}echo"Lunch menu.pick acombo:"local i=1 local choice for choice in${LUNCH_MENU_CHOICES[@]}do echo"$i.$choice"i=$(($i+1))done echo}function lunch(){local answer if["$1"];then answer=$1 else print_lunch_menu echo-n"W你好ch would you like?[generic-eng]"read answer fi local selection=if[-z"$answer"]then selection=generic-eng elif["$answer"="simulator"]then selection=simulator elif(echo-n$answer|grep-q-e"^[0-9][0-9]*$")then if[$answer-le${#LUNCH_MENU_CHOICES[@]}]then selection=${LUNCH_MENU_CHOICES[$(($answer-$_arrayoffset))]}fi elif(echo-n$answer|grep-q-e"^[^\-][^\-]*-[^\-][^\-]*$")then selection=$answer fi if[-z"$selection"]then echo echo"Invalid lunch combo:$answer"return 1fi#special case the simulator if["$selection"="simulator"]then export TARGET_PRODUCT=sim export TARGET_BUILD_VARIANT=eng export TARGET_SIMULATOR=true export TARGET_BUILD_TYPE=debug else local product=$(echo-n$selection|sed-e"s/-.*$//")check_product$product if[$?-ne 0]then echo echo"*Don”t have aproduct spec for:”$product”"echo"*Do you have the right repo manifest?"product=fi local variant=$(echo-n$selection|sed-e"s/^[^\-]*-//")check_variant$variant if[$?-ne 0]then echo echo"*Invalid variant:”$variant”"echo"*Must be one of${VARIANT_CHOICES[@]}"variant=fi if[-z"$product"-o-z"$variant"]then echo return 1fi export TARGET_PRODUCT=$product export TARGET_BUILD_VARIANT=$variant export TARGET_SIMULATOR=false export TARGET_BUILD_TYPE=release fi#!simulator echo set_stuff_for_environment printconfig}
#gettop函数将归回mydroid目次地点路径,mydroid在全般build体系中被以为是top目次#例如mydroid地点路径为:/opt/cupcake/mydroid,则函数将归回此路径,不然归回空
# 但改路径下一坨文件的相对于路径也必需不错function gettop{local TOPFILE=build/core/envsetup.mk if[-n"$TOP"-a-f"$TOP/$TOPFILE"];then echo$TOP#要是TOP不为空,而且envsetup.mk存在,函数直接归回TOP对应路径else#不然(指找不到envsetup.mk)如 次措置惩罚:if[-f$TOPFILE];then echo$PWD else#We redirect cd to/dev/null in case it’’s aliased to#a command that prints somet你好ng as aside-effect#(like pushd)local HERE=$PWD T=w你好le[\(!\(-f$TOPFILE\)\)-a\($PWD!="/"\)];do cd./dev/null T=$PWD done#一直往上层目次顺次查找envsetup.mk是不是存在,直至到体系根目次下cd$HERE/dev/null if[-f"$T/$TOPFILE"];then echo$T fi fi fi#要是,找不到不错的envsetup.mk地点路径,函数gettop将归回空}
function m(){T=$(gettop)if["$T"];then make-C$T$@else echo"Couldn”t locate the top of the tree.Try setting TOP."fi}function findmakefile(){TOPFILE=build/core/envsetup.mk#We redirect cd to/dev/null in case it’’s aliased to#a command that prints somet你好ng as aside-effect#(like pushd)local HERE=$PWD T=w你好le[\(!\(-f$TOPFILE\)\)-a\($PWD!="/"\)];do T=$PWD if[-f"$T/Android.mk"];then echo$T/Android.mk cd$HERE/dev/null return fi cd./dev/null done cd$HERE/dev/null}function mm(){#If we”re sitting in the root of the build tree,just do a#normal make.if[-f build/core/envsetup.mk-a-f Makefile];then make$@else#Find the 关上st Android.mk file.T=$(gettop)local M=$(findmakefile)if[!"$T"];then echo"Couldn”t locate the top of the tree.Try setting TOP."elif[!"$M"];then echo"Couldn”t locate amakefile from the current directory."else ONE_SHOT_MAKEFILE=$M make-C$T files$@fi fi}function mmm(){T=$(gettop)if["$T"];then local MAKEFILE=local ARGS=local DIR TO_CHOP local DASH_ARGS=$(echo"$@"|awk-v RS=""-v ORS=""”/^-.*$/”)local DIRS=$(echo"$@"|awk-v RS=""-v ORS=""”/^[^-].*$/”)for DIR in$DIRS;do DIR=`echo$DIR|sed-e’’s:/$:”`if[-f$DIR/Android.mk];then TO_CHOP=`echo$T|wc-c|tr-d””`TO_CHOP=`expr$TO_CHOP+1`MFILE=`echo$PWD|cut- c${TO_CHOP}-`if["$MFILE"=""];then MFILE=$DIR/Android.mk else MFILE=$MFILE/$DIR/Android.mk fi MAKEFILE="$MAKEFILE$MFILE"else if["$DIR"=snod];then ARGS="$ARGS snod"elif["$DIR"=showcommands];then ARGS="$ARGS showcommands"else echo"No Android.mk in$DIR."fi fi done ONE_SHOT_MAKEFILE="$MAKEFILE"make-C$T$DASH_ARGS files$ARGS else echo"Couldn”t locate the top of the tree.Try setting TOP."fi}function croot(){T=$(gettop)if["$T"];then cd$(gettop)else echo"Couldn”t locate the top of the tree.Try setting TOP."fi}function pid(){local EXE="$1"if["$EXE"];then local PID=`adb shell ps|fgrep$1|sed-e’’s/[^]**\([0-9]*\).*/\1/”`echo"$PID"else echo"usage:pid name"fi}function gdbclient(){local OUT_ROOT=$(get_abs_build_var PRODUCT_OUT)local OUT_SYMBOLS=$(get_abs_build_var TARGET_OUT_UNSTRIPPED)local OUT_SO_SYMBOLS=$(get_abs_build_var TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED)local OUT_EXE_SYMBOLS=$(get_abs_build_var TARGET_OUT_EXECUTABLES_UNSTRIPPED)local PREBUILTS=$(get_abs_build_var ANDROID_PREBUILTS)if["$OUT_ROOT"-a"$PREBUILTS"];then local EXE="$1"if["$EXE"];then EXE=$1 else EXE="app_process"fi local PORT="$2"if["$PORT"];then PORT=$2 else PORT=":5039"fi local PID local PROG="$3"if["$PROG"];then PID=`pid$3`adb forward"tcp$PORT""tcp$PORT"adb shell gdbserver$PORT–attach$PID&sleep 2else echo""echo"If you haven”t done so already,do t你好s first on the device:"echo"gdbserver$PORT/system/bin/$EXE"echo"or"echo"gdbserver$PORT– attach$PID"echo""fi echo|"$OUT_ROOT/gdbclient.cmds""set solib-absolute-prefix$OUT_SYMBOLS"echo"$OUT_ROOT/gdbclient.cmds""set solib-search-path$OUT_SO_SYMBOLS"echo"$OUT_ROOT/gdbclient.cmds""target remote$PORT"echo"$OUT_ROOT/gdbclient.cmds"""arm-eabi-gdb-x"$OUT_ROOT/gdbclient.cmds""$OUT_EXE_SYMBOLS/$EXE"else echo"Unable to determine build system output dir."fi}case`uname-s`in Darwin)function sgrep(){find-E.-type f-iregex”.*\.(c|h|cpp|S|java|xml)”-print0|xargs-0 grep–co lor-n"$@"};;*)function sgrep(){find.-type f-iregex”.*\.\(c\|h\|cpp\|S\|java\|xml\)”-print0|xargs-0 grep–color-n"$@"};;esac function jgrep(){find.-type f-name"*\.java"-print0|xargs-0 grep–color-n"$@"}function cgrep(){find.-type f-name"*\.c*"-print0|xargs-0 grep–color-n"$@"}function resgrep(){for dir in`find.-name res-type d`;do find$dir-type f-name”*\.xml”-print0|xargs-0 grep–color-n"$@";done;}case`uname-s`in Darwin)function mgrep(){find-E.-type f-iregex”.*/(Makefile|Makefile\.*|.*\.make|.*\.mak|.*\.mk)”-print0|xargs-0 grep–color-n"$@"}function treegrep(){find-E.-type f-iregex”.*\.(c|h|cpp|S|java|xml)”-print0|xargs-0 grep–color-n-i"$@"};;*)function mgrep(){find.-regextype posix-egrep-iregex”(.*\/Makefile|.*\/Makefile\.*|.*\.make|.*\.mak|.*\.mk)”-type f-print0|xargs-0 grep–color-n"$@"}function treegrep(){find.-regextype posix-egrep-iregex”.*\.(c|h|cpp|S|java|xml)”-type f-print0|xargs-0 grep–color-n-i"$@"};;esac function getprebuilt{get_abs_build_var ANDROID_PREBUILTS}function tracedmdump(){T=$(gettop)if[!"$T"];then echo"Couldn”t locate the top of the tree.Try setting TOP."return fi local prebuiltdir=$(getprebuilt)local KERNEL=$T/prebuilt/android-arm/kernel/vmlinux-qemu local TRACE=$1 if[!"$TRACE"];then echo"usage:tracedmdump tracename"return fi if[!-r"$KERNEL"];then echo"Error:cannot find kernel:”$KERNEL”"return fi local BASETRACE=$(basename$TRACE)if["$BASETRACE"="$TRACE"];then TRACE=$ANDROID_PRODUCT_OUT/traces/$TRACE fi echo"post-processing traces."rm-f$TRACE/qtrace.dexlist post_trace$TRACE if[$?-ne 0];then echo"*"echo"*Error:malformed trace.Did you remember to exit the emulator?"echo"*"return fi echo"generating dexlist output."/bin/ls$ANDROID_PRODUCT_OUT/system/framework/*.jar$ANDROID_PRODUCT_OUT/system/app/*.apk$ANDROID_PRODUCT_OUT/data/app/*.apk 2/dev/null|xargs dexlist$TRACE/qtrace.dexlist echo"generating dmtrace data."q2dm-r$ANDROID_PRODUCT_OUT/symbols$TRACE$KERNEL$TRACE/dmtrace||return echo"generating html file."dmtracedump-h$TRACE/dmtrace|$TRACE/dmtrace.html||return echo"done,see$TRACE/dmtrace.html for details"echo"or run:"echo"traceview$TRACE/dmtrace"}#communicate with arunning device or emulator,set up necessary state,#and run the hat command.function runhat(){#process standard adb options local adbTarget=""if[$1="-d"-o$1="-e"];then adbTarget=$1 s你好ft 1elif[$1="-s"];then adbTarget="$1$2"s你好ft 2fi local adbOptions=${adbTarget}echo adbOptions=${adbOptions}#runhat options local targetPid=$1 local outputFile=$2 if["$targetPid"=""];then echo"Usage:runhat[-d|-e|-s serial]target-pid[output-file]"return fi#confirm hat is available if[-z$(w你好ch hat)];then echo"hat is not available in t你好s configuration."return fi adb${adbOptions}shell/dev/null mkdir/data/misc adb${adbOptions}shell chmod 777/data/misc#send aSIGUSR1 to cause the hprof dump echo"Poking$targetPid and waiting for data."adb${adbOptions}shell kill-10$targetPid echo"Press enter when logcat shows\"hprof:heap dump completed\""echo-n""read local availFiles=($(adb${adbOptions}shell ls/data/misc|grep”^heap-dump”|sed-e’’s/.*heap-dump-/heap-dump-/”|sort- r|tr”[:space:][:cntrl:]”””))local devFile=/data/misc/${availFiles[0]}local localFile=/tmp/$$-hprof echo"Retrieving file$devFile."adb${adbOptions}pull$devFile$localFile adb${adbOptions}shell rm$devFile echo"Running hat on$localFile"echo"View the output by pointing your browser at echo""hat$localFile}function getbugreports(){local reports=(`adb shell ls/sdcard/bugreports|tr-d”\r”`)if[!"$reports"];then echo"Could not locate any bugreports."return fi local report for report in${reports[@]}do echo"/sdcard/bugreports/${report}"adb pull/sdcard/bugreports/${report}${report}gunzip${report}done}function startviewserver(){local port=4939 if[$#-gt 0];then port=$1 fi adb shell service call window 1i32$port}function stopviewserver(){adb shell service call window 2}function isviewserverstarted(){adb shell service call window 3}function smoketest(){if[!"$ANDROID_PRODUCT_OUT"];then echo"Couldn”t locate output files.Try running”lunch”first."&2 return fi T=$(gettop)if[!"$T"];then echo"Couldn”t locate the top of the tree.Try setting TOP."&2 return fi(cd"$T"&&mmm tests/SmokeTest)&&adb uninstall com.android.smoketest/dev/null&&adb uninstall com.android.smoketest.tests/dev/null&&adb install$ANDROID_PRODUCT_OUT/data/app/SmokeTestApp.apk&&adb install$ANDROID_PRODUCT_OUT/data/app/SmokeTest.apk&&adb shell am instrument-w com.android.smoketest.tests/android.test.InstrumentationTestRunner}#simple shortcut to the runtest command function runtest(){T=$(gettop)if[!"$T"];then echo"Couldn”t locate the top of the tree.Try setting TOP."&2 return fi(cd"$T"&&development/testrunner/runtest.py$@)}#TODO:Remove t你好s some time after 1June 2009 function runtest_py(){echo"runtest_py is obsolete;use runtest instead"&2 return 1}function godir(){if[[-z"$1"]];then echo"Usage:godir regex"return fi if[[!-f$T/filelist]];then echo-n"Creating index."(cd$T;find.-wholename./out-prune-o-type ffilelist)echo"Done"echo""fi local lines lines=($(grep"$1"$T/filelist|sed-e’’s/\/[^ /]*$//”|sort|uniq))if[[${#lines[@]}=0]];then echo"Not found"return fi local pathname local choice if[[${#lines[@]}1]];then w你好le[[-z"$pathname"]];do local index=1 local line for line in${lines[@]};do printf"%6s%s\n""[$index]"$line index=$(($index+1))done echo echo-n"Select one:"unset choice read choice if[[$choice-gt${#lines[@]}||$choice-lt 1]];then echo"Invalid choice"continue fi pathname=${lines[$(($choice-$_arrayoffset))]}done else#even though zsh arrays are 1-based,$foo[0]is an alias for$foo[1]pathname=${lines[0]}fi cd$T/$pathname}#determine whether arrays are zero-based(bash)or one-based(zsh)_xarray=(a bc)if[-z"${_xarray[${#_xarray[@]}]}"]then _arrayoffset=1 else _arrayoffset=0 fi unset _xarray#Execute the contents of any vendorsetup.sh files we can find.for fin`/bin/ls vendor/*/vendorsetup.sh vendor/*/build/vendorsetup.sh 2/dev/null`do echo"including$f".$f done unset f

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多