• build/envsetup.sh
  • 命令————envsetup.sh中的函数简介
  • source build/envsetup.sh 执行流程
  • build/envsetup.sh 常用函数介绍
    • lunch流程
    • _lunch函数
    • gettop
    • hmm
    • mm
    • mmm
    • godir
    • sgrep

看了好多篇关于build/envsetup.sh 的介绍,记下的总结

build/envsetup.sh

每次开始编译开始的第一个命令便是source build/envsetup.sh,其中source命令就是用于运行shell脚本命令,功能等价于”.”,因此该命令也等价于. build/envsetup.sh。在文件envsetup.sh声明了当前会话终端可用的命令,这里需要注意的是当前会话终端,也就意味着每次新打开一个终端都必须再一次执行这些指令。build/envsetup.sh文件存在的意义就是,设置一些环境变量和shell函数为后续的编译工作做准备。 接下来我们分析下
envsetup.sh.

命令————envsetup.sh中的函数简介

导入envsetup.sh系统会都出一些命令,先看下命令的作用

常用的命令

编译指令 解释
mm 编译当前路径下所有模块,但不包含依赖
mmm [module_path] 编译指定路径下所有模块,但不包含依赖
mma 编译当前路径下所有模块,且包含依赖
mmma [module_path] 编译指定路径下所有模块,且包含依赖
make [module_name] 无参数,则表示编译整个Android代码
cgrep 当前目录下所有C/C++文件执行搜索操作
jgrep 当前目录下所有Java文件执行搜索操作
ggrep 当前目录下所有Gradle文件执行搜索操作
mangrep [keyword] 当前目录下所有AndroidManifest.xml文件执行搜索操作
mgrep [keyword] 当前目录下所有Android.mk文件执行搜索操作
sepgrep [keyword] 所有sepolicy文件执行搜索操作
resgrep [keyword] 当前目录下所有本地res/*.xml文件执行搜索操作
sgrep [keyword] 当前目录下基于(c
croot 切换至Android根目录
cproj 切换至工程的根目录
godir [filename] 跳转到包含某个文件的目录
hmm 查询所有的指令help信息

Tips: Android源码非常庞大,直接采用grep来搜索代码,不仅方法笨拙、浪费时间,而且搜索出很多无意义的混淆结果。根据具体需求,来选择合适的代码搜索指令,能节省代码搜索时间,提高搜索结果的精准度,方便定位目标代码。
godir [filename] 第一次执行速度较慢,之后运作效率比find命令高(具体见后面解析)

  • 其他指令
    make clean:执行清理操作,等价于 rm -rf out/
    make update-api:更新API,在framework API改动后需执行该指令,Api记录在目录frameworks/base/api

source build/envsetup.sh 执行流程

envsetup.sh 定义了很多函数,除此之外还执行了其它操作,以下是除去函数部分的代码::

VARIANT_CHOICES=(user userdebug eng)# 提前定义3种编译模式,供后面使用# Clear this variable.  It will be built up again when the vendorsetup.sh
# files are included at the end of this file.
#LUNCH_MENU_CHOICES是供用户选择的prodcut列表,
#每次source build/envsetup.sh时需重置变量LUNCH_MENU_CHOICES
#不然后续的include vendor/cm/vendorsetup.sh时会继续添加产品至变量LUNCH_MENU_CHOICES里,
#导致出现很多重复产品
unset LUNCH_MENU_CHOICES   #处理逻辑就是先从LUNCH_MENU_CHOICES中循环查找,看存不存在要添加的板型,如果存在就直接返回,如果不存在就添加到LUNCH_MENU_CHOICES中function add_lunch_combo()
{local new_combo=$1local cfor c in ${LUNCH_MENU_CHOICES[@]} ; doif [ "$new_combo" = "$c" ] ; thenreturnfidoneLUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
}#默认会加载如下6个板型选项
# 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 # 这行代码前定义了lunch和_lunch函数,这行代码的意思是通过_lunch函数实现lunch命令的自动补全功能
complete -F _lunch lunch #shell检查和警告,这里只支持bash,如果是其他的shell会发出这个WARNING
if [ "x$SHELL" != "x/bin/bash" ]; thencase `ps -o command -p $$` in*bash*);;*)echo "WARNING: Only bash is supported, use of other shell would lead to erroneous results";;esac
fi# Execute the contents of any vendorsetup.sh files we can find.
#source vendor和device下能找到的所有vendorsetup.sh
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`
doecho "including $f". $f
done
unset faddcompletions

最最后看看addcompletions干了啥

function addcompletions()
{local T dir f# Keep us from trying to run in something that isn't bash.if [ -z "${BASH_VERSION}" ]; thenreturnfi# Keep us from trying to run in bash that's too old.if [ ${BASH_VERSINFO[0]} -lt 3 ]; thenreturnfidir="sdk/bash_completion"if [ -d ${dir} ]; thenfor f in `/bin/ls ${dir}/[a-z]*.bash 2> /dev/null`; doecho "including $f". $fdonefi
}

如果BASH的版本为空或者小于3都直接返回,否则打印android/sdk/bash_completion/目录下的以.bash结尾的所有文件,执行.bash文件,实现Linux终端下adb命令的补全

至此, source build/envsetup.sh的过程就分析完了。

build/envsetup.sh 常用函数介绍

lunch流程

在source流程之后,紧接着就是执行lunch操作,lunch操作执行的其实就是build/envsetup.sh脚本中的lunch函数,直接看代码:

function lunch()
{local answer#如果lunch命令后跟有参数,则直接赋给answer变量;#如果lunch命令后没有参数,则调用函数print_lunch_menu打印出板型列表供用户选择,并将用户的选择存储在answer变量中。if [ "$1" ] ; thenanswer=$1elseprint_lunch_menu #函数的作用就是打印数组LUNCH_MENU_CHOICES的内容echo -n "Which would you like? [aosp_arm-eng] "read answerfilocal selection=#如果answer为空,则selection默认赋值为aosp_arm-eng;#如果answer是纯数字,则将answer作为数组下标从LUNCH_MENU_CHOICES数组中取出板型名称;#如果anwser是字符串,并且字符串使用”-”连接,而且”-”连接的前后两个子串中都没有”-”,则认为是板型名称字符串,直接赋给selection。if [ -z "$answer" ]thenselection=aosp_arm-engelif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")thenif [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]thenselection=${LUNCH_MENU_CHOICES[$(($answer-1))]}fielif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")thenselection=$answerfi#如果selection仍然为空,则直接报错并退出if [ -z "$selection" ]thenechoecho "Invalid lunch combo: $answer"return 1fiexport TARGET_BUILD_APPS=#从selection中取出“-”前面的字符串到product,local product=$(echo -n $selection | sed -e "s/-.*$//")#检查product的合法性,涉及到make 不展开check_product $productif [ $? -ne 0 ]thenechoecho "** Don't have a product spec for: '$product'"echo "** Do you have the right repo manifest?"product=fi#再从selection中取出“-”后面的字符串到variant,然后调用函数check_variant判断variant是否符合要求。check_variant函数很简单,主要就是用到前文source流程中初始化为"user userdebug eng"的VARIANT_CHOICES数组,判断variant是否是数组成员之一,是则返回0,不是则返回1。local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")check_variant $variantif [ $? -ne 0 ]thenechoecho "** Invalid variant: '$variant'"echo "** Must be one of ${VARIANT_CHOICES[@]}"variant=fiif [ -z "$product" -o -z "$variant" ]thenechoreturn 1fi#导出以下准备好的宏变量供整个shell环境使用export TARGET_PRODUCT=$productexport TARGET_BUILD_VARIANT=$variantexport TARGET_BUILD_TYPE=releaseecho#设置PROMPT_COMMAND,ANDROID_BUILD_PATHS,JAVA_HOME和BUILD_ENV_SEQUENCE_NUMBER等等环境变量set_stuff_for_environment#打印最终准备好的环境变量printconfig
} 

Tips:set_stuff_for_environment 里面的set_java_home函数,可以根据实际情况修改java路径等,这样可以避免编译不同的android版本还需手动切换java版本

_lunch函数

实现lunch命令的补全的步骤:

首先定义了lunch函数

然后定义了_lunch函数

使用complete命令

# Tab completion for lunch.
function _lunch()
{local cur prev optsCOMPREPLY=()cur="${COMP_WORDS[COMP_CWORD]}"prev="${COMP_WORDS[COMP_CWORD-1]}"COMPREPLY=( $(compgen -W "${LUNCH_MENU_CHOICES[*]}" -- ${cur}) )return 0
}

++complete -F _lunch lunch++ 是上面代码中最关键的一行。

当bash在遇到lunch这个词的时候,会调用_lunch函数。
该函数会传入三个参数:要补全的命令名、当前的光标所在的词、当前光标所在的词的前一个词。
补全的结果需要存储到COMPREPLY变量中,以待bash获取。

gettop

function gettop
{local TOPFILE=build/core/envsetup.mkif [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then# The following circumlocution ensures we remove symlinks from TOP.(cd $TOP; PWD= /bin/pwd)elseif [ -f $TOPFILE ] ; then# The following circumlocution (repeated below as well) ensures# that we record the true directory name and not one that is# faked up with symlink names.PWD= /bin/pwdelselocal HERE=$PWDT=while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do\cd ..T=`PWD= /bin/pwd -P`done\cd $HEREif [ -f "$T/$TOPFILE" ]; thenecho $Tfififi
}

-n 变量是否存在

-a 逻辑与

-f 文件是否存在

PWD 打印当前目录

这个函数主要是获取Android源码的根目录,根据根目录下的build/core/envsetup.mk文件来判断的。

注意:调用这个函数的时候有个要求:就是你必须在源码根目录的子目录中,如果你处在根目录的上级目录则调用失败,因为这个函数是通过不停的调用cd ..命令,然后通过文件build/core/envsetup.mk来判断源码根目录的。

hmm


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.
- 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.
- sepgrep: Greps on all local sepolicy files.
- sgrep:   Greps on all local source files.
- godir:   Go to the directory containing a file.Environemnt options:
- SANITIZE_HOST: Set to 'true' to use ASAN for all host modules. Note thatASAN_OPTIONS=detect_leaks=0 will be set by default until thebuild is leak-check clean.Look at the source to view more functions. The complete list is:
EOFT=$(gettop)local AA=""for i in `cat $T/build/envsetup.sh | sed -n "/^[ \t]*function /s/function \([a-z_]*\).*/\1/p" | sort | uniq`; doA="$A  $i"doneecho $A
}

