搭建gn环境踩坑存档
流程
1. 项目根目录下新增.gclient和.gclient_entries
//.gclient
solutions = [
{
"name": "src",
"url": "",
"managed": False,
"custom_deps": {},
"custom_vars": {},
},
]
// .gclient_entries
entries = {
'src': '',
}
gclient是一个多仓管理工具,可以把多个仓库的源码clone到本地的一个项目中,具体可以看2. chromium开发工具--gclient - Bigben - 博客园
2. 新建并进入src目录
3. 新建示例代码hello_world.cc
#include <iostream>
using namespace std;
int main()
{
cout<< "hello world" << endl;
while(1) {} // 为了让窗口不要自动关闭,好看到输出结果
return 0;
}
4 新建.gn,该文件指出了gn的配置文件路径,以及使用哪个python解释器。
buildconfig = "//build/config/BUILDCONFIG.gn"
# The python interpreter to use by default. On Windows, this will look
# for python3.exe and python3.bat.
script_executable = "python3"
5. 新建BULD.gn,这个文件指出我们该如何形成可执行文件
executable("hello_world") {
sources = [
"hello-world.cc",
]
}
6. 新建build/config/BUILDCONFIG.gn,把chromuim源码下对应文件粘过来,因为我只想研究一下核心流程,不考虑兼容性,所以我把default_compiler_configs及以后的代码全删了,不然要复制太多文件了。
7. 运行gn gen out/default,out/后文件名可以任意,但是out/这个前缀不能修改。
8. 报错gn.py: Could not find checkout in any parent of the current path。原因是src目录下缺少buildtools,把chromium/src/buildtools复制到<your project>/src/buildtools即可。
9. 接下来会报一堆缺少文件的错误,这些文件分别在src/build、src/build_overrides、src/buildtools、src/tools四个目录下,你可以试着把这几个文件夹一次性从chromium项目下拷贝过来,我当时只拷了build文件夹所以报错了,而且没报具体错误原因所以没法排查,最后是缺哪个文件拷哪个文件一点点拷完了,rust相关代码我不想复制了,所以引用到的地方我就全删了。
10. gn gen out/default后,运行
autoninja -C out/default
这时候会报错depot_tools/ninja.py: Could not find Ninja in the third_party of the current project, nor in your PATH,原因就是ninja.exe既不在当前项目下,也不在环境变量里。
解决方案:在src目录下新建third_party,把chromium项目中的third_party/ninja复制过去。
11. 再次运行autoninja -C out/default,报错The system cannot find the file specified.
把chromium项目third_party目录下llvm-build\Release+Asserts\bin\clang-cl.exe复制过来,再次运行命令就可以成功了。
12. 双击out/default/hello_world.exe,显示
证明构建成功了。
问题汇总
问题一:gn.py: Could not find checkout in any parent of the current path.
核心原因:缺少buildtools。
解决方案:把chromium/src/buildtools复制到<your project>/src/buildtools,进入src目录后执行命令
gn gen out/default
.gclient和.gclient_entries文件最好也加上,虽然gclient_paths.py脚本中做了兼容,如果命令行所在目录下存在buildtools,就返回当前目录作为入口,但这毕竟不是一种规范的做法。
问题二:could not execute interpreter
根目录下的.gn文件需要设置script_executable,否则gn会直接执行py文件,windows环境下会在环境变量path中寻找python.exe和python.bat,没找到就会报错。
# The python interpreter to use by default. On Windows, this will look
# for python3.exe and python3.bat.
script_executable = "python3"
具体逻辑看gn源码src\gn\setup.cc
大致就是先查当前目录下是否存在python3.exe,存在就返回绝对路径。
否则查找环境变量path下是否存在python3.exe,和python3.bat;如果存在python3.exe,返回对应绝对路径,存在python3.bat,就通过该文件找到对应的python3.exe,返回exe的绝对路径。
否则返回空路径。
// python_exe_name and python_bat_name can be empty but cannot be absolute
// paths. They should be "python.exe" or "", etc., and "python.bat" or "", etc.
base::FilePath FindWindowsPython(const base::FilePath& python_exe_name,
const base::FilePath& python_bat_name) {
char16_t current_directory[MAX_PATH];
::GetCurrentDirectory(MAX_PATH, reinterpret_cast<LPWSTR>(current_directory));
// First search for python.exe in the current directory.
if (!python_exe_name.empty()) {
CHECK(python_exe_name.FinalExtension() == u".exe");
CHECK_EQ(python_exe_name.IsAbsolute(), false);
base::FilePath cur_dir_candidate_exe =
base::FilePath(current_directory).Append(python_exe_name);
if (base::PathExists(cur_dir_candidate_exe))
return cur_dir_candidate_exe;
}
// Get the path.
const char16_t kPathEnvVarName[] = u"Path";
DWORD path_length = ::GetEnvironmentVariable(
reinterpret_cast<LPCWSTR>(kPathEnvVarName), nullptr, 0);
if (path_length == 0)
return base::FilePath();
std::unique_ptr<char16_t[]> full_path(new char16_t[path_length]);
DWORD actual_path_length = ::GetEnvironmentVariable(
reinterpret_cast<LPCWSTR>(kPathEnvVarName),
reinterpret_cast<LPWSTR>(full_path.get()), path_length);
CHECK_EQ(path_length, actual_path_length + 1);
// Search for python.exe in the path.
for (const auto& component : base::SplitStringPiece(
std::u16string_view(full_path.get(), path_length), u";",
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
if (!python_exe_name.empty()) {
base::FilePath candidate_exe =
base::FilePath(component).Append(python_exe_name);
if (base::PathExists(candidate_exe))
return candidate_exe;
}
// Also allow python.bat, but convert into the .exe.
if (!python_bat_name.empty()) {
CHECK(python_bat_name.FinalExtension() == u".bat");
CHECK_EQ(python_bat_name.IsAbsolute(), false);
base::FilePath candidate_bat =
base::FilePath(component).Append(python_bat_name);
if (base::PathExists(candidate_bat)) {
base::FilePath python_exe = PythonBatToExe(candidate_bat);
if (!python_exe.empty())
return python_exe;
}
}
}
return base::FilePath();
}
问题三:depot_tools/ninja.py: Could not find Ninja in the third_party of the current project, nor in your PATH
解决方案,要么添加指向ninja.exe的path,要么就在项目里添加third_party/ninja.exe
def main(args):
# ...
# Get gclient root + src.
primary_solution_path = gclient_paths.GetPrimarySolutionPath()
gclient_root_path = gclient_paths.FindGclientRoot(os.getcwd())
gclient_src_root_path = None
if gclient_root_path:
gclient_src_root_path = os.path.join(gclient_root_path, "src")
for base_path in set(
# 这三个目录下只要有一个存在/third_party/ninja.exe就行
[primary_solution_path, gclient_root_path, gclient_src_root_path]):
if not base_path:
continue
ninja_path = os.path.join(
base_path,
"third_party",
"ninja",
"ninja" + gclient_paths.GetExeSuffix(),
)
if os.path.isfile(ninja_path):
check_out_dir(args[1:])
return subprocess.call([ninja_path] + args[1:])
return fallback(args[1:]) #这个函数会在环境变量中找ninja.exe,还没找到就会打印报错信息了