1 // Copyright 2014 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/header_checker.h"
6
7 #include <algorithm>
8
9 #include "base/containers/queue.h"
10 #include "base/files/file_util.h"
11 #include "base/strings/string_util.h"
12 #include "gn/build_settings.h"
13 #include "gn/builder.h"
14 #include "gn/c_include_iterator.h"
15 #include "gn/config.h"
16 #include "gn/config_values_extractors.h"
17 #include "gn/err.h"
18 #include "gn/filesystem_utils.h"
19 #include "gn/scheduler.h"
20 #include "gn/target.h"
21 #include "gn/trace.h"
22 #include "util/worker_pool.h"
23
24 namespace {
25
26 struct PublicGeneratedPair {
PublicGeneratedPair__anon6660c8340111::PublicGeneratedPair27 PublicGeneratedPair() : is_public(false), is_generated(false) {}
28 bool is_public;
29 bool is_generated;
30 };
31
32 // This class makes InputFiles on the stack as it reads files to check. When
33 // we throw an error, the Err indicates a locatin which has a pointer to
34 // an InputFile that must persist as long as the Err does.
35 //
36 // To make this work, this function creates a clone of the InputFile managed
37 // by the InputFileManager so the error can refer to something that
38 // persists. This means that the current file contents will live as long as
39 // the program, but this is OK since we're erroring out anyway.
CreatePersistentRange(const InputFile & input_file,const LocationRange & range)40 LocationRange CreatePersistentRange(const InputFile& input_file,
41 const LocationRange& range) {
42 InputFile* clone_input_file;
43 std::vector<Token>* tokens; // Don't care about this.
44 std::unique_ptr<ParseNode>* parse_root; // Don't care about this.
45
46 g_scheduler->input_file_manager()->AddDynamicInput(
47 input_file.name(), &clone_input_file, &tokens, &parse_root);
48 clone_input_file->SetContents(input_file.contents());
49
50 return LocationRange(Location(clone_input_file, range.begin().line_number(),
51 range.begin().column_number()),
52 Location(clone_input_file, range.end().line_number(),
53 range.end().column_number()));
54 }
55
56 // Given a reverse dependency chain where the target chain[0]'s includes are
57 // being used by chain[end] and not all deps are public, returns the string
58 // describing the error.
GetDependencyChainPublicError(const HeaderChecker::Chain & chain)59 std::string GetDependencyChainPublicError(const HeaderChecker::Chain& chain) {
60 std::string ret =
61 "The target:\n " +
62 chain[chain.size() - 1].target->label().GetUserVisibleName(false) +
63 "\nis including a file from the target:\n " +
64 chain[0].target->label().GetUserVisibleName(false) + "\n";
65
66 // Invalid chains should always be 0 (no chain) or more than two
67 // (intermediate private dependencies). 1 and 2 are impossible because a
68 // target can always include headers from itself and its direct dependents.
69 DCHECK(chain.size() != 1 && chain.size() != 2);
70 if (chain.empty()) {
71 ret += "There is no dependency chain between these targets.";
72 } else {
73 // Indirect dependency chain, print the chain.
74 ret +=
75 "\nIt's usually best to depend directly on the destination target.\n"
76 "In some cases, the destination target is considered a subcomponent\n"
77 "of an intermediate target. In this case, the intermediate target\n"
78 "should depend publicly on the destination to forward the ability\n"
79 "to include headers.\n"
80 "\n"
81 "Dependency chain (there may also be others):\n";
82
83 for (int i = static_cast<int>(chain.size()) - 1; i >= 0; i--) {
84 ret.append(" " + chain[i].target->label().GetUserVisibleName(false));
85 if (i != 0) {
86 // Identify private dependencies so the user can see where in the
87 // dependency chain things went bad. Don't list this for the first link
88 // in the chain since direct dependencies are OK, and listing that as
89 // "private" may make people feel like they need to fix it.
90 if (i == static_cast<int>(chain.size()) - 1 || chain[i - 1].is_public)
91 ret.append(" -->");
92 else
93 ret.append(" --[private]-->");
94 }
95 ret.append("\n");
96 }
97 }
98 return ret;
99 }
100
101 // Returns true if the two targets have the same label not counting the
102 // toolchain.
TargetLabelsMatchExceptToolchain(const Target * a,const Target * b)103 bool TargetLabelsMatchExceptToolchain(const Target* a, const Target* b) {
104 return a->label().dir() == b->label().dir() &&
105 a->label().name() == b->label().name();
106 }
107
108 // Returns true if the target |annotation_on| includes a friend annotation
109 // that allows |is_marked_friend| as a friend.
FriendMatches(const Target * annotation_on,const Target * is_marked_friend)110 bool FriendMatches(const Target* annotation_on,
111 const Target* is_marked_friend) {
112 return LabelPattern::VectorMatches(annotation_on->friends(),
113 is_marked_friend->label());
114 }
115
116 } // namespace
117
HeaderChecker(const BuildSettings * build_settings,const std::vector<const Target * > & targets,bool check_generated,bool check_system)118 HeaderChecker::HeaderChecker(const BuildSettings* build_settings,
119 const std::vector<const Target*>& targets,
120 bool check_generated,
121 bool check_system)
122 : build_settings_(build_settings),
123 check_generated_(check_generated),
124 check_system_(check_system),
125 lock_(),
126 task_count_cv_() {
127 for (auto* target : targets)
128 AddTargetToFileMap(target, &file_map_);
129 }
130
131 HeaderChecker::~HeaderChecker() = default;
132
Run(const std::vector<const Target * > & to_check,bool force_check,std::vector<Err> * errors)133 bool HeaderChecker::Run(const std::vector<const Target*>& to_check,
134 bool force_check,
135 std::vector<Err>* errors) {
136 FileMap files_to_check;
137 for (auto* check : to_check) {
138 // This function will get called with all target types, but check only
139 // applies to binary targets.
140 if (check->IsBinary())
141 AddTargetToFileMap(check, &files_to_check);
142 }
143 RunCheckOverFiles(files_to_check, force_check);
144
145 if (errors_.empty())
146 return true;
147 *errors = errors_;
148 return false;
149 }
150
RunCheckOverFiles(const FileMap & files,bool force_check)151 void HeaderChecker::RunCheckOverFiles(const FileMap& files, bool force_check) {
152 WorkerPool pool;
153
154 for (const auto& file : files) {
155 // Only check C-like source files (RC files also have includes).
156 const SourceFile::Type type = file.first.GetType();
157 if (type != SourceFile::SOURCE_CPP && type != SourceFile::SOURCE_H &&
158 type != SourceFile::SOURCE_C && type != SourceFile::SOURCE_M &&
159 type != SourceFile::SOURCE_MM && type != SourceFile::SOURCE_RC)
160 continue;
161
162 if (!check_generated_) {
163 // If any target marks it as generated, don't check it. We have to check
164 // file_map_, which includes all known files; files only includes those
165 // being checked.
166 bool is_generated = false;
167 for (const auto& vect_i : file_map_[file.first])
168 is_generated |= vect_i.is_generated;
169 if (is_generated)
170 continue;
171 }
172
173 for (const auto& vect_i : file.second) {
174 if (vect_i.target->check_includes()) {
175 task_count_.Increment();
176 pool.PostTask([this, target = vect_i.target, file = file.first]() {
177 DoWork(target, file);
178 });
179 }
180 }
181 }
182
183 // Wait for all tasks posted by this method to complete.
184 std::unique_lock<std::mutex> auto_lock(lock_);
185 while (!task_count_.IsZero())
186 task_count_cv_.wait(auto_lock);
187 }
188
DoWork(const Target * target,const SourceFile & file)189 void HeaderChecker::DoWork(const Target* target, const SourceFile& file) {
190 std::vector<Err> errors;
191 if (!CheckFile(target, file, &errors)) {
192 std::lock_guard<std::mutex> lock(lock_);
193 errors_.insert(errors_.end(), errors.begin(), errors.end());
194 }
195
196 if (!task_count_.Decrement()) {
197 // Signal |task_count_cv_| when |task_count_| becomes zero.
198 std::unique_lock<std::mutex> auto_lock(lock_);
199 task_count_cv_.notify_one();
200 }
201 }
202
203 // static
AddTargetToFileMap(const Target * target,FileMap * dest)204 void HeaderChecker::AddTargetToFileMap(const Target* target, FileMap* dest) {
205 // Files in the sources have this public bit by default.
206 bool default_public = target->all_headers_public();
207
208 std::map<SourceFile, PublicGeneratedPair> files_to_public;
209
210 // First collect the normal files, they get the default visibility. If you
211 // depend on the compiled target, it should be enough to be able to include
212 // the header.
213 for (const auto& source : target->sources()) {
214 files_to_public[source].is_public = default_public;
215 }
216
217 // Add in the public files, forcing them to public. This may overwrite some
218 // entries, and it may add new ones.
219 if (default_public) // List only used when default is not public.
220 DCHECK(target->public_headers().empty());
221 for (const auto& source : target->public_headers()) {
222 files_to_public[source].is_public = true;
223 }
224
225 // Add in outputs from actions. These are treated as public (since if other
226 // targets can't use them, then there wouldn't be any point in outputting).
227 std::vector<SourceFile> outputs;
228 target->action_values().GetOutputsAsSourceFiles(target, &outputs);
229 for (const auto& output : outputs) {
230 PublicGeneratedPair* pair = &files_to_public[output];
231 pair->is_public = true;
232 pair->is_generated = true;
233 }
234
235 // Add the merged list to the master list of all files.
236 for (const auto& cur : files_to_public) {
237 (*dest)[cur.first].push_back(
238 TargetInfo(target, cur.second.is_public, cur.second.is_generated));
239 }
240 }
241
IsFileInOuputDir(const SourceFile & file) const242 bool HeaderChecker::IsFileInOuputDir(const SourceFile& file) const {
243 const std::string& build_dir = build_settings_->build_dir().value();
244 return file.value().compare(0, build_dir.size(), build_dir) == 0;
245 }
246
SourceFileForInclude(const IncludeStringWithLocation & include,const std::vector<SourceDir> & include_dirs,const InputFile & source_file,Err * err) const247 SourceFile HeaderChecker::SourceFileForInclude(
248 const IncludeStringWithLocation& include,
249 const std::vector<SourceDir>& include_dirs,
250 const InputFile& source_file,
251 Err* err) const {
252 using base::FilePath;
253
254 Value relative_file_value(nullptr, std::string(include.contents));
255
256 auto find_predicate = [relative_file_value, err, this](const SourceDir& dir) -> bool {
257 SourceFile include_file =
258 dir.ResolveRelativeFile(relative_file_value, err);
259 return file_map_.find(include_file) != file_map_.end();
260 };
261 if (!include.system_style_include) {
262 const SourceDir& file_dir = source_file.dir();
263 if (find_predicate(file_dir)) {
264 return file_dir.ResolveRelativeFile(relative_file_value, err);
265 }
266 }
267
268 auto it = std::find_if(
269 include_dirs.begin(), include_dirs.end(), find_predicate);
270
271 if (it != include_dirs.end())
272 return it->ResolveRelativeFile(relative_file_value, err);
273
274 return SourceFile();
275 }
276
CheckFile(const Target * from_target,const SourceFile & file,std::vector<Err> * errors) const277 bool HeaderChecker::CheckFile(const Target* from_target,
278 const SourceFile& file,
279 std::vector<Err>* errors) const {
280 ScopedTrace trace(TraceItem::TRACE_CHECK_HEADER, file.value());
281
282 // Sometimes you have generated source files included as sources in another
283 // target. These won't exist at checking time. Since we require all generated
284 // files to be somewhere in the output tree, we can just check the name to
285 // see if they should be skipped.
286 if (!check_generated_ && IsFileInOuputDir(file))
287 return true;
288
289 base::FilePath path = build_settings_->GetFullPath(file);
290 std::string contents;
291 if (!base::ReadFileToString(path, &contents)) {
292 // A missing (not yet) generated file is an acceptable problem
293 // considering this code does not understand conditional includes.
294 if (IsFileInOuputDir(file))
295 return true;
296
297 errors->emplace_back(from_target->defined_from(), "Source file not found.",
298 "The target:\n " +
299 from_target->label().GetUserVisibleName(false) +
300 "\nhas a source file:\n " + file.value() +
301 "\nwhich was not found.");
302 return false;
303 }
304
305 InputFile input_file(file);
306 input_file.SetContents(contents);
307
308 std::vector<SourceDir> include_dirs;
309 for (ConfigValuesIterator iter(from_target); !iter.done(); iter.Next()) {
310 const std::vector<SourceDir>& target_include_dirs =
311 iter.cur().include_dirs();
312 include_dirs.insert(include_dirs.end(), target_include_dirs.begin(),
313 target_include_dirs.end());
314 }
315
316 size_t error_count_before = errors->size();
317 CIncludeIterator iter(&input_file);
318
319 IncludeStringWithLocation include;
320
321 std::set<std::pair<const Target*, const Target*>> no_dependency_cache;
322
323 while (iter.GetNextIncludeString(&include)) {
324 if (include.system_style_include && !check_system_)
325 continue;
326
327 Err err;
328 SourceFile included_file = SourceFileForInclude(include,
329 include_dirs,
330 input_file,
331 &err);
332 if (!included_file.is_null()) {
333 CheckInclude(from_target, input_file, included_file, include.location,
334 &no_dependency_cache, errors);
335 }
336 }
337
338 return errors->size() == error_count_before;
339 }
340
341 // If the file exists:
342 // - The header must be in the public section of a target, or it must
343 // be in the sources with no public list (everything is implicitly public).
344 // - The dependency path to the included target must follow only public_deps.
345 // - If there are multiple targets with the header in it, only one need be
346 // valid for the check to pass.
CheckInclude(const Target * from_target,const InputFile & source_file,const SourceFile & include_file,const LocationRange & range,std::set<std::pair<const Target *,const Target * >> * no_dependency_cache,std::vector<Err> * errors) const347 void HeaderChecker::CheckInclude(
348 const Target* from_target,
349 const InputFile& source_file,
350 const SourceFile& include_file,
351 const LocationRange& range,
352 std::set<std::pair<const Target*, const Target*>>* no_dependency_cache,
353 std::vector<Err>* errors) const {
354 // Assume if the file isn't declared in our sources that we don't need to
355 // check it. It would be nice if we could give an error if this happens, but
356 // our include finder is too primitive and returns all includes, even if
357 // they're in a #if not executed in the current build. In that case, it's
358 // not unusual for the buildfiles to not specify that header at all.
359 FileMap::const_iterator found = file_map_.find(include_file);
360 if (found == file_map_.end())
361 return;
362
363 const TargetVector& targets = found->second;
364 Chain chain; // Prevent reallocating in the loop.
365
366 // If the file is unknown in the current toolchain (rather than being private
367 // or in a target not visible to the current target), ignore it. This is a
368 // bit of a hack to account for the fact that the include finder doesn't
369 // understand the preprocessor.
370 //
371 // When not cross-compiling, if a platform specific header is conditionally
372 // included in the build, and preprocessor conditions around #includes of
373 // that match the build conditions, everything will be OK because the file
374 // won't be known to GN even though the #include finder identified the file.
375 //
376 // Cross-compiling breaks this. When compiling Android on Linux, for example,
377 // we might see both Linux and Android definitions of a target and know
378 // about the union of all headers in the build. Since the #include finder
379 // ignores preprocessor, we will find the Linux headers in the Android
380 // build and note that a dependency from the Android target to the Linux
381 // one is missing (these might even be the same target in different
382 // toolchains!).
383 bool present_in_current_toolchain = false;
384 for (const auto& target : targets) {
385 if (from_target->label().ToolchainsEqual(target.target->label())) {
386 present_in_current_toolchain = true;
387 break;
388 }
389 }
390 if (!present_in_current_toolchain)
391 return;
392
393 // For all targets containing this file, we require that at least one be
394 // a direct or public dependency of the current target, and either (1) the
395 // header is public within the target, or (2) there is a friend definition
396 // whitelisting the includor.
397 //
398 // If there is more than one target containing this header, we may encounter
399 // some error cases before finding a good one. This error stores the previous
400 // one encountered, which we may or may not throw away.
401 Err last_error;
402
403 bool found_dependency = false;
404 for (const auto& target : targets) {
405 // We always allow source files in a target to include headers also in that
406 // target.
407 const Target* to_target = target.target;
408 if (to_target == from_target)
409 return;
410
411 bool is_permitted_chain = false;
412
413 bool cached_no_dependency =
414 no_dependency_cache->find(std::make_pair(to_target, from_target)) !=
415 no_dependency_cache->end();
416
417 bool add_to_cache = !cached_no_dependency;
418
419 if (!cached_no_dependency &&
420 IsDependencyOf(to_target, from_target, &chain, &is_permitted_chain)) {
421 add_to_cache = false;
422
423 DCHECK(chain.size() >= 2);
424 DCHECK(chain[0].target == to_target);
425 DCHECK(chain[chain.size() - 1].target == from_target);
426
427 found_dependency = true;
428
429 bool effectively_public =
430 target.is_public || FriendMatches(to_target, from_target);
431
432 if (effectively_public && is_permitted_chain) {
433 // This one is OK, we're done.
434 last_error = Err();
435 break;
436 }
437
438 // Diagnose the error.
439 if (!effectively_public) {
440 // Danger: must call CreatePersistentRange to put in Err.
441 last_error = Err(CreatePersistentRange(source_file, range),
442 "Including a private header.",
443 "This file is private to the target " +
444 target.target->label().GetUserVisibleName(false));
445 } else if (!is_permitted_chain) {
446 last_error = Err(CreatePersistentRange(source_file, range),
447 "Can't include this header from here.",
448 GetDependencyChainPublicError(chain));
449 } else {
450 NOTREACHED();
451 }
452 } else if (to_target->allow_circular_includes_from().find(
453 from_target->label()) !=
454 to_target->allow_circular_includes_from().end()) {
455 // Not a dependency, but this include is whitelisted from the destination.
456 found_dependency = true;
457 last_error = Err();
458 break;
459 }
460
461 if (add_to_cache) {
462 no_dependency_cache->emplace(to_target, from_target);
463 }
464 }
465
466 if (!found_dependency || last_error.has_error()) {
467 if (!found_dependency) {
468 DCHECK(!last_error.has_error());
469 Err err = MakeUnreachableError(source_file, range, from_target, targets);
470 errors->push_back(std::move(err));
471 } else {
472 // Found at least one dependency chain above, but it had an error.
473 errors->push_back(std::move(last_error));
474 }
475 return;
476 }
477
478 // One thing we didn't check for is targets that expose their dependents
479 // headers in their own public headers.
480 //
481 // Say we have A -> B -> C. If C has public_configs, everybody getting headers
482 // from C should get the configs also or things could be out-of-sync. Above,
483 // we check for A including C's headers directly, but A could also include a
484 // header from B that in turn includes a header from C.
485 //
486 // There are two ways to solve this:
487 // - If a public header in B includes C, force B to publicly depend on C.
488 // This is possible to check, but might be super annoying because most
489 // targets (especially large leaf-node targets) don't declare
490 // public/private headers and you'll get lots of false positives.
491 //
492 // - Save the includes found in each file and actually compute the graph of
493 // includes to detect when A implicitly includes C's header. This will not
494 // have the annoying false positive problem, but is complex to write.
495 }
496
IsDependencyOf(const Target * search_for,const Target * search_from,Chain * chain,bool * is_permitted) const497 bool HeaderChecker::IsDependencyOf(const Target* search_for,
498 const Target* search_from,
499 Chain* chain,
500 bool* is_permitted) const {
501 if (search_for == search_from) {
502 // A target is always visible from itself.
503 *is_permitted = true;
504 return false;
505 }
506
507 // Find the shortest public dependency chain.
508 if (IsDependencyOf(search_for, search_from, true, chain)) {
509 *is_permitted = true;
510 return true;
511 }
512
513 // If not, try to find any dependency chain at all.
514 if (IsDependencyOf(search_for, search_from, false, chain)) {
515 *is_permitted = false;
516 return true;
517 }
518
519 *is_permitted = false;
520 return false;
521 }
522
IsDependencyOf(const Target * search_for,const Target * search_from,bool require_permitted,Chain * chain) const523 bool HeaderChecker::IsDependencyOf(const Target* search_for,
524 const Target* search_from,
525 bool require_permitted,
526 Chain* chain) const {
527 // This method conducts a breadth-first search through the dependency graph
528 // to find a shortest chain from search_from to search_for.
529 //
530 // work_queue maintains a queue of targets which need to be considered as
531 // part of this chain, in the order they were first traversed.
532 //
533 // Each time a new transitive dependency of search_from is discovered for
534 // the first time, it is added to work_queue and a "breadcrumb" is added,
535 // indicating which target it was reached from when first discovered.
536 //
537 // Once this search finds search_for, the breadcrumbs are used to reconstruct
538 // a shortest dependency chain (in reverse order) from search_from to
539 // search_for.
540
541 std::map<const Target*, ChainLink> breadcrumbs;
542 base::queue<ChainLink> work_queue;
543 work_queue.push(ChainLink(search_from, true));
544
545 bool first_time = true;
546 while (!work_queue.empty()) {
547 ChainLink cur_link = work_queue.front();
548 const Target* target = cur_link.target;
549 work_queue.pop();
550
551 if (target == search_for) {
552 // Found it! Reconstruct the chain.
553 chain->clear();
554 while (target != search_from) {
555 chain->push_back(cur_link);
556 cur_link = breadcrumbs[target];
557 target = cur_link.target;
558 }
559 chain->push_back(ChainLink(search_from, true));
560 return true;
561 }
562
563 // Always consider public dependencies as possibilities.
564 for (const auto& dep : target->public_deps()) {
565 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
566 work_queue.push(ChainLink(dep.ptr, true));
567 }
568
569 if (first_time || !require_permitted) {
570 // Consider all dependencies since all target paths are allowed, so add
571 // in private ones. Also do this the first time through the loop, since
572 // a target can include headers from its direct deps regardless of
573 // public/private-ness.
574 first_time = false;
575 for (const auto& dep : target->private_deps()) {
576 if (breadcrumbs.insert(std::make_pair(dep.ptr, cur_link)).second)
577 work_queue.push(ChainLink(dep.ptr, false));
578 }
579 }
580 }
581
582 return false;
583 }
584
MakeUnreachableError(const InputFile & source_file,const LocationRange & range,const Target * from_target,const TargetVector & targets)585 Err HeaderChecker::MakeUnreachableError(const InputFile& source_file,
586 const LocationRange& range,
587 const Target* from_target,
588 const TargetVector& targets) {
589 // Normally the toolchains will all match, but when cross-compiling, we can
590 // get targets with more than one toolchain in the list of possibilities.
591 std::vector<const Target*> targets_with_matching_toolchains;
592 std::vector<const Target*> targets_with_other_toolchains;
593 for (const TargetInfo& candidate : targets) {
594 if (candidate.target->toolchain() == from_target->toolchain())
595 targets_with_matching_toolchains.push_back(candidate.target);
596 else
597 targets_with_other_toolchains.push_back(candidate.target);
598 }
599
600 // It's common when cross-compiling to have a target with the same file in
601 // more than one toolchain. We could output all of them, but this is
602 // generally confusing to people (most end-users won't understand toolchains
603 // well).
604 //
605 // So delete any candidates in other toolchains that also appear in the same
606 // toolchain as the from_target.
607 for (int other_index = 0;
608 other_index < static_cast<int>(targets_with_other_toolchains.size());
609 other_index++) {
610 for (const Target* cur_matching : targets_with_matching_toolchains) {
611 if (TargetLabelsMatchExceptToolchain(
612 cur_matching, targets_with_other_toolchains[other_index])) {
613 // Found a duplicate, erase it.
614 targets_with_other_toolchains.erase(
615 targets_with_other_toolchains.begin() + other_index);
616 other_index--;
617 break;
618 }
619 }
620 }
621
622 // Only display toolchains on labels if they don't all match.
623 bool include_toolchain = !targets_with_other_toolchains.empty();
624
625 std::string msg = "It is not in any dependency of\n " +
626 from_target->label().GetUserVisibleName(include_toolchain);
627 msg += "\nThe include file is in the target(s):\n";
628 for (auto* target : targets_with_matching_toolchains)
629 msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n";
630 for (auto* target : targets_with_other_toolchains)
631 msg += " " + target->label().GetUserVisibleName(include_toolchain) + "\n";
632 if (targets_with_other_toolchains.size() +
633 targets_with_matching_toolchains.size() >
634 1)
635 msg += "at least one of ";
636 msg += "which should somehow be reachable.";
637
638 // Danger: must call CreatePersistentRange to put in Err.
639 return Err(CreatePersistentRange(source_file, range), "Include not allowed.",
640 msg);
641 }
642