帮助函数,cat $T/build/envsetup.sh | sed -n “/^[ \t]function /s/function ([a-z_]).*/\1/p” | sort | uniq 这句脚本主要是获取envsetup.sh里面的以function开头的函数,函数名都是字母,并将函数名打印出来。
sed -n “/^[ \t]*function /p” 是打印出以function 开头的行
s/function ([a-z_])./\1 这部分是将函数名提取出来
sort 排序
uniq 去重

mm

function mm()
{local T=$(gettop)local DRV=$(getdriver $T)# 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$DRV make $@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 relativelocal M=`echo $M|sed 's:'$T'/::'`if [ ! "$T" ]; thenecho "Couldn't locate the top of the tree.  Try setting TOP."return 1elif [ ! "$M" ]; thenecho "Couldn't locate a makefile from the current directory."return 1elsefor ARG in $@; docase $ARG inGET-INSTALL-PATH) GET_INSTALL_PATH=$ARG;;esacdoneif [ -n "$GET_INSTALL_PATH" ]; thenMODULES=ARGS=GET-INSTALL-PATHelseMODULES=all_modulesARGS=$@fiONE_SHOT_MAKEFILE=$M $DRV make -C $T -f build/core/main.mk $MODULES $ARGSfifi
}

如果当前处在源码根目录下,且存在Makefile文件,直接执行make $@命令。

