source()方法是源码“落盘”的关键一步,负责将源代码放置到self.source_folder目录下供后续构建使用;若未执行或失败,build()和package()将因找不到源码而报错。

source() 方法是源码“落盘”的关键一步
它不负责编译,只负责把源代码放到 self.source_folder 目录下——这个目录后续会被 build() 和 package() 阶段反复读取。如果 source() 没执行或执行失败,后面所有构建步骤都会因找不到源码而报错(比如 FileNotFoundError: No such file or directory 'src/main.cpp')。
什么时候必须写 source()?
以下情况绕不开 source():
- 源码不在当前 recipe 目录里(比如要从 Git、tarball 或本地路径拉取)
- 需要解压、打补丁、重命名、过滤文件(例如删掉测试用例或文档)
- 要适配不同平台的源码分支(如
git checkout origin/harmonyos-3.1) - 项目使用了
no_copy_source=True,此时 Conan 不会自动复制exports_sources,必须靠source()显式获取
常见错误和坑
source() 看似简单,但几个细节极易出问题:
- 路径操作要用
os.path.join(self.source_folder, ...),别硬写"./src"——self.source_folder在不同命令(conan createvsconan install --build)下可能指向不同位置 - 别在
source()里调用self.run("make")或任何构建命令 —— 这属于build()的职责,混用会导致缓存失效或并行冲突 - 如果用了
exports_sources = ["CMakeLists.txt"],又在source()里执行git clone,两者会打架:前者把文件复制进source_folder,后者又把它覆盖掉 -
source()在layout()之后执行,但self.source_folder此时还没被 Conan 设置好;所以不能依赖它的值做条件判断,更不能手动改它
一个干净的 source() 示例
这是 Flipper Zero 固件中 nanopb 依赖的真实写法片段:
def source(self):
git = Git(self)
git.clone("https://github.com/nanopb/nanopb.git", "nanopb-0.4.6")
# 注意:没用 os.chdir,而是让 Git 类自动落到 self.source_folder
它没碰 self.source_folder 变量,也没做任何构建动作,只专注一件事:把指定 tag 的源码放到位。复杂逻辑(比如 patch、proto 编译)留给 build() 阶段处理——这才是 recipe 可复用、可调试的分层基础。


















