☰
lunch不显示构建列表?源码编译环境排障全指南
2026/10/13 4:51:20 网站建设 项目流程

lunch 不显示构建列表,这个问题凡是碰过源码编译环境的开发者,多少都遇到过几次。明明按教程执行了lunch,终端却只蹦出两行提示,然后“构建目标列表”那一栏干干净净,一个选项都没有,回车也不知道该选什么。整个编译流程直接卡在这一步,后面根本没法继续。

作为接手过不少源码构建环境的开发者,我可以负责任地说:这个问题的根源并不复杂,但现象特别能迷惑人。它不像编译报错那样会直接甩一段堆栈给你,而是“静悄悄地失败”——脚本跑完了,却什么都没给你列出来。这篇就是要把lunch背后的原理、不显示列表的几类常见原因、排查步骤,以及我自己踩过的坑一次性说清楚。不管是刚接触系统源码编译的新人,还是被这个问题反复折磨的熟手,都能对照着定位到自己机器上的问题。

1. 先说清楚 lunch 到底是干什么的

1.1 lunch 的工作机制,远不止“选一个 target”

很多教程把lunch描述成“选择编译目标的命令”,这没错,但如果你只把它当普通命令,遇到问题就很被动。lunch本质上不是一个独立的二进制程序,而是一个定义在环境初始化脚本里的shell 函数。它依赖你先把整个构建环境的初始化脚本用source方式加载进当前终端,函数才能真正“活”起来。

当你执行lunch(不带参数)时,它做的事情大致是:先去读取产品配置目录里的构建目标定义文件,把每个目标对应的编译产品名、设备名、版本变量都解析出来,然后按照固定的格式渲染成一个列表,最后等着你输入编号或名称。这个机制有点像餐厅里服务员递上来的菜单——厨房其实早就有能做哪些菜的清单,菜单只是一个“呈现”环节。lunch不显示列表,问题可能出在“菜单没印出来”,也可能出在“厨房压根不知道有哪些菜谱”。

理解了这个机制,你排障时就不会只盯着一行命令看了,而是会去检查三个环节:函数是否被正确定义、配置扫描路径是否正确、解析过程有没有被错误打断。这三个环节任何一个出问题,最终表现都是“列表不显示”。

1.2 “不显示构建列表”到底有哪几种表现

同样是“不显示列表”,实际现象是有差异的,而且这些差异直接指向不同原因。我把它分成三类,你排查时先对照自己的情况。

第一类是完全没有输出:执行lunch后,终端几乎没有任何反馈,直接就回到命令提示符了。这种情况绝大多数是lunch压根不是那个 shell 函数——比如环境脚本没 source 成功,系统里却恰好有个同名命令或别名,结果执行了别的东西。

第二类是打印了开头,但列表区为空:终端能看到You're building on Linux之类的提示,甚至能看到“Lunch menu... pick a combo”的引导文字,但真正列可选项的部分却没有内容。这种情况说明脚本函数执行了,配置扫描也跑了,只是筛选出来的目标数为零,或者中途因为某个解析错误被提前终止了。

第三类是列了列表但明显不全:列表只有一两项,而你确定仓库里应该有十几个目标设备。这种通常是配置目录里的产品定义文件没同步完整,或者文件格式/命名不符合脚本的匹配规则,被过滤掉了。

这三类现象的处理方向差别很大。最忌讳的是不管现象如何,直接去删缓存或重装环境,那样大概率把问题搞得更乱。先分清现象,再往下走。

2. 问题定位:先分清是“卡死”还是“列表为空”

2.1 执行 lunch 之后,先看这几样东西

拿到一台“lunch 不显示构建列表”的机器,我做的第一件事不是改代码,而是先盯着终端看三样东西:输出内容、退出状态、光标位置。

输出内容不要只看最后几行,要往上翻。脚本如果中途报错,错误信息往往混在环境初始化的输出里,很容易被滚动窗口淹没。建议直接执行并带上退出码查看:

lunch; echo "exit=$?"

如果exit=0,说明脚本逻辑走完了,列表为空是“正常地没找到目标”;如果exit非 0,说明哪一步直接报错中断了,错误信息通常就在前面。另外注意光标位置:如果执行后终端久久不返回,甚至像卡住一样,那就不是“不显示列表”的范畴,而是脚本在某个循环或输入读取上挂住了。前者是配置/扫描逻辑的问题,后者往往是终端类型或管道交互的问题,处理方式完全不同。

还有一个容易忽略的点:lunch交互模式下,它会读你的标准输入。如果你是通过脚本、管道、CI 任务去调用它,而不是在一个真正的终端里跑,它会读不到输入,表现也像是“没有输出列表”。这种要和“真不显示”分开判断。

2.2 快速确认环境初始化是否成功

lunch是 shell 函数,验证它是否存在,直接用一个命令就能看出来:

type lunch

如果输出是lunch is a shell function,说明环境脚本加载成功;如果输出是lunch is /usr/bin/lunch之类的文件路径,那就要小心了——你执行的可能根本不是构建脚本里那个函数。或者更糟的情况,type lunch直接提示not found,那说明初始化脚本压根没有运行。