如果不是在源码根目录下:循环调用cd .. 调用findmakefile函数查找Android.mk文件,直到根目录,将Android.mk文件的绝对路径返回给M.

如找到了:M=/home/admin/android/frameworks/base/Android.mk,调用: local M=echo $M|sed 's}'$T'/}}'这句就是将源码根目录/home/admin/android/去掉,取得相对目录后,M=frameworks/base/Android.mk

最后执行:ONE_SHOT_MAKEFILE=frameworks/base/android.mk make -C /home/admin/android all_modules

相当于make -C /home/admin/android all_modules ONE_SHOT_MAKEFILE=frameworks/base/android.mk

接下来会调用到:源码根目录下的Makefile中,我们发现内容是include build/core/main.mk,即走到了build/core/main.mk文件中。后面就设计到Makefile的问题,我们后面会分析。现在只需要知道mm命令是怎么调用makefile里面去就行

mmm

mmm 命令就是从指定目录下开始编译所有模块

function mmm()
{local T=$(gettop)local DRV=$(getdriver $T)if [ "$T" ]; thenlocal MAKEFILE=local MODULES=local ARGS=local DIR TO_CHOPlocal GET_INSTALL_PATH=local DASH_ARGS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^-.*$/')local DIRS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^[^-].*$/')for DIR in $DIRS ; doMODULES=`echo $DIR | sed -n -e 's/.*:\(.*$\)/\1/p' | sed 's/,/ /'`if [ "$MODULES" = "" ]; thenMODULES=all_modulesfiDIR=`echo $DIR | sed -e 's/:.*//' -e 's:/$::'`if [ -f $DIR/Android.mk ]; thenlocal TO_CHOP=`(\cd -P -- $T && pwd -P) | wc -c | tr -d ' '`local TO_CHOP=`expr $TO_CHOP + 1`local START=`PWD= /bin/pwd`local MFILE=`echo $START | cut -c${TO_CHOP}-`if [ "$MFILE" = "" ] ; thenMFILE=$DIR/Android.mkelseMFILE=$MFILE/$DIR/Android.mkfiMAKEFILE="$MAKEFILE $MFILE"elsecase $DIR inshowcommands | snod | dist | incrementaljavac | *=*) ARGS="$ARGS $DIR";;GET-INSTALL-PATH) GET_INSTALL_PATH=$DIR;;*) echo "No Android.mk in $DIR."; return 1;;esacfidoneif [ -n "$GET_INSTALL_PATH" ]; thenARGS=$GET_INSTALL_PATHMODULES=fiONE_SHOT_MAKEFILE="$MAKEFILE" $DRV make -C $T -f build/core/main.mk $DASH_ARGS $MODULES $ARGSelseecho "Couldn't locate the top of the tree.  Try setting TOP."return 1fi
}

