1 // Copyright 2025 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <filesystem>
6 #include <fstream>
7 #include <iostream>
8
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/json/json_reader.h"
12 #include "base/strings/string_util.h"
13 #include "base/values.h"
14 #include "gn/build_settings.h"
15 #include "gn/config.h"
16 #include "gn/filesystem_utils.h"
17 #include "gn/functions.h"
18 #include "gn/label_ptr.h"
19 #include "gn/parse_tree.h"
20 #include "gn/precise/precise.h"
21 #include "gn/settings.h"
22 #include "gn/substitution_writer.h"
23 #include "gn/target.h"
24 #include "gn/value.h"
25
26 namespace fs = std::filesystem;
27
28 PreciseManager* PreciseManager::instance_ = nullptr;
29 static int hFileDepth_ = INT_MAX;
30 static int cFileDepth_ = INT_MAX;
31 static int gnFileDepth_ = INT_MAX;
32 static int gnModuleDepth_ = INT_MAX;
33 static bool testOnly_ = false;
34 static std::string outDir_;
35 static std::string preciseConfig_;
36 static std::string modifyFilesPath_;
37 static std::string preciseResultPath_;
38 static std::string preciseLogPath_;
39 static std::vector<std::string> targetTypeList_;
40 static std::vector<std::string> modifyHFileList_;
41 static std::vector<std::string> modifyCFileList_;
42 static std::vector<std::string> modifyGnFileList_;
43 static std::vector<std::string> modifyGnModuleList_;
44 static std::vector<std::string> ignoreList_;
45 static std::vector<std::string> maxRangeList_;
46
ReadFile(base::FilePath path,std::string & content)47 static bool ReadFile(base::FilePath path, std::string& content)
48 {
49 if (!base::ReadFileToString(path, &content)) {
50 return false;
51 }
52 return true;
53 }
54
LoadHFileList(const base::Value & list)55 static void LoadHFileList(const base::Value& list)
56 {
57 for (const base::Value& value : list.GetList()) {
58 modifyHFileList_.push_back(value.GetString());
59 }
60 }
61
LoadCFileList(const base::Value & list)62 static void LoadCFileList(const base::Value& list)
63 {
64 for (const base::Value& value : list.GetList()) {
65 modifyCFileList_.push_back(value.GetString());
66 }
67 }
68
LoadGnFileList(const base::Value & list)69 static void LoadGnFileList(const base::Value& list)
70 {
71 for (const base::Value& value : list.GetList()) {
72 modifyGnFileList_.push_back(value.GetString());
73 }
74 }
75
LoadGnModuleList(const base::Value & list)76 static void LoadGnModuleList(const base::Value& list)
77 {
78 for (const base::Value& value : list.GetList()) {
79 modifyGnModuleList_.push_back(value.GetString());
80 }
81 }
82
LoadHFileDepth(const base::Value & depth)83 static void LoadHFileDepth(const base::Value& depth)
84 {
85 hFileDepth_ = depth.GetInt();
86 }
87
LoadCFileDepth(const base::Value & depth)88 static void LoadCFileDepth(const base::Value& depth)
89 {
90 cFileDepth_ = depth.GetInt();
91 }
92
LoadGnFileDepth(const base::Value & depth)93 static void LoadGnFileDepth(const base::Value& depth)
94 {
95 gnFileDepth_ = depth.GetInt();
96 }
97
LoadGnModuleDepth(const base::Value & depth)98 static void LoadGnModuleDepth(const base::Value& depth)
99 {
100 gnModuleDepth_ = depth.GetInt();
101 }
102
LoadIgnoreList(const base::Value & list)103 static void LoadIgnoreList(const base::Value& list)
104 {
105 for (const base::Value& value : list.GetList()) {
106 ignoreList_.push_back(value.GetString());
107 }
108 }
109
LoadMaxRangeList(const base::Value & list)110 static void LoadMaxRangeList(const base::Value& list)
111 {
112 for (const base::Value& value : list.GetList()) {
113 maxRangeList_.push_back(value.GetString());
114 }
115 }
116
LoadModifyFilesPath(const base::Value & value)117 static void LoadModifyFilesPath(const base::Value& value)
118 {
119 modifyFilesPath_ = value.GetString();
120 std::cout << "Precise config modify files path : " << modifyFilesPath_ << std::endl;
121 }
122
LoadPreciseResultPath(const base::Value & value)123 static void LoadPreciseResultPath(const base::Value& value)
124 {
125 preciseResultPath_ = value.GetString();
126 std::cout << "Precise config result path : " << preciseResultPath_ << std::endl;
127 }
128
LoadPreciseLogPath(const base::Value & value)129 static void LoadPreciseLogPath(const base::Value& value)
130 {
131 preciseLogPath_ = value.GetString();
132 std::cout << "Precise config log path : " << preciseLogPath_ << std::endl;
133 }
134
LoadTestOnly(const base::Value & value)135 static void LoadTestOnly(const base::Value& value)
136 {
137 testOnly_ = value.GetBool();
138 std::cout << "Precise config testonly : " << testOnly_ << std::endl;
139 }
140
LoadTargetTypeList(const base::Value & list)141 static void LoadTargetTypeList(const base::Value& list)
142 {
143 for (const base::Value& value : list.GetList()) {
144 targetTypeList_.push_back(value.GetString());
145 }
146 }
147
148 static std::map<std::string, std::function<void(const base::Value& value)>> modifyMap_ = {
149 { "h_file", LoadHFileList },
150 { "c_file", LoadCFileList },
151 { "gn_file", LoadGnFileList },
152 { "gn_module", LoadGnModuleList },
153 };
154
155 static std::map<std::string, std::function<void(const base::Value& value)>> configMap_ = {
156 { "h_file_depth", LoadHFileDepth },
157 { "c_file_depth", LoadCFileDepth },
158 { "gn_file_depth", LoadGnFileDepth },
159 { "gn_module_depth", LoadGnModuleDepth },
160 { "test_only", LoadTestOnly},
161 { "target_type_list", LoadTargetTypeList },
162 { "ignore_list", LoadIgnoreList },
163 { "max_range_list", LoadMaxRangeList },
164 { "modify_files_path", LoadModifyFilesPath },
165 { "precise_result_path", LoadPreciseResultPath },
166 { "precise_log_path", LoadPreciseLogPath },
167 };
168
LoadModifyList()169 static void LoadModifyList()
170 {
171 std::string modifyListContent;
172 if (!ReadFile(base::FilePath(modifyFilesPath_), modifyListContent)) {
173 std::cout << "Load modify file list failed." << std::endl;
174 return;
175 }
176 const base::DictionaryValue* modifyListDict;
177 std::unique_ptr<base::Value> modifyList = base::JSONReader::ReadAndReturnError(modifyListContent,
178 base::JSONParserOptions::JSON_PARSE_RFC, nullptr, nullptr, nullptr, nullptr);
179 if (!modifyList) {
180 std::cout << "Read modify file json failed." << std::endl;
181 return;
182 }
183 if (!modifyList->GetAsDictionary(&modifyListDict)) {
184 std::cout << "Get modify file dictionary failed." << std::endl;
185 return;
186 }
187
188 for (const auto kv : modifyListDict->DictItems()) {
189 auto iter = modifyMap_.find(kv.first);
190 if (iter != modifyMap_.end()) {
191 iter->second(kv.second);
192 }
193 }
194 }
195
LoadPreciseConfig()196 static void LoadPreciseConfig()
197 {
198 std::string configContent;
199 if (!ReadFile(base::FilePath(preciseConfig_), configContent)) {
200 std::cout << "Load precise config failed." << std::endl;
201 return;
202 }
203 const base::DictionaryValue* configDict;
204 std::unique_ptr<base::Value> config = base::JSONReader::ReadAndReturnError(configContent,
205 base::JSONParserOptions::JSON_PARSE_RFC, nullptr, nullptr, nullptr, nullptr);
206 if (!config) {
207 std::cout << "Read precise config json failed." << std::endl;
208 return;
209 }
210 if (!config->GetAsDictionary(&configDict)) {
211 std::cout << "Get precise config dictionary failed." << std::endl;
212 return;
213 }
214
215 for (const auto kv : configDict->DictItems()) {
216 auto iter = configMap_.find(kv.first);
217 if (iter != configMap_.end()) {
218 iter->second(kv.second);
219 }
220 }
221 }
222
PreciseManager(const std::string & outDir,const std::string & preciseConfig)223 PreciseManager::PreciseManager(const std::string& outDir, const std::string& preciseConfig)
224 {
225 std::cout << "Read precise config from " << preciseConfig << std::endl;
226 outDir_ = outDir;
227 preciseConfig_ = preciseConfig;
228 LoadPreciseConfig();
229 LoadModifyList();
230 }
231
AddModule(std::string name,Node * node)232 void PreciseManager::AddModule(std::string name, Node* node)
233 {
234 moduleList_[name] = node;
235 }
236
IsIgnore(const std::string & name)237 bool PreciseManager::IsIgnore(const std::string& name)
238 {
239 auto result = std::find(ignoreList_.begin(), ignoreList_.end(), name);
240 if (result != ignoreList_.end()) {
241 return true;
242 }
243 return false;
244 }
245
IsInMaxRange(const std::string & name)246 bool PreciseManager::IsInMaxRange(const std::string& name)
247 {
248 if (maxRangeList_.empty()) {
249 return true;
250 }
251 auto result = std::find(maxRangeList_.begin(), maxRangeList_.end(), name);
252 if (result != maxRangeList_.end()) {
253 return true;
254 }
255 return false;
256 }
257
IsDependent(const Node * node)258 bool PreciseManager::IsDependent(const Node* node)
259 {
260 int size = node->GetFromList().size();
261 if (size == 0) {
262 return false;
263 }
264 if (size == 1) {
265 const Item* item = ((Module* )(node->GetFromList()[0]))->GetItem();
266 if(!FilterType(item)) {
267 return false;
268 }
269 }
270 return true;
271 }
272
IsContainModifiedFiles(const std::string & file,bool isHFile)273 bool PreciseManager::IsContainModifiedFiles(const std::string& file, bool isHFile)
274 {
275 if (isHFile) {
276 for (const std::string& h : modifyHFileList_) {
277 if (base::starts_with(h, file)) {
278 return true;
279 }
280 }
281 return false;
282 } else {
283 for (const std::string& c : modifyCFileList_) {
284 if (c == file) {
285 return true;
286 }
287 }
288 return false;
289 }
290 }
291
GetModule(const std::string & name)292 Node* PreciseManager::GetModule(const std::string& name)
293 {
294 return moduleList_[name];
295 }
296
CheckIncludeInConfig(const Config * config)297 bool PreciseManager::CheckIncludeInConfig(const Config* config)
298 {
299 const std::vector<SourceDir> dirs = config->own_values().include_dirs();
300 for (const SourceDir& dir : dirs) {
301 if (IsContainModifiedFiles(dir.value(), true)) {
302 return true;
303 }
304 }
305 return false;
306 }
307
CheckIncludeInTarget(const Item * item)308 bool PreciseManager::CheckIncludeInTarget(const Item* item)
309 {
310 if (item == nullptr) {
311 return false;
312 }
313
314 std::vector<SourceDir> dirs;
315 if (item->GetItemTypeName() == "target") {
316 dirs = item->AsTarget()->include_dirs();
317 } else if (item->GetItemTypeName() == "config") {
318 dirs = item->AsConfig()->own_values().include_dirs();
319 } else {
320 return false;
321 }
322
323 std::string name = item->label().GetUserVisibleName(false);
324 for (const SourceDir& dir : dirs) {
325 if (IsContainModifiedFiles(dir.value(), true)) {
326 return true;
327 }
328 }
329 return false;
330 }
331
CheckSourceInTarget(const Item * item)332 bool PreciseManager::CheckSourceInTarget(const Item* item)
333 {
334 if (item == nullptr || item->GetItemTypeName() != "target") {
335 return false;
336 }
337
338 const std::vector<SourceFile>& dirs = item->AsTarget()->sources();
339 for (const SourceFile& dir : dirs) {
340 if (IsContainModifiedFiles(dir.value(), false)) {
341 return true;
342 }
343 }
344 return false;
345 }
346
CheckConfigInfo(const UniqueVector<LabelConfigPair> & configs)347 bool PreciseManager::CheckConfigInfo(const UniqueVector<LabelConfigPair>& configs)
348 {
349 for (const auto& config : configs) {
350 std::string label = config.label.GetUserVisibleName(false);
351 if (base::starts_with(label, "//build/config")) {
352 continue;
353 }
354 if (CheckIncludeInConfig(config.ptr)) {
355 return true;
356 }
357 }
358 return false;
359 }
360
CheckPrivateConfigs(const Item * item)361 bool PreciseManager::CheckPrivateConfigs(const Item* item)
362 {
363 if (item == nullptr || item->GetItemTypeName() != "target") {
364 return false;
365 }
366
367 const UniqueVector<LabelConfigPair> configs = item->AsTarget()->configs();
368 if (configs.size() > 0) {
369 if (CheckConfigInfo(configs)) {
370 return true;
371 }
372 }
373 return false;
374 }
375
CheckPublicConfigs(const Item * item)376 bool PreciseManager::CheckPublicConfigs(const Item* item)
377 {
378 if (item == nullptr || item->GetItemTypeName() != "target") {
379 return false;
380 }
381
382 const UniqueVector<LabelConfigPair> configs = item->AsTarget()->public_configs();
383 if (configs.size() > 0) {
384 if (CheckConfigInfo(configs)) {
385 return true;
386 }
387 }
388 return false;
389 }
390
CheckAllDepConfigs(const Item * item)391 bool PreciseManager::CheckAllDepConfigs(const Item* item)
392 {
393 if (item == nullptr || item->GetItemTypeName() != "target") {
394 return false;
395 }
396
397 const UniqueVector<LabelConfigPair> configs = item->AsTarget()->all_dependent_configs();
398 if (configs.size() > 0) {
399 if (CheckConfigInfo(configs)) {
400 return true;
401 }
402 }
403 return false;
404 }
405
EnsurePathExists(const std::string & filePath)406 void PreciseManager::EnsurePathExists(const std::string& filePath)
407 {
408 fs::path path(filePath);
409 auto dirPath = path.parent_path();
410 if (!dirPath.empty() && !fs::exists(dirPath)) {
411 fs::create_directories(dirPath);
412 }
413 }
414
WriteFile(const std::string & path,const std::string & info)415 void PreciseManager::WriteFile(const std::string& path, const std::string& info)
416 {
417 std::string outFile = outDir_ + "/" + path;
418 EnsurePathExists(outFile);
419 std::ofstream fileFd;
420 fileFd.open(outFile, std::ios::out);
421 fileFd << info;
422 fileFd.close();
423 }
424
FilterType(const Item * item)425 bool PreciseManager::FilterType(const Item* item)
426 {
427 if (item == nullptr || item->GetItemTypeName() == "config") {
428 return false;
429 }
430
431 std::string name = item->label().GetUserVisibleName(false);
432 if (base::ends_with(name, "__check")
433 || base::ends_with(name, "__collect")
434 || base::ends_with(name, "__notice")
435 || base::ends_with(name, "_info_install_info")
436 || base::ends_with(name, "_resource_copy")) {
437 return false;
438 }
439
440 return true;
441 }
442
IsTargetTypeMatch(const Item * item)443 bool PreciseManager::IsTargetTypeMatch(const Item* item)
444 {
445 if (item == nullptr || item->GetItemTypeName() == "config") {
446 return false;
447 }
448
449 std::string type;
450 if (item->GetItemTypeName() == "target") {
451 std::string tmp(Target::GetStringForOutputType(item->AsTarget()->output_type()));
452 type += tmp;
453 } else {
454 type += item->GetItemTypeName();
455 }
456
457 if (std::find(targetTypeList_.begin(), targetTypeList_.end(), type) != targetTypeList_.end()) {
458 return true;
459 }
460
461 return false;
462 }
463
IsTestOnlyMatch(const Item * item)464 bool PreciseManager::IsTestOnlyMatch(const Item* item)
465 {
466 if (item == nullptr || (testOnly_ && !item->testonly())) {
467 return false;
468 }
469
470 return true;
471 }
472
IsFirstRecord(const std::vector<std::string> & result,const std::string & name)473 bool PreciseManager::IsFirstRecord(const std::vector<std::string>& result, const std::string& name)
474 {
475 if (std::find(result.begin(), result.end(), name) == result.end()) {
476 return true;
477 }
478 return false;
479 }
480
PreciseSearch(const Node * node,std::vector<std::string> & result,std::vector<std::string> & log,bool forGn,int depth,int maxDepth)481 void PreciseManager::PreciseSearch(const Node* node, std::vector<std::string>& result, std::vector<std::string>& log,
482 bool forGn, int depth, int maxDepth)
483 {
484 Module* module = (Module* )node;
485 const Item* item = module->GetItem();
486 std::string name = item->label().GetUserVisibleName(false);
487 log.push_back("Check:" + name);
488
489 if (depth >= maxDepth) {
490 log.push_back("Over Depth:" + name);
491 return;
492 }
493
494 if (!FilterType(item)) {
495 log.push_back("FilterType false:" + name);
496 return;
497 }
498
499 if (IsTargetTypeMatch(item) && IsTestOnlyMatch(item) && IsFirstRecord(result, name)
500 && !IsIgnore(name) && IsInMaxRange(name)) {
501 log.push_back("OK:" + name);
502 result.push_back(name);
503 return;
504 }
505
506 for (Node* parent : node->GetFromList()) {
507 Module* moduleParent = (Module* )parent;
508 const Item* itemParent = moduleParent->GetItem();
509 std::string nameParent = itemParent->label().GetUserVisibleName(false);
510 log.push_back("Check Parent:" + nameParent + "->" + name);
511 PreciseSearch(parent, result, log, forGn, depth + 1, maxDepth);
512 }
513 }
514
CheckModuleInGn(const std::string & label)515 bool PreciseManager::CheckModuleInGn(const std::string& label)
516 {
517 for (const std::string& gn : modifyGnFileList_) {
518 size_t pos = label.find(":");
519 if (pos == std::string::npos) {
520 return false;
521 }
522 std::string labelPrefix = label.substr(0, pos);
523
524 size_t posGn = gn.find("BUILD.gn");
525 if (posGn == std::string::npos) {
526 return false;
527 }
528 std::string filePrefix = gn.substr(0, posGn - 1);
529 if (labelPrefix == filePrefix) {
530 return true;
531 }
532 }
533 return false;
534 }
535
CheckModuleMatch(const std::string & label)536 bool PreciseManager::CheckModuleMatch(const std::string& label)
537 {
538 for (const std::string& modify : modifyGnModuleList_) {
539 if (label == modify) {
540 return true;
541 }
542 }
543 return false;
544 }
545
WritePreciseTargets(const std::vector<std::string> & result,const std::vector<std::string> & log)546 void PreciseManager::WritePreciseTargets(const std::vector<std::string>& result, const std::vector<std::string>& log)
547 {
548 std::string logInfo = "";
549 for(size_t i = 0; i < log.size(); ++i) {
550 logInfo += log[i];
551 logInfo += " ";
552 logInfo += "\n";
553 }
554 WriteFile(preciseLogPath_, logInfo);
555
556 std::string resultInfo = "";
557 for(size_t i = 0; i < result.size(); ++i) {
558 resultInfo += result[i];
559 resultInfo += " ";
560 resultInfo += "\n";
561 }
562 WriteFile(preciseResultPath_, resultInfo);
563 }
564
GeneratPreciseTargets()565 void PreciseManager::GeneratPreciseTargets()
566 {
567 std::cout << "GeneratPreciseTargets Begain." << std::endl;
568 std::vector<std::string> result;
569 std::vector<std::string> log;
570 log.push_back("Init Precise depth:" + std::to_string(hFileDepth_) + " " + std::to_string(cFileDepth_)
571 + " " + std::to_string(gnFileDepth_) + " " + std::to_string(gnModuleDepth_));
572
573 for (const auto& pair : moduleList_) {
574 Module* module = (Module* )pair.second;
575 const Item* item = module->GetItem();
576 std::string label = item->label().GetUserVisibleName(false);
577 if (!FilterType(item)) {
578 continue;
579 }
580
581 if (CheckSourceInTarget(item)) {
582 log.push_back("Hit C:");
583 PreciseSearch(pair.second, result, log, false, 0, cFileDepth_);
584 } else if (CheckIncludeInTarget(item) || CheckPrivateConfigs(item)
585 || CheckPublicConfigs(item) || CheckAllDepConfigs(item)) {
586 log.push_back("Hit H:");
587 PreciseSearch(pair.second, result, log, false, 0, hFileDepth_);
588 } else if (CheckModuleInGn(label)) {
589 log.push_back("Hit GN:");
590 PreciseSearch(pair.second, result, log, true, 0, gnFileDepth_);
591 } else if (CheckModuleMatch(label)) {
592 log.push_back("Hit Module:");
593 PreciseSearch(pair.second, result, log, true, 0, gnModuleDepth_);
594 }
595 }
596 WritePreciseTargets(result, log);
597 }