很多源码仓库的构建环境里,初始化脚本是一个形如envsetup.sh的文件,位于仓库根目录。开发者需要用source或点号命令加载它,而不是直接执行:

source build/envsetup.sh

这里有个非常关键的区别:如果直接bash envsetup.sh,脚本会在一个子 shell 里执行,里面定义的环境变量和函数在子 shell 退出后全部消失,当前终端还是什么都没有。新手最容易在这一步踩坑。我见过不少“lunch 按下去没反应”的案例,最后发现就是把source写成了bash,或者干脆忘了执行这一步。

确认环境函数到位之后,再检查几个关键环境变量是否存在,比如TARGET_PRODUCT、TARGET_BUILD_VARIANT之类的构建变量。虽然未选 target 时它们可能是空的,但至少能看出初始化逻辑有没有运行到后半段。

3. 几类典型原因与修复方案

3.1 环境初始化脚本没有正确执行,导致 lunch 函数不存在

这是最常见、也最容易排查的原因,但它造成的现象很有欺骗性。有时候你明明执行过初始化脚本,但那是上一个终端会话里执行的;新开一个终端后,函数并不会残留。shell 的环境变量和函数定义只在当前进程里有效,终端一关就没了。所以我每次接手别人的电脑或服务器,都会先跑一下type lunch确认。

修复方法很简单:重新source正确的环境脚本。但要注意,有些仓库的初始化脚本还有前置依赖,比如必须先进入仓库根目录,或者必须设置某个JAVA_HOME、ANDROID_*环境变量,脚本才会完整执行。如果你发现source过程打印了报错,就要先解决那些报错,否则脚本可能没有执行到最后一步,lunch函数依然没定义。

另外,有些开发者喜欢把初始化脚本写进~/.bashrc,每次打开终端自动加载。这个思路本身没问题,但如果你用的 shell 不是 Bash,或者远程登录时走了非交互模式,自动加载不一定生效。判断方式还是type lunch,不要凭记忆觉得“我配置过”。

3.2 产品配置目录缺失或不在预期路径,扫描结果为空

lunch之所以能列出构建目标,是因为它在固定的目录结构里扫描一类特定命名规则的产品配置文件。这类文件通常以类似AndroidProducts.mk的文件名分布在设备相关的仓库目录里。假设你同步的源码不完整,或者仓库里缺了某个 device 目录,那扫描目标就会少一大截。

一个很容易被忽视的场景是:你换了分支,分支之间目录结构有差异。比如之前的分支把设备配置放在device/某厂商/某型号/下,新分支重构了目录,把它挪到了vendor/某厂商/下面。初始化脚本里的扫描路径如果还是旧逻辑,自然就找不到新位置的目标,列表自然为空。这种情况编译环境本身是“健康”的,纯粹是分支演进带来的路径漂移。

检查方式就是手动看一眼配置目录是否存在、是否有正常的配置文件,而不是空壳或.git目录。如果仓库同步是用某个 repo 管理工具拉的,还需要确认那几个配置相关的仓库有没有被正确检出来。经常有人同步到一半中断,结果目录在,文件却少了好几个。

修复就是补全同步,或者根据实际目录路径去调整扫描配置。但我不建议你直接修改扫描脚本去适配错误的分支——先确认是不是分支本身就不完整,才是正路。

3.3 Python 版本与脚本不兼容,解析过程异常中断

这个原因我单独拿出来说,是因为它在近几年的构建环境里越来越常见。构建选择脚本本身可能混合了不同版本的 Python 语法,有些旧环境默认python指向 Python 2,新环境默认指向 Python 3,脚本在解析配置时遇到不兼容的语法就直接抛异常了。

但诡异之处在于,某些脚本调子脚本的时候没有把 stderr 透传到终端,或者异常被 try/except 吞掉了,最终你看到的不是一串红字报错,而是“列表没打出来”。如果lunch执行后一切静悄悄,十有八九是这种静默失败。

排查方式很直接:

python --version python3 --version

看看默认python指向哪个版本,再根据构建系统要求的版本来调整。有的仓库确实要求 Python 3,但你在PATH里先找到了某个旧版本,导致脚本跑起来就报错。我自己的习惯是在环境初始化脚本前,单独把需要的 Python 路径加到PATH前部,或者用虚拟环境隔离,而不是去改系统默认的python链接。乱改系统 Python 链接极容易牵连其他工具。

3.4 环境变量残留与历史配置干扰,列表被“过滤干净”

这个原因特别隐蔽,也很容易发生在“来回切换目标产品”的开发机上。lunch的列表生成逻辑里,往往会读取一些现成的环境变量去过滤目标,比如你设置的TARGET_PRODUCT如果非空,脚本可能就不再显示交互列表,而是直接尝试加载那个指定的产品配置。