首先T=$(gettop)获取源码根目录,

local DASH_ARGS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^-.*$/') 

根据mmm后面的参数获取以空格隔开,以-开头的字符串,如:mmm -B frameworks/base 则DASH_ARGS = -B

local DIRS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^[^-].*$/') 

根据mmm后面参数获取以空格隔开,不以-开头的字符串。如:DIRS=frameworks/base/

DIR=`echo $DIR | sed -e 's:/$::'` 

去掉DIR结尾的/,DIR=frameworks/base

if [ -f $DIR/Android.mk ] ;then
TO_CHOP=`(cd -P -- $T && pwd -P) | wc -c | tr -d ' '`

跳到源码根目录,pwd -P打印出来,获取到字符串长度如:/home/admin/androids TO_CHOP就是20

TO_CHOP=`expr $TO_CHOP + 1`START=`PWD= /bin/pwd`MFILE=`echo $START | cut -c${TO_CHOP}-`

获取当前目录字符串,去掉前面21个字符,如:当前目录为/home/admin/androids,则MFILE=”“

  if [ "$MFILE" = "" ] ; thenMFILE=$DIR/Android.mkelseMFILE=$MFILE/frameworks/base/Android.mk fi

这里进行判断当前目录是否就是源码目录,如果是的话,MFILE为空,所以MFILe=frameworks/base/Android.mk

否则:MFILE=$MFILE/frameworks/base/Android.mk

总之:就是把当前目录的前面源码根目录去了,然后加上后面mmm -B **参数部分相对的目录,最后加上Android.mk就是,最后Android.mk文件的相对路径。

ONE_SHOT_MAKEFILE=”MAKEFILE” make -C TTT DASH_ARGS all_modules $ARGS

相当于:make -C /home/admin/android -B all_modules ONE_SHOT_MAKEFILE=frameworks/base/Android.mk

godir

function godir () {#如果没有带参数直接提示错误返回if [[ -z "$1" ]]; thenecho "Usage: godir <regex>"returnfiT=$(gettop)#如果OUT_DIR 不等于空创建OUT_DIR这个目录,设置FILELIST文件路径if [ ! "$OUT_DIR" = "" ]; thenmkdir -p $OUT_DIRFILELIST=$OUT_DIR/filelistelseFILELIST=$T/filelistfi#判断filelist是否存在,不存在新建if [[ ! -f $FILELIST ]]; thenecho -n "Creating index..."# 使用find命令从编译的根目录下查找文件"-type f"(目录out和.repo除外),并将结果输出到$FILELIST文件中# out目录和.repo目录的排除选项分别为:# - "-wholename ./out -prune"# - "-wholename ./.repo -prune"# 关于find的"-wholename pattern"选项,其行为跟"-path pattern"基本一样,具体可以查看find的帮助信息# 因此filelist文件保存了除out和.repo目录外其余目录的完整文件名(\cd $T; find . -wholename ./out -prune -o -wholename ./.repo -prune -o -type f > $FILELIST)echo " Done"echo ""filocal lines# 根据传入godir的参数,在filelist中搜索,并用sed处理后将结果存放在lines中# 操作"sed -e 's/\/[^/]*$//'"仅保留完整文件名的路径部分lines=($(\grep "$1" $FILELIST | sed -e 's/\/[^/]*$//' | sort | uniq))# 检查lines中的结果,即filelist通过grep和sed操作后,是否还有匹配的目录if [[ ${#lines[@]} = 0 ]]; thenecho "Not found"returnfilocal pathnamelocal choice# 如果lines的结果多于1行,则对各行进行编号并输出if [[ ${#lines[@]} > 1 ]]; thenwhile [[ -z "$pathname" ]]; do# 从1开始编号local index=1local linefor line in ${lines[@]}; do# 对每行以类似以下的格式进行输出:printf "%6s %s\n" "[$index]" $line# 序号自增index=$(($index + 1))doneechoecho -n "Select one: "unset choice# 读取输入序号read choiceif [[ $choice -gt ${#lines[@]} || $choice -lt 1 ]]; thenecho "Invalid choice"continuefi# 取得输入序号对应的目录pathname=${lines[$(($choice-1))]}doneelse# 如果符合匹配的路径只有一条,则直接将匹配的路径存放到pathname中pathname=${lines[0]}fi跳转到\cd $T/$pathname
}

godir 是在编译路径下搜索匹配模式的目录,然后跳转到此目录,同时可以用来快速查找文件的路径
如像查找frameworks/base/core/res/res/values在那些地方被overlay 了直接godir frameworks/base/core/res/res/values/ 查找包含这个路径的所有文件夹,及所有可能被覆盖的地方

sgrep

