• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2023 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 "gn/ninja_outputs_writer.h"
6 
7 #include <algorithm>
8 #include <memory>
9 
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/json/string_escape.h"
13 #include "gn/builder.h"
14 #include "gn/commands.h"
15 #include "gn/filesystem_utils.h"
16 #include "gn/invoke_python.h"
17 #include "gn/settings.h"
18 #include "gn/string_output_buffer.h"
19 
20 // NOTE: Intentional macro definition allows compile-time string concatenation.
21 // (see usage below).
22 #if defined(OS_WINDOWS)
23 #define LINE_ENDING "\r\n"
24 #else
25 #define LINE_ENDING "\n"
26 #endif
27 
28 namespace {
29 
30 using MapType = NinjaOutputsWriter::MapType;
31 
32 // Sort the targets according to their human visible labels first.
33 struct TargetLabelPair {
TargetLabelPair__anonf9452e240111::TargetLabelPair34   TargetLabelPair(const Target* target, const Label& default_toolchain_label)
35       : target(target),
36         label(std::make_unique<std::string>(
37             target->label().GetUserVisibleName(default_toolchain_label))) {}
38 
39   const Target* target;
40   std::unique_ptr<std::string> label;
41 
operator <__anonf9452e240111::TargetLabelPair42   bool operator<(const TargetLabelPair& other) const {
43     return *label < *other.label;
44   }
45 
46   using List = std::vector<TargetLabelPair>;
47 
48   // Create list of TargetLabelPairs sorted by their target labels.
CreateSortedList__anonf9452e240111::TargetLabelPair49   static List CreateSortedList(const MapType& outputs_map,
50                                const Label& default_toolchain_label) {
51     List result;
52     result.reserve(outputs_map.size());
53 
54     for (const auto& output_pair : outputs_map)
55       result.emplace_back(output_pair.first, default_toolchain_label);
56 
57     std::sort(result.begin(), result.end());
58     return result;
59   }
60 };
61 
62 }  // namespace
63 
64 // static
GenerateJSON(const MapType & outputs_map)65 StringOutputBuffer NinjaOutputsWriter::GenerateJSON(
66     const MapType& outputs_map) {
67   Label default_toolchain_label;
68   if (!outputs_map.empty()) {
69     default_toolchain_label =
70         outputs_map.begin()->first->settings()->default_toolchain_label();
71   }
72 
73   auto sorted_pairs =
74       TargetLabelPair::CreateSortedList(outputs_map, default_toolchain_label);
75 
76   StringOutputBuffer out;
77   out.Append('{');
78 
79   auto escape = [](std::string_view str) -> std::string {
80     std::string result;
81     base::EscapeJSONString(str, true, &result);
82     return result;
83   };
84 
85   bool first_label = true;
86   for (const auto& pair : sorted_pairs) {
87     const Target* target = pair.target;
88     const std::string& label = *pair.label;
89 
90     auto it = outputs_map.find(target);
91     CHECK(it != outputs_map.end());
92 
93     if (!first_label)
94       out.Append(',');
95     first_label = false;
96 
97     out.Append("\n  ");
98     out.Append(escape(label));
99     out.Append(": [");
100     bool first_path = true;
101     for (const auto& output : it->second) {
102       if (!first_path)
103         out.Append(',');
104       first_path = false;
105       out.Append("\n    ");
106       out.Append(escape(output.value()));
107     }
108     out.Append("\n  ]");
109   }
110 
111   out.Append("\n}");
112   return out;
113 }
114 
RunAndWriteFiles(const MapType & outputs_map,const BuildSettings * build_settings,const std::string & file_name,const std::string & exec_script,const std::string & exec_script_extra_args,bool quiet,Err * err)115 bool NinjaOutputsWriter::RunAndWriteFiles(
116     const MapType& outputs_map,
117     const BuildSettings* build_settings,
118     const std::string& file_name,
119     const std::string& exec_script,
120     const std::string& exec_script_extra_args,
121     bool quiet,
122     Err* err) {
123   SourceFile output_file = build_settings->build_dir().ResolveRelativeFile(
124       Value(nullptr, file_name), err);
125   if (output_file.is_null()) {
126     return false;
127   }
128 
129   StringOutputBuffer outputs = GenerateJSON(outputs_map);
130 
131   base::FilePath output_path = build_settings->GetFullPath(output_file);
132   if (!outputs.ContentsEqual(output_path)) {
133     if (!outputs.WriteToFile(output_path, err)) {
134       return false;
135     }
136 
137     if (!exec_script.empty()) {
138       SourceFile script_file;
139       if (exec_script[0] != '/') {
140         // Relative path, assume the base is in build_dir.
141         script_file = build_settings->build_dir().ResolveRelativeFile(
142             Value(nullptr, exec_script), err);
143         if (script_file.is_null()) {
144           return false;
145         }
146       } else {
147         script_file = SourceFile(exec_script);
148       }
149       base::FilePath script_path = build_settings->GetFullPath(script_file);
150       return internal::InvokePython(build_settings, script_path,
151                                     exec_script_extra_args, output_path, quiet,
152                                     err);
153     }
154   }
155 
156   return true;
157 }
158