1 // Copyright 2015 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 "base/files/file_enumerator.h"
6 #include "base/files/file_path.h"
7 #include "base/files/file_util.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "gn/commands.h"
11 #include "gn/err.h"
12 #include "gn/filesystem_utils.h"
13 #include "gn/ninja_build_writer.h"
14 #include "gn/setup.h"
15
16 namespace {
17
CleanOneDir(const std::string & dir)18 bool CleanOneDir(const std::string& dir) {
19 // Deliberately leaked to avoid expensive process teardown.
20 Setup* setup = new Setup;
21 if (!setup->DoSetup(dir, false))
22 return false;
23
24 base::FilePath build_dir(setup->build_settings().GetFullPath(
25 SourceDir(setup->build_settings().build_dir().value())));
26
27 // NOTE: Not all GN builds have args.gn file hence we also check here
28 // if a build.ninja.d files exists instead.
29 base::FilePath args_gn_file = build_dir.AppendASCII("args.gn");
30 base::FilePath build_ninja_d_file = build_dir.AppendASCII("build.ninja.d");
31 if (!base::PathExists(args_gn_file) &&
32 !base::PathExists(build_ninja_d_file)) {
33 Err(Location(),
34 base::StringPrintf(
35 "%s does not look like a build directory.\n",
36 FilePathToUTF8(build_ninja_d_file.DirName().value()).c_str()))
37 .PrintToStdout();
38 return false;
39 }
40
41 // Replace existing build.ninja with just enough for ninja to call GN and
42 // regenerate ninja files.
43 if (!commands::PrepareForRegeneration(&setup->build_settings())) {
44 return false;
45 }
46
47 // Erase everything but (user-created) args.gn and the build.ninja files we
48 // just wrote.
49 const base::FilePath::CharType* remaining[]{
50 FILE_PATH_LITERAL("args.gn"),
51 FILE_PATH_LITERAL("build.ninja"),
52 FILE_PATH_LITERAL("build.ninja.d"),
53 };
54 base::FileEnumerator traversal(
55 build_dir, false,
56 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
57 for (base::FilePath current = traversal.Next(); !current.empty();
58 current = traversal.Next()) {
59 base::FilePath::StringType basename =
60 base::ToLowerASCII(current.BaseName().value());
61 if (std::none_of(std::begin(remaining), std::end(remaining),
62 [&](auto rem) { return basename == rem; })) {
63 base::DeleteFile(current, true);
64 }
65 }
66
67 return true;
68 }
69
70 } // namespace
71
72 namespace commands {
73
74 const char kClean[] = "clean";
75 const char kClean_HelpShort[] = "clean: Cleans the output directory.";
76 const char kClean_Help[] =
77 "gn clean <out_dir>...\n"
78 "\n"
79 " Deletes the contents of the output directory except for args.gn and\n"
80 " creates a Ninja build environment sufficient to regenerate the build.\n";
81
RunClean(const std::vector<std::string> & args)82 int RunClean(const std::vector<std::string>& args) {
83 if (args.empty()) {
84 Err(Location(), "Missing argument.", "Usage: \"gn clean <out_dir>...\"")
85 .PrintToStdout();
86 return 1;
87 }
88
89 for (const auto& dir : args) {
90 if (!CleanOneDir(dir))
91 return 1;
92 }
93
94 return 0;
95 }
96
97 } // namespace commands
98