| // Copyright 2015 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "base/files/file_enumerator.h" | 
 | #include "base/files/file_path.h" | 
 | #include "base/files/file_util.h" | 
 | #include "base/strings/string_split.h" | 
 | #include "base/strings/string_util.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "tools/gn/commands.h" | 
 | #include "tools/gn/err.h" | 
 | #include "tools/gn/filesystem_utils.h" | 
 | #include "tools/gn/setup.h" | 
 |  | 
 | namespace { | 
 |  | 
 | // Extracts from a build.ninja the commands to run GN. | 
 | // | 
 | // The commands to run GN are the gn rule and build.ninja build step at the top | 
 | // of the build.ninja file. We want to keep these when deleting GN builds since | 
 | // we want to preserve the command-line flags to GN. | 
 | // | 
 | // On error, returns the empty string. | 
 | std::string ExtractGNBuildCommands(const base::FilePath& build_ninja_file) { | 
 |   std::string file_contents; | 
 |   if (!base::ReadFileToString(build_ninja_file, &file_contents)) | 
 |     return std::string(); | 
 |  | 
 |   std::vector<std::string_view> lines = base::SplitStringPiece( | 
 |       file_contents, "\n", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL); | 
 |  | 
 |   std::string result; | 
 |   int num_blank_lines = 0; | 
 |   for (const auto& line : lines) { | 
 |     result.append(line); | 
 |     result.push_back('\n'); | 
 |     if (line.empty()) | 
 |       ++num_blank_lines; | 
 |     if (num_blank_lines == 3) | 
 |       break; | 
 |   } | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | namespace commands { | 
 |  | 
 | const char kClean[] = "clean"; | 
 | const char kClean_HelpShort[] = "clean: Cleans the output directory."; | 
 | const char kClean_Help[] = | 
 |     "gn clean <out_dir>\n" | 
 |     "\n" | 
 |     "  Deletes the contents of the output directory except for args.gn and\n" | 
 |     "  creates a Ninja build environment sufficient to regenerate the build.\n"; | 
 |  | 
 | int RunClean(const std::vector<std::string>& args) { | 
 |   if (args.size() != 1) { | 
 |     Err(Location(), "You're holding it wrong.", "Usage: \"gn clean <out_dir>\"") | 
 |         .PrintToStdout(); | 
 |     return 1; | 
 |   } | 
 |  | 
 |   // Deliberately leaked to avoid expensive process teardown. | 
 |   Setup* setup = new Setup; | 
 |   if (!setup->DoSetup(args[0], false)) | 
 |     return 1; | 
 |  | 
 |   base::FilePath build_dir(setup->build_settings().GetFullPath( | 
 |       SourceDir(setup->build_settings().build_dir().value()))); | 
 |  | 
 |   // NOTE: Not all GN builds have args.gn file hence we check here | 
 |   // if a build.ninja.d files exists instead. | 
 |   base::FilePath build_ninja_d_file = build_dir.AppendASCII("build.ninja.d"); | 
 |   if (!base::PathExists(build_ninja_d_file)) { | 
 |     Err(Location(), | 
 |         base::StringPrintf( | 
 |             "%s does not look like a build directory.\n", | 
 |             FilePathToUTF8(build_ninja_d_file.DirName().value()).c_str())) | 
 |         .PrintToStdout(); | 
 |     return 1; | 
 |   } | 
 |  | 
 |   // Erase everything but the args file, and write a dummy build.ninja file that | 
 |   // will automatically rerun GN the next time Ninja is run. | 
 |   base::FilePath build_ninja_file = build_dir.AppendASCII("build.ninja"); | 
 |   std::string build_commands = ExtractGNBuildCommands(build_ninja_file); | 
 |   if (build_commands.empty()) { | 
 |     // Couldn't parse the build.ninja file. | 
 |     Err(Location(), "Couldn't read build.ninja in this directory.", | 
 |         "Try running \"gn gen\" on it and then re-running \"gn clean\".") | 
 |         .PrintToStdout(); | 
 |     return 1; | 
 |   } | 
 |  | 
 |   base::FileEnumerator traversal( | 
 |       build_dir, false, | 
 |       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES); | 
 |   for (base::FilePath current = traversal.Next(); !current.empty(); | 
 |        current = traversal.Next()) { | 
 |     if (base::ToLowerASCII(current.BaseName().value()) != | 
 |         FILE_PATH_LITERAL("args.gn")) { | 
 |       base::DeleteFile(current, true); | 
 |     } | 
 |   } | 
 |  | 
 |   // Write the build.ninja file sufficiently to regenerate itself. | 
 |   if (base::WriteFile(build_ninja_file, build_commands.data(), | 
 |                       static_cast<int>(build_commands.size())) == -1) { | 
 |     Err(Location(), std::string("Failed to write build.ninja.")) | 
 |         .PrintToStdout(); | 
 |     return 1; | 
 |   } | 
 |  | 
 |   // Write a .d file for the build which references a nonexistant file. | 
 |   // This will make Ninja always mark the build as dirty. | 
 |   std::string dummy_content("build.ninja: nonexistant_file.gn\n"); | 
 |   if (base::WriteFile(build_ninja_d_file, dummy_content.data(), | 
 |                       static_cast<int>(dummy_content.size())) == -1) { | 
 |     Err(Location(), std::string("Failed to write build.ninja.d.")) | 
 |         .PrintToStdout(); | 
 |     return 1; | 
 |   } | 
 |  | 
 |   return 0; | 
 | } | 
 |  | 
 | }  // namespace commands |