|  | // 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<base::StringPiece> 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) { | 
|  | line.AppendToString(&result); | 
|  | 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 |