流程
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 = Noneif 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:continueninja_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,还没找到就会打印报错信息了