[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 1 | // Copyright (c) 2013 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 "tools/gn/args.h" |
| 6 | |
thakis | 3dd54710 | 2014-12-17 20:05:02 | [diff] [blame] | 7 | #include "base/sys_info.h" |
[email protected] | 145e5a1 | 2013-10-18 21:57:13 | [diff] [blame] | 8 | #include "build/build_config.h" |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 9 | #include "tools/gn/variables.h" |
| 10 | |
| 11 | const char kBuildArgs_Help[] = |
[email protected] | ea3690c | 2013-09-23 17:59:22 | [diff] [blame] | 12 | "Build Arguments Overview\n" |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 13 | "\n" |
| 14 | " Build arguments are variables passed in from outside of the build\n" |
| 15 | " that build files can query to determine how the build works.\n" |
| 16 | "\n" |
[email protected] | ea3690c | 2013-09-23 17:59:22 | [diff] [blame] | 17 | "How build arguments are set\n" |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 18 | "\n" |
| 19 | " First, system default arguments are set based on the current system.\n" |
| 20 | " The built-in arguments are:\n" |
dpranke | 74add9fc | 2015-02-19 20:21:00 | [diff] [blame] | 21 | " - host_cpu\n" |
| 22 | " - host_os\n" |
| 23 | " - current_cpu\n" |
| 24 | " - current_os\n" |
| 25 | " - target_cpu\n" |
| 26 | " - target_os\n" |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 27 | "\n" |
[email protected] | d5645f1 | 2014-05-03 04:32:19 | [diff] [blame] | 28 | " If specified, arguments from the --args command line flag are used. If\n" |
| 29 | " that flag is not specified, args from previous builds in the build\n" |
| 30 | " directory will be used (this is in the file args.gn in the build\n" |
| 31 | " directory).\n" |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 32 | "\n" |
[email protected] | d5645f1 | 2014-05-03 04:32:19 | [diff] [blame] | 33 | " Last, for targets being compiled with a non-default toolchain, the\n" |
| 34 | " toolchain overrides are applied. These are specified in the\n" |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 35 | " toolchain_args section of a toolchain definition. The use-case for\n" |
| 36 | " this is that a toolchain may be building code for a different\n" |
| 37 | " platform, and that it may want to always specify Posix, for example.\n" |
| 38 | " See \"gn help toolchain_args\" for more.\n" |
| 39 | "\n" |
[email protected] | d5645f1 | 2014-05-03 04:32:19 | [diff] [blame] | 40 | " If you specify an override for a build argument that never appears in\n" |
| 41 | " a \"declare_args\" call, a nonfatal error will be displayed.\n" |
| 42 | "\n" |
| 43 | "Examples\n" |
| 44 | "\n" |
| 45 | " gn args out/FooBar\n" |
| 46 | " Create the directory out/FooBar and open an editor. You would type\n" |
| 47 | " something like this into that file:\n" |
| 48 | " enable_doom_melon=false\n" |
| 49 | " os=\"android\"\n" |
| 50 | "\n" |
| 51 | " gn gen out/FooBar --args=\"enable_doom_melon=true os=\\\"android\\\"\"\n" |
| 52 | " This will overwrite the build directory with the given arguments.\n" |
| 53 | " (Note that the quotes inside the args command will usually need to\n" |
| 54 | " be escaped for your shell to pass through strings values.)\n" |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 55 | "\n" |
[email protected] | ea3690c | 2013-09-23 17:59:22 | [diff] [blame] | 56 | "How build arguments are used\n" |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 57 | "\n" |
| 58 | " If you want to use an argument, you use declare_args() and specify\n" |
| 59 | " default values. These default values will apply if none of the steps\n" |
| 60 | " listed in the \"How build arguments are set\" section above apply to\n" |
| 61 | " the given argument, but the defaults will not override any of these.\n" |
| 62 | "\n" |
| 63 | " Often, the root build config file will declare global arguments that\n" |
| 64 | " will be passed to all buildfiles. Individual build files can also\n" |
[email protected] | b8aa8124 | 2014-01-05 11:59:51 | [diff] [blame] | 65 | " specify arguments that apply only to those files. It is also useful\n" |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 66 | " to specify build args in an \"import\"-ed file if you want such\n" |
| 67 | " arguments to apply to multiple buildfiles.\n"; |
| 68 | |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 69 | namespace { |
| 70 | |
| 71 | // Removes all entries in |overrides| that are in |declared_overrides|. |
| 72 | void RemoveDeclaredOverrides(const Scope::KeyValueMap& declared_arguments, |
| 73 | Scope::KeyValueMap* overrides) { |
| 74 | for (Scope::KeyValueMap::iterator override = overrides->begin(); |
| 75 | override != overrides->end();) { |
| 76 | if (declared_arguments.find(override->first) == declared_arguments.end()) |
| 77 | ++override; |
| 78 | else |
| 79 | overrides->erase(override++); |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | } // namespace |
| 84 | |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 85 | Args::Args() { |
| 86 | } |
| 87 | |
[email protected] | e3730f81 | 2013-10-16 16:46:14 | [diff] [blame] | 88 | Args::Args(const Args& other) |
| 89 | : overrides_(other.overrides_), |
| 90 | all_overrides_(other.all_overrides_), |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 91 | declared_arguments_per_toolchain_( |
| 92 | other.declared_arguments_per_toolchain_) { |
[email protected] | e3730f81 | 2013-10-16 16:46:14 | [diff] [blame] | 93 | } |
| 94 | |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 95 | Args::~Args() { |
| 96 | } |
| 97 | |
[email protected] | e3730f81 | 2013-10-16 16:46:14 | [diff] [blame] | 98 | void Args::AddArgOverride(const char* name, const Value& value) { |
[email protected] | 0aa183b | 2014-02-06 08:23:40 | [diff] [blame] | 99 | base::AutoLock lock(lock_); |
| 100 | |
[email protected] | e3730f81 | 2013-10-16 16:46:14 | [diff] [blame] | 101 | overrides_[base::StringPiece(name)] = value; |
| 102 | all_overrides_[base::StringPiece(name)] = value; |
| 103 | } |
| 104 | |
[email protected] | 0a79fe4 | 2013-08-29 21:06:26 | [diff] [blame] | 105 | void Args::AddArgOverrides(const Scope::KeyValueMap& overrides) { |
[email protected] | 0aa183b | 2014-02-06 08:23:40 | [diff] [blame] | 106 | base::AutoLock lock(lock_); |
| 107 | |
brettw | d1033b6 | 2014-09-30 21:44:05 | [diff] [blame] | 108 | for (const auto& cur_override : overrides) { |
| 109 | overrides_[cur_override.first] = cur_override.second; |
| 110 | all_overrides_[cur_override.first] = cur_override.second; |
[email protected] | 0a79fe4 | 2013-08-29 21:06:26 | [diff] [blame] | 111 | } |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 112 | } |
| 113 | |
[email protected] | 8d039c6 | 2014-02-03 12:04:33 | [diff] [blame] | 114 | const Value* Args::GetArgOverride(const char* name) const { |
[email protected] | 0aa183b | 2014-02-06 08:23:40 | [diff] [blame] | 115 | base::AutoLock lock(lock_); |
| 116 | |
[email protected] | 8d039c6 | 2014-02-03 12:04:33 | [diff] [blame] | 117 | Scope::KeyValueMap::const_iterator found = |
| 118 | all_overrides_.find(base::StringPiece(name)); |
| 119 | if (found == all_overrides_.end()) |
tfarina | 9b636af | 2014-12-23 00:52:07 | [diff] [blame] | 120 | return nullptr; |
[email protected] | 8d039c6 | 2014-02-03 12:04:33 | [diff] [blame] | 121 | return &found->second; |
| 122 | } |
| 123 | |
[email protected] | 60fe509 | 2014-02-13 18:03:43 | [diff] [blame] | 124 | Scope::KeyValueMap Args::GetAllOverrides() const { |
| 125 | base::AutoLock lock(lock_); |
| 126 | return all_overrides_; |
| 127 | } |
| 128 | |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 129 | void Args::SetupRootScope(Scope* dest, |
| 130 | const Scope::KeyValueMap& toolchain_overrides) const { |
[email protected] | 0aa183b | 2014-02-06 08:23:40 | [diff] [blame] | 131 | base::AutoLock lock(lock_); |
| 132 | |
| 133 | SetSystemVarsLocked(dest); |
| 134 | ApplyOverridesLocked(overrides_, dest); |
| 135 | ApplyOverridesLocked(toolchain_overrides, dest); |
| 136 | SaveOverrideRecordLocked(toolchain_overrides); |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 137 | } |
| 138 | |
| 139 | bool Args::DeclareArgs(const Scope::KeyValueMap& args, |
| 140 | Scope* scope_to_set, |
| 141 | Err* err) const { |
| 142 | base::AutoLock lock(lock_); |
| 143 | |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 144 | Scope::KeyValueMap& declared_arguments( |
| 145 | DeclaredArgumentsForToolchainLocked(scope_to_set)); |
brettw | d1033b6 | 2014-09-30 21:44:05 | [diff] [blame] | 146 | for (const auto& arg : args) { |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 147 | // Verify that the value hasn't already been declared. We want each value |
| 148 | // to be declared only once. |
| 149 | // |
| 150 | // The tricky part is that a buildfile can be interpreted multiple times |
| 151 | // when used from different toolchains, so we can't just check that we've |
[email protected] | 55a3dd76 | 2014-02-18 20:24:40 | [diff] [blame] | 152 | // seen it before. Instead, we check that the location matches. |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 153 | Scope::KeyValueMap::iterator previously_declared = |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 154 | declared_arguments.find(arg.first); |
| 155 | if (previously_declared != declared_arguments.end()) { |
brettw | d1033b6 | 2014-09-30 21:44:05 | [diff] [blame] | 156 | if (previously_declared->second.origin() != arg.second.origin()) { |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 157 | // Declaration location mismatch. |
brettw | d1033b6 | 2014-09-30 21:44:05 | [diff] [blame] | 158 | *err = Err(arg.second.origin(), |
| 159 | "Duplicate build argument declaration.", |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 160 | "Here you're declaring an argument that was already declared " |
| 161 | "elsewhere.\nYou can only declare each argument once in the entire " |
| 162 | "build so there is one\ncanonical place for documentation and the " |
| 163 | "default value. Either move this\nargument to the build config " |
| 164 | "file (for visibility everywhere) or to a .gni file\nthat you " |
| 165 | "\"import\" from the files where you need it (preferred)."); |
| 166 | err->AppendSubErr(Err(previously_declared->second.origin(), |
| 167 | "Previous declaration.", |
| 168 | "See also \"gn help buildargs\" for more on how " |
[email protected] | b8aa8124 | 2014-01-05 11:59:51 | [diff] [blame] | 169 | "build arguments work.")); |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 170 | return false; |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 171 | } |
| 172 | } else { |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 173 | declared_arguments.insert(arg); |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 174 | } |
| 175 | |
| 176 | // Only set on the current scope to the new value if it hasn't been already |
[email protected] | 42b80ef | 2013-10-29 23:43:57 | [diff] [blame] | 177 | // set. Mark the variable used so the build script can override it in |
| 178 | // certain cases without getting unused value errors. |
brettw | d1033b6 | 2014-09-30 21:44:05 | [diff] [blame] | 179 | if (!scope_to_set->GetValue(arg.first)) { |
| 180 | scope_to_set->SetValue(arg.first, arg.second, arg.second.origin()); |
| 181 | scope_to_set->MarkUsed(arg.first); |
[email protected] | 42b80ef | 2013-10-29 23:43:57 | [diff] [blame] | 182 | } |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | return true; |
| 186 | } |
| 187 | |
| 188 | bool Args::VerifyAllOverridesUsed(Err* err) const { |
| 189 | base::AutoLock lock(lock_); |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 190 | Scope::KeyValueMap all_overrides(all_overrides_); |
| 191 | for (const auto& map_pair : declared_arguments_per_toolchain_) |
| 192 | RemoveDeclaredOverrides(map_pair.second, &all_overrides); |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 193 | |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 194 | if (all_overrides.empty()) |
| 195 | return true; |
[email protected] | 0aa183b | 2014-02-06 08:23:40 | [diff] [blame] | 196 | |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 197 | *err = Err( |
| 198 | all_overrides.begin()->second.origin(), "Build argument has no effect.", |
| 199 | "The variable \"" + all_overrides.begin()->first.as_string() + |
| 200 | "\" was set as a build argument\nbut never appeared in a " + |
brettw | 12985c0 | 2016-04-24 19:03:22 | [diff] [blame] | 201 | "declare_args() block in any buildfile.\n\n" |
| 202 | "To view possible args, run \"gn args --list <builddir>\""); |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 203 | return false; |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 204 | } |
| 205 | |
[email protected] | 60fe509 | 2014-02-13 18:03:43 | [diff] [blame] | 206 | void Args::MergeDeclaredArguments(Scope::KeyValueMap* dest) const { |
| 207 | base::AutoLock lock(lock_); |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 208 | for (const auto& map_pair : declared_arguments_per_toolchain_) { |
| 209 | for (const auto& arg : map_pair.second) |
| 210 | (*dest)[arg.first] = arg.second; |
| 211 | } |
[email protected] | 60fe509 | 2014-02-13 18:03:43 | [diff] [blame] | 212 | } |
| 213 | |
[email protected] | 0aa183b | 2014-02-06 08:23:40 | [diff] [blame] | 214 | void Args::SetSystemVarsLocked(Scope* dest) const { |
[email protected] | 60fe509 | 2014-02-13 18:03:43 | [diff] [blame] | 215 | lock_.AssertAcquired(); |
| 216 | |
[email protected] | 145e5a1 | 2013-10-18 21:57:13 | [diff] [blame] | 217 | // Host OS. |
tfarina | 9b636af | 2014-12-23 00:52:07 | [diff] [blame] | 218 | const char* os = nullptr; |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 219 | #if defined(OS_WIN) |
[email protected] | 145e5a1 | 2013-10-18 21:57:13 | [diff] [blame] | 220 | os = "win"; |
| 221 | #elif defined(OS_MACOSX) |
| 222 | os = "mac"; |
| 223 | #elif defined(OS_LINUX) |
| 224 | os = "linux"; |
[email protected] | a74ddbc | 2014-05-13 17:41:06 | [diff] [blame] | 225 | #elif defined(OS_ANDROID) |
| 226 | os = "android"; |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 227 | #else |
[email protected] | 145e5a1 | 2013-10-18 21:57:13 | [diff] [blame] | 228 | #error Unknown OS type. |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 229 | #endif |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 230 | |
[email protected] | 145e5a1 | 2013-10-18 21:57:13 | [diff] [blame] | 231 | // Host architecture. |
[email protected] | 777ad7f | 2013-11-22 22:38:39 | [diff] [blame] | 232 | static const char kX86[] = "x86"; |
| 233 | static const char kX64[] = "x64"; |
thakis | 3dd54710 | 2014-12-17 20:05:02 | [diff] [blame] | 234 | static const char kArm[] = "arm"; |
milko.leporis | 8ea3910c7 | 2016-04-18 07:51:18 | [diff] [blame] | 235 | static const char kMips[] = "mipsel"; |
tfarina | 9b636af | 2014-12-23 00:52:07 | [diff] [blame] | 236 | const char* arch = nullptr; |
thakis | 3dd54710 | 2014-12-17 20:05:02 | [diff] [blame] | 237 | |
dpranke | 74add9fc | 2015-02-19 20:21:00 | [diff] [blame] | 238 | // Set the host CPU architecture based on the underlying OS, not |
[email protected] | 145e5a1 | 2013-10-18 21:57:13 | [diff] [blame] | 239 | // whatever the current bit-tedness of the GN binary is. |
thakis | 3dd54710 | 2014-12-17 20:05:02 | [diff] [blame] | 240 | std::string os_arch = base::SysInfo::OperatingSystemArchitecture(); |
| 241 | if (os_arch == "x86") |
[email protected] | 777ad7f | 2013-11-22 22:38:39 | [diff] [blame] | 242 | arch = kX86; |
thakis | 3dd54710 | 2014-12-17 20:05:02 | [diff] [blame] | 243 | else if (os_arch == "x86_64") |
| 244 | arch = kX64; |
saiarcot895 | b68180a | 2016-04-28 22:25:01 | [diff] [blame^] | 245 | else if (os_arch.substr(0, 3) == "arm") |
[email protected] | 145e5a1 | 2013-10-18 21:57:13 | [diff] [blame] | 246 | arch = kArm; |
milko.leporis | 8ea3910c7 | 2016-04-18 07:51:18 | [diff] [blame] | 247 | else if (os_arch == "mips") |
| 248 | arch = kMips; |
thakis | 3dd54710 | 2014-12-17 20:05:02 | [diff] [blame] | 249 | else |
| 250 | CHECK(false) << "OS architecture not handled."; |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 251 | |
[email protected] | dbd915b4 | 2013-10-25 22:23:17 | [diff] [blame] | 252 | // Save the OS and architecture as build arguments that are implicitly |
| 253 | // declared. This is so they can be overridden in a toolchain build args |
| 254 | // override, and so that they will appear in the "gn args" output. |
dpranke | 74add9fc | 2015-02-19 20:21:00 | [diff] [blame] | 255 | Value empty_string(nullptr, std::string()); |
| 256 | |
| 257 | Value os_val(nullptr, std::string(os)); |
| 258 | dest->SetValue(variables::kHostOs, os_val, nullptr); |
| 259 | dest->SetValue(variables::kTargetOs, empty_string, nullptr); |
| 260 | dest->SetValue(variables::kCurrentOs, empty_string, nullptr); |
| 261 | |
| 262 | Value arch_val(nullptr, std::string(arch)); |
| 263 | dest->SetValue(variables::kHostCpu, arch_val, nullptr); |
| 264 | dest->SetValue(variables::kTargetCpu, empty_string, nullptr); |
| 265 | dest->SetValue(variables::kCurrentCpu, empty_string, nullptr); |
| 266 | |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 267 | Scope::KeyValueMap& declared_arguments( |
| 268 | DeclaredArgumentsForToolchainLocked(dest)); |
| 269 | declared_arguments[variables::kHostOs] = os_val; |
| 270 | declared_arguments[variables::kCurrentOs] = empty_string; |
| 271 | declared_arguments[variables::kTargetOs] = empty_string; |
| 272 | declared_arguments[variables::kHostCpu] = arch_val; |
| 273 | declared_arguments[variables::kCurrentCpu] = empty_string; |
| 274 | declared_arguments[variables::kTargetCpu] = empty_string; |
dpranke | 74add9fc | 2015-02-19 20:21:00 | [diff] [blame] | 275 | |
[email protected] | 145e5a1 | 2013-10-18 21:57:13 | [diff] [blame] | 276 | // Mark these variables used so the build config file can override them |
| 277 | // without geting a warning about overwriting an unused variable. |
dpranke | 74add9fc | 2015-02-19 20:21:00 | [diff] [blame] | 278 | dest->MarkUsed(variables::kHostCpu); |
| 279 | dest->MarkUsed(variables::kCurrentCpu); |
| 280 | dest->MarkUsed(variables::kTargetCpu); |
| 281 | dest->MarkUsed(variables::kHostOs); |
| 282 | dest->MarkUsed(variables::kCurrentOs); |
| 283 | dest->MarkUsed(variables::kTargetOs); |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 284 | } |
| 285 | |
[email protected] | 0aa183b | 2014-02-06 08:23:40 | [diff] [blame] | 286 | void Args::ApplyOverridesLocked(const Scope::KeyValueMap& values, |
| 287 | Scope* scope) const { |
[email protected] | 60fe509 | 2014-02-13 18:03:43 | [diff] [blame] | 288 | lock_.AssertAcquired(); |
brettw | d1033b6 | 2014-09-30 21:44:05 | [diff] [blame] | 289 | for (const auto& val : values) |
| 290 | scope->SetValue(val.first, val.second, val.second.origin()); |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 291 | } |
| 292 | |
[email protected] | 0aa183b | 2014-02-06 08:23:40 | [diff] [blame] | 293 | void Args::SaveOverrideRecordLocked(const Scope::KeyValueMap& values) const { |
[email protected] | 60fe509 | 2014-02-13 18:03:43 | [diff] [blame] | 294 | lock_.AssertAcquired(); |
brettw | d1033b6 | 2014-09-30 21:44:05 | [diff] [blame] | 295 | for (const auto& val : values) |
| 296 | all_overrides_[val.first] = val.second; |
[email protected] | 60749e1c | 2013-08-19 21:11:05 | [diff] [blame] | 297 | } |
sky | ca5cccef | 2015-03-16 18:06:00 | [diff] [blame] | 298 | |
| 299 | Scope::KeyValueMap& Args::DeclaredArgumentsForToolchainLocked( |
| 300 | Scope* scope) const { |
| 301 | lock_.AssertAcquired(); |
| 302 | return declared_arguments_per_toolchain_[scope->settings()]; |
| 303 | } |