case `uname -s` inDarwin)function sgrep(){find -E . -name .repo -prune -o -name .git -prune -o  -type f -iregex '.*\.(c|h|cc|cpp|S|java|xml|sh|mk|aidl)' -print0 | xargs -0 grep --color -n "$@"};;*)function sgrep(){   # 排除 .repo, .git目录# 并对后缀(c|h|cc|cpp|S|java|xml|sh|mk|aidl|vts)的文件执行grep模式搜索find . -name .repo -prune -o -name .git -prune -o  -type f -iregex '.*\.\(c\|h\|cc\|cpp\|S\|java\|xml\|sh\|mk\|aidl\)' -print0 | xargs -0 grep --color -n "$@"};;
esac

这里为什么没有把out目录去了表示无法理解

其他代码搜索原理类似就不一一列举

build/envsetup.sh 简介相关推荐

  1. 【转】Android source build/envsetup.sh学习笔记

    原文网址:http://blog.csdn.net/mliubing2532/article/details/7567164 如果你只需要修改某一个模块的内容,但是却每次都要执行make, 最后等待很 ...

  2. build/envsetup.sh脚本分析

    1. Android编译系统分析 编译脚本及系统变量 build/envsetup.sh脚本分析 在编译源代码之前通常需要在android源代码顶层目录执行 . ./build/envsetup.sh ...

  3. android8.1 source build/envsetup.sh分析 增加删除lunch

    https://blog.csdn.net/weixin_39694445/article/details/84753142 Android 编译过程 1.  初始化参数设置  环境变量 2.  检查 ...

  4. 【Bash百宝箱】Android envsetup.sh及lunch

    文章目录 1.envsetup.sh 2.lunch 3.make 在Android开发环境中编译一个目标时,一般要执行下面三行命令: $ . build/envsetup.sh $ lunch &l ...

  5. 编译Android源码(2) ---- envsetup.sh文件分析

    在Android源码下载完成后,只需要简单的三个步骤就能把Android编译完成( http://source.android.com/source/building.html): 1.当前目录切换到 ...

  6. android 编译步骤分析之envsetup.sh

    1. cd AP      进入AP目录,如果已经进入到了A目录,可以忽略 2. source build/envsetup.sh        初始编译环境,envsetup.sh里面是一些编译命令 ...

  7. iOS开发之Documentation.build/Script-BC552B3A15.sh:

    /Users/hbbhao/Library/Developer/Xcode/DerivedData/AWLive-dmbegyhgamayzudqqdentwngdpkr/Build/Intermed ...

  8. 第2章:Android的编译环境--build系统

    2.0 build简介 Android的build系统基于GNU Make 和shell 构建的一套编译环境.这套系统定义了大量的变量和函数,无论编写一个产品的配置文件还是一个模块的Android.m ...

  9. 了解一下,Android 10 Build系统

    源起 因工作原因不得不重新抄起Android源代码开始看.这次就直接上Android 10.0了.当把代码导入Source Insight后,感慨万千.我一度觉得对TA的熟悉简直超过对自己的身体... ...

  10. android系列:第一篇 android开发常用命令集合,代码目录简介

    下面整理了android开发常用命令集合如adb命令,adb over wifi,jgrep等代码搜索命令,编译环境变量配置,lunch平台选择,mm模块编译,godir代码路径跳转,log.v()等 ...

最新文章

  1. Linux服务之Samba服务篇
  2. html设置正则表达式,html 正则表达式
  3. 尚学堂java 答案解析 第五章
  4. Angular19 自定义表单控件
  5. 工程师误删了公司生产数据库,如何看待数据安全架构的脆弱性?
  6. CentOS 6下安装nodejs 0.9.0
  7. java 项目心得_读懂JAVA WEB项目的一点心得 | 学步园
  8. linux远程访问服务器
  9. Hadoop之mapReduce有几种排序及排序发生的阶段
  10. Surprise官方文档练习
  11. 关于联想键盘,如何去除fn 键 联想官方解决方法
  12. golang防止MySQL注入_防止SQL注入解决方案
  13. 检验二元分解是否为无损分解(非加性)
  14. git lfs mac 安装_Git LFS
  15. php网站挂马,转 :php 网站挂马检查
  16. python三阶魔方_三阶魔方自动求解及动态可视化matlab代码
  17. 宽高不定div水平、垂直居中
  18. 一张图看懂阿里云网络产品[七]共享流量包
  19. 高尔顿数据集和Anscombe四重奏数据集
  20. 旅行照片剪辑--北京篇

热门文章

  1. 计算机科学箴言集 -- 编程珠玑续
  2. 计算机中丢失galaxy2d.dll,galaxy2d.dll
  3. 进博会高端自行车领导品牌辐轮王盛赞第三届进博会精彩而富有成效
  4. 信息安全基础练习题(看完包过)
  5. word 尾注 脚注_如何在Microsoft Word中使用脚注和尾注
  6. 泛微OA系统远程命令执行漏洞
  7. MySQl学习(从入门到精通 1.1)
  8. (附源码)springboot宠物管理系统 毕业设计 121654
  9. 网络上行 下行速度测试软件,测试网络流畅度和上下行的方法
  10. windows 上面如何解压rar 文件