如果你之前编译过特定目标,这些变量被写入到~/.bashrc、~/.profile或其他 shell 配置文件里,每次新开终端都会自动加载。那么你再执行不带参数的lunch,脚本会走“跳过列表选择、直接进入指定配置”的逻辑,等同于你看到的现象就是“没有列表”。

处理方法也简单:检查这些 shell 配置文件里有没有写入构建变量,有的话清理掉。另外可以用env命令查看当前终端的完整环境变量,找到TARGET_*、LUNCH_*之类前缀的可疑变量,逐个unset。但要注意,这种清理只对当前终端有效,如果根因是写进了配置文件,一定还是要改配置文件才能根治。

3.5 文件权限、软链与挂载方式引发的怪问题

最后一类原因属于“环境性怪胎”,排查起来最费时间,但知道方向后会变得很快。比如源码目录是通过共享挂载方式访问的,目录权限或属主不对,脚本扫描时没有读取权限,静默跳过;又比如配置目录是一个软链,软链指向的路径失效,脚本遍历到它时直接跳过;再比如某些文件是从 Windows 拷贝过来的,带着CRLF换行符,脚本按 Unix 行去读配置时解析失败。

这类问题在 CI 机器、共享开发机、跨平台拷贝的场景里特别多。我印象很深的一次,是某台服务器上源码挂载自一个共享存储,所有 user 都没有写权限,但读权限正常。按理说不影响lunch扫描,可偏偏构建脚本启动时会在配置目录里尝试创建临时文件,创建失败又不报警,后续扫描直接返回空结果。最终结果是列表为空,但一切“看似正常”。

遇到这类情况,先确认你当前用户对源码目录是不是只有读权限;再看mount挂载参数和文件系统类型;最后拿一个代表性配置文件试试当前用户能否正常打开读取。三个检查做完,基本上问题就清楚了。

4. 排障实操:一步一步把问题找出来

4.1 从执行过程里收集最完整的线索

排障最怕信息不全,所以我建议你先别急着改任何东西,而是把问题现场完整复现一遍并保留输出。推荐手动执行:

source build/envsetup.sh lunch 2>&1 | tee /tmp/lunch_debug.log

用tee把输出同时写到文件里,哪怕终端显示内容早就滚走了,你也能回来慢慢看。如果lunch执行很快无输出,调试时还可以打开 shell 的追踪模式:

bash -x build/envsetup.sh

同样用2>&1 | tee收集日志。这种方式能看到脚本每一步执行了什么命令、哪个变量被赋了什么值、哪一步之后列表循环没有再继续。日志里往往直接写着原因:有可能是“目录不存在”,有可能是“匹配了 0 个文件”,也有可能是“Python 报错”。这一招能救回大量盲目操作的时间。

我个人比较反感“一出问题就先删除目录重新同步”的做法。因为删除重拉要花很长时间,而且如果根因是环境变量污染或脚本兼容问题,重新同步后问题依旧。保留日志、分析日志,才是性价比最高的起点。

4.2 用“干净终端对照法”做定位

很多lunch不显示列表的问题,来源是你的交互登录环境被太多自定义配置污染了。定位时可以用一个干净环境做对照实验。

先开一个不带任何配置的干净 Bash:

env -i bash --noprofile --norc

在这个 shell 里手动设置必要的基础变量(比如HOME、PATH),然后手动source环境初始化脚本,再执行lunch。如果这里能正常显示列表,说明问题出在你的登录环境加载了某些干扰配置;如果这里也一样空,那问题就在源码目录或脚本本身。

这个对照法特别适合排查“我这台机器刚换了个人用,之前好好的突然不行了”的场景。很多情况是某次装软件时,安装程序往~/.bashrc里塞了自定义变量,而这些变量正好和构建环境相冲突。用干净 shell 一跑,立刻水落石出。

拿到干净环境能正常工作之后,再逐步把你原来的自定义配置一项一项加回来,每加一项就执行一次lunch,直到定位出是哪一个变量/哪一段配置导致了问题。这个过程看着笨,但非常可靠。

4.3 给构建环境做一次“温和的重置”

如果确认当前环境已经被各种脏配置弄乱了,又不想重新登录或重启机器,可以按下面的顺序做一次温和重置。这个方法比直接重装环境温和得多,也快得多。

第一步,清理可疑环境变量:

unset TARGET_PRODUCT TARGET_BUILD_VARIANT TARGET_BUILD_TYPE unset lunch 2>/dev/null

注意:unset lunch只会删掉当前会话里函数定义,不会伤及任何文件,可以放心执行。

第二步,回到源码根目录,重新加载一次环境:

cd /path/to/source/root source build/envsetup.sh

第三步,确认函数和目录状态:

type lunch ls device/ vendor/

第四步,重新执行lunch。这时候一般就能看到完整列表了。

不到万不得已,我不建议动辄删除整个 out 目录或其他构建输出目录。因为如果只是环境状态问题,删除输出目录并不能解决lunch不显示列表的问题,反而会影响后续增量编译的速度。真想清缓存,优先考虑删除构建系统内部的状态缓存文件,而不是一股脑全删。

5. 常

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询