Merge pull request #1789 from neheb/if

[clang-tidy] fix small false positive
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 007c662..b0c0911 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,7 @@
 cmake_minimum_required(VERSION 3.15)
+
+include(CheckIncludeFileCXX)
+
 project(ninja)
 
 # --- optional link-time optimization
@@ -39,15 +42,39 @@
 			COMMAND ${RE2C} -b -i --no-generation-date -o ${OUT} ${IN}
 		)
 	endfunction()
-	re2c(${CMAKE_SOURCE_DIR}/src/depfile_parser.in.cc ${CMAKE_BINARY_DIR}/depfile_parser.cc)
-	re2c(${CMAKE_SOURCE_DIR}/src/lexer.in.cc ${CMAKE_BINARY_DIR}/lexer.cc)
-	add_library(libninja-re2c OBJECT ${CMAKE_BINARY_DIR}/depfile_parser.cc ${CMAKE_BINARY_DIR}/lexer.cc)
+	re2c(${PROJECT_SOURCE_DIR}/src/depfile_parser.in.cc ${PROJECT_BINARY_DIR}/depfile_parser.cc)
+	re2c(${PROJECT_SOURCE_DIR}/src/lexer.in.cc ${PROJECT_BINARY_DIR}/lexer.cc)
+	add_library(libninja-re2c OBJECT ${PROJECT_BINARY_DIR}/depfile_parser.cc ${PROJECT_BINARY_DIR}/lexer.cc)
 else()
 	message(WARNING "re2c was not found; changes to src/*.in.cc will not affect your build.")
 	add_library(libninja-re2c OBJECT src/depfile_parser.cc src/lexer.cc)
 endif()
 target_include_directories(libninja-re2c PRIVATE src)
 
+# --- Check for 'browse' mode support
+function(check_platform_supports_browse_mode RESULT)
+	# Make sure the inline.sh script works on this platform.
+	# It uses the shell commands such as 'od', which may not be available.
+	execute_process(
+		COMMAND sh -c "echo 'TEST' | src/inline.sh var"
+		WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+		RESULT_VARIABLE inline_result
+		OUTPUT_QUIET
+		ERROR_QUIET
+	)
+	if(NOT inline_result EQUAL "0")
+		# The inline script failed, so browse mode is not supported.
+		set(${RESULT} "0" PARENT_SCOPE)
+		return()
+	endif()
+
+	# Now check availability of the unistd header
+	check_include_file_cxx(unistd.h PLATFORM_HAS_UNISTD_HEADER)
+	set(${RESULT} "${PLATFORM_HAS_UNISTD_HEADER}" PARENT_SCOPE)
+endfunction()
+
+check_platform_supports_browse_mode(platform_supports_ninja_browse)
+
 # Core source files all build into ninja library.
 add_library(libninja OBJECT
 	src/build_log.cc
@@ -96,6 +123,32 @@
 add_executable(ninja src/ninja.cc)
 target_link_libraries(ninja PRIVATE libninja libninja-re2c)
 
+# Adds browse mode into the ninja binary if it's supported by the host platform.
+if(platform_supports_ninja_browse)
+	# Inlines src/browse.py into the browse_py.h header, so that it can be included
+	# by src/browse.cc
+	add_custom_command(
+		OUTPUT build/browse_py.h
+		MAIN_DEPENDENCY src/browse.py
+		DEPENDS src/inline.sh
+		COMMAND cmake -E make_directory ${CMAKE_BINARY_DIR}/build
+		COMMAND src/inline.sh kBrowsePy
+						< src/browse.py
+						> ${CMAKE_BINARY_DIR}/build/browse_py.h
+		WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+		VERBATIM
+	)
+
+	target_compile_definitions(ninja PRIVATE NINJA_HAVE_BROWSE)
+	target_sources(ninja PRIVATE src/browse.cc)
+	set_source_files_properties(src/browse.cc
+		PROPERTIES
+			OBJECT_DEPENDS "${CMAKE_BINARY_DIR}/build/browse_py.h"
+			INCLUDE_DIRECTORIES "${CMAKE_BINARY_DIR}"
+			COMPILE_DEFINITIONS NINJA_PYTHON="python"
+	)
+endif()
+
 # Tests all build into ninja_test executable.
 add_executable(ninja_test
 	src/build_log_test.cc
diff --git a/configure.py b/configure.py
index 8eef7e6..48c4821 100755
--- a/configure.py
+++ b/configure.py
@@ -269,7 +269,7 @@
     n.variable('configure_env', config_str + '$ ')
 n.newline()
 
-CXX = configure_env.get('CXX', 'g++')
+CXX = configure_env.get('CXX', 'c++')
 objext = '.o'
 if platform.is_msvc():
     CXX = 'cl'
diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 9976ce4..e1ae083 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -899,7 +899,7 @@
 On Windows, commands are strings, so Ninja passes the `command` string
 directly to `CreateProcess`.  (In the common case of simply executing
 a compiler this means there is less overhead.)  Consequently the
-quoting rules are deterimined by the called program, which on Windows
+quoting rules are determined by the called program, which on Windows
 are usually provided by the C library.  If you need shell
 interpretation of the command (such as the use of `&&` to chain
 multiple commands), make the command execute the Windows shell by
@@ -935,7 +935,7 @@
 
 1. _Explicit dependencies_, as listed in a build line.  These are
    available as the `$in` variable in the rule.  Changes in these files
-   cause the output to be rebuilt; if these file are missing and
+   cause the output to be rebuilt; if these files are missing and
    Ninja doesn't know how to build them, the build is aborted.
 +
 This is the standard form of dependency to be used e.g. for the
diff --git a/src/build.cc b/src/build.cc
index ecb35a4..8c6ada5 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -77,12 +77,9 @@
 }  // namespace
 
 BuildStatus::BuildStatus(const BuildConfig& config)
-    : config_(config),
-      start_time_millis_(GetTimeMillis()),
-      started_edges_(0), finished_edges_(0), total_edges_(0),
-      progress_status_format_(NULL),
-      overall_rate_(), current_rate_(config.parallelism) {
-
+    : config_(config), start_time_millis_(GetTimeMillis()), started_edges_(0),
+      finished_edges_(0), total_edges_(0), progress_status_format_(NULL),
+      current_rate_(config.parallelism) {
   // Don't do anything fancy in verbose mode.
   if (config_.verbosity != BuildConfig::NORMAL)
     printer_.set_smart_terminal(false);
@@ -183,7 +180,7 @@
   // it considers a portion of the graph to be out of date.  Normally
   // this is done before the build starts, but our caller is about to
   // load a dyndep file during the build.  Doing so may generate more
-  // exlanation lines (via fprintf directly to stderr), but in an
+  // explanation lines (via fprintf directly to stderr), but in an
   // interactive console the cursor is currently at the end of a status
   // line.  Start a new line so that the first explanation does not
   // append to the status line.  After the explanations are done a
diff --git a/src/build_log.cc b/src/build_log.cc
index 98543b6..e5f179c 100644
--- a/src/build_log.cc
+++ b/src/build_log.cc
@@ -23,6 +23,7 @@
 #include "build_log.h"
 #include "disk_interface.h"
 
+#include <cassert>
 #include <errno.h>
 #include <stdlib.h>
 #include <string.h>
@@ -132,25 +133,9 @@
       return false;
   }
 
-  log_file_ = fopen(path.c_str(), "ab");
-  if (!log_file_) {
-    *err = strerror(errno);
-    return false;
-  }
-  setvbuf(log_file_, NULL, _IOLBF, BUFSIZ);
-  SetCloseOnExec(fileno(log_file_));
-
-  // Opening a file in append mode doesn't set the file pointer to the file's
-  // end on Windows. Do that explicitly.
-  fseek(log_file_, 0, SEEK_END);
-
-  if (ftell(log_file_) == 0) {
-    if (fprintf(log_file_, kFileSignature, kCurrentVersion) < 0) {
-      *err = strerror(errno);
-      return false;
-    }
-  }
-
+  assert(!log_file_);
+  log_file_path_ = path;  // we don't actually open the file right now, but will
+                          // do so on the first write attempt
   return true;
 }
 
@@ -174,6 +159,9 @@
     log_entry->end_time = end_time;
     log_entry->mtime = mtime;
 
+    if (!OpenForWriteIfNeeded()) {
+      return false;
+    }
     if (log_file_) {
       if (!WriteEntry(log_file_, *log_entry))
         return false;
@@ -186,11 +174,36 @@
 }
 
 void BuildLog::Close() {
+  OpenForWriteIfNeeded();  // create the file even if nothing has been recorded
   if (log_file_)
     fclose(log_file_);
   log_file_ = NULL;
 }
 
+bool BuildLog::OpenForWriteIfNeeded() {
+  if (log_file_path_.empty()) {
+    return true;
+  }
+  log_file_ = fopen(log_file_path_.c_str(), "ab");
+  if (!log_file_) {
+    return false;
+  }
+  setvbuf(log_file_, NULL, _IOLBF, BUFSIZ);
+  SetCloseOnExec(fileno(log_file_));
+
+  // Opening a file in append mode doesn't set the file pointer to the file's
+  // end on Windows. Do that explicitly.
+  fseek(log_file_, 0, SEEK_END);
+
+  if (ftell(log_file_) == 0) {
+    if (fprintf(log_file_, kFileSignature, kCurrentVersion) < 0) {
+      return false;
+    }
+  }
+  log_file_path_.clear();
+  return true;
+}
+
 struct LineReader {
   explicit LineReader(FILE* file)
     : file_(file), buf_end_(buf_), line_start_(buf_), line_end_(NULL) {
diff --git a/src/build_log.h b/src/build_log.h
index ebe0530..6d060d1 100644
--- a/src/build_log.h
+++ b/src/build_log.h
@@ -45,7 +45,10 @@
   BuildLog();
   ~BuildLog();
 
+  /// Prepares writing to the log file without actually opening it - that will
+  /// happen when/if it's needed
   bool OpenForWrite(const string& path, const BuildLogUser& user, string* err);
+
   bool RecordCommand(Edge* edge, int start_time, int end_time,
                      TimeStamp mtime = 0);
   void Close();
@@ -91,8 +94,13 @@
   const Entries& entries() const { return entries_; }
 
  private:
+  /// Should be called before using log_file_. When false is returned, errno
+  /// will be set.
+  bool OpenForWriteIfNeeded();
+
   Entries entries_;
   FILE* log_file_;
+  std::string log_file_path_;
   bool needs_recompaction_;
 };
 
diff --git a/src/clean.cc b/src/clean.cc
index ec6e7d7..55d7277 100644
--- a/src/clean.cc
+++ b/src/clean.cc
@@ -28,8 +28,6 @@
   : state_(state),
     config_(config),
     dyndep_loader_(state, disk_interface),
-    removed_(),
-    cleaned_(),
     cleaned_files_count_(0),
     disk_interface_(disk_interface),
     status_(0) {
diff --git a/src/deps_log.cc b/src/deps_log.cc
index 59a1956..1fb65ae 100644
--- a/src/deps_log.cc
+++ b/src/deps_log.cc
@@ -49,34 +49,9 @@
       return false;
   }
 
-  file_ = fopen(path.c_str(), "ab");
-  if (!file_) {
-    *err = strerror(errno);
-    return false;
-  }
-  // Set the buffer size to this and flush the file buffer after every record
-  // to make sure records aren't written partially.
-  setvbuf(file_, NULL, _IOFBF, kMaxRecordSize + 1);
-  SetCloseOnExec(fileno(file_));
-
-  // Opening a file in append mode doesn't set the file pointer to the file's
-  // end on Windows. Do that explicitly.
-  fseek(file_, 0, SEEK_END);
-
-  if (ftell(file_) == 0) {
-    if (fwrite(kFileSignature, sizeof(kFileSignature) - 1, 1, file_) < 1) {
-      *err = strerror(errno);
-      return false;
-    }
-    if (fwrite(&kCurrentVersion, 4, 1, file_) < 1) {
-      *err = strerror(errno);
-      return false;
-    }
-  }
-  if (fflush(file_) != 0) {
-    *err = strerror(errno);
-    return false;
-  }
+  assert(!file_);
+  file_path_ = path;  // we don't actually open the file right now, but will do
+                      // so on the first write attempt
   return true;
 }
 
@@ -132,6 +107,10 @@
     errno = ERANGE;
     return false;
   }
+
+  if (!OpenForWriteIfNeeded()) {
+    return false;
+  }
   size |= 0x80000000;  // Deps record: set high bit.
   if (fwrite(&size, 4, 1, file_) < 1)
     return false;
@@ -162,6 +141,7 @@
 }
 
 void DepsLog::Close() {
+  OpenForWriteIfNeeded();  // create the file even if nothing has been recorded
   if (file_)
     fclose(file_);
   file_ = NULL;
@@ -396,6 +376,10 @@
     errno = ERANGE;
     return false;
   }
+
+  if (!OpenForWriteIfNeeded()) {
+    return false;
+  }
   if (fwrite(&size, 4, 1, file_) < 1)
     return false;
   if (fwrite(node->path().data(), path_size, 1, file_) < 1) {
@@ -416,3 +400,35 @@
 
   return true;
 }
+
+bool DepsLog::OpenForWriteIfNeeded() {
+  if (file_path_.empty()) {
+    return true;
+  }
+  file_ = fopen(file_path_.c_str(), "ab");
+  if (!file_) {
+    return false;
+  }
+  // Set the buffer size to this and flush the file buffer after every record
+  // to make sure records aren't written partially.
+  setvbuf(file_, NULL, _IOFBF, kMaxRecordSize + 1);
+  SetCloseOnExec(fileno(file_));
+
+  // Opening a file in append mode doesn't set the file pointer to the file's
+  // end on Windows. Do that explicitly.
+  fseek(file_, 0, SEEK_END);
+
+  if (ftell(file_) == 0) {
+    if (fwrite(kFileSignature, sizeof(kFileSignature) - 1, 1, file_) < 1) {
+      return false;
+    }
+    if (fwrite(&kCurrentVersion, 4, 1, file_) < 1) {
+      return false;
+    }
+  }
+  if (fflush(file_) != 0) {
+    return false;
+  }
+  file_path_.clear();
+  return true;
+}
diff --git a/src/deps_log.h b/src/deps_log.h
index e7974a1..c4ada8b 100644
--- a/src/deps_log.h
+++ b/src/deps_log.h
@@ -110,8 +110,13 @@
   // Write a node name record, assigning it an id.
   bool RecordId(Node* node);
 
+  /// Should be called before using file_. When false is returned, errno will
+  /// be set.
+  bool OpenForWriteIfNeeded();
+
   bool needs_recompaction_;
   FILE* file_;
+  std::string file_path_;
 
   /// Maps id -> Node.
   vector<Node*> nodes_;
diff --git a/src/disk_interface.cc b/src/disk_interface.cc
index dc297c4..594bc51 100644
--- a/src/disk_interface.cc
+++ b/src/disk_interface.cc
@@ -26,6 +26,8 @@
 #include <sstream>
 #include <windows.h>
 #include <direct.h>  // _mkdir
+#else
+#include <unistd.h>
 #endif
 
 #include "metrics.h"
diff --git a/src/inline.sh b/src/inline.sh
index b64e8ca..5092fa2 100755
--- a/src/inline.sh
+++ b/src/inline.sh
@@ -19,7 +19,14 @@
 # stdin and writes stdout.
 
 varname="$1"
-echo "const char $varname[] ="
-od -t x1 -A n -v | sed -e 's|^[\t ]\{0,\}$||g; s|[\t ]\{1,\}| |g; s| \{1,\}$||g; s| |\\x|g; s|^|"|; s|$|"|'
-echo ";"
 
+# 'od' and 'sed' may not be available on all platforms, and may not support the
+# flags used here. We must ensure that the script exits with a non-zero exit
+# code in those cases.
+byte_vals=$(od -t x1 -A n -v) || exit 1
+escaped_byte_vals=$(echo "${byte_vals}" \
+  | sed -e 's|^[\t ]\{0,\}$||g; s|[\t ]\{1,\}| |g; s| \{1,\}$||g; s| |\\x|g; s|^|"|; s|$|"|') \
+  || exit 1
+
+# Only write output once we have successfully generated the required data
+printf "const char %s[] = \n%s;" "${varname}" "${escaped_byte_vals}"
diff --git a/src/ninja.cc b/src/ninja.cc
index 1429639..00e3a5c 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -91,7 +91,7 @@
   /// Loaded state (rules, nodes).
   State state_;
 
-  /// Functions for accesssing the disk.
+  /// Functions for accessing the disk.
   RealDiskInterface disk_interface_;
 
   /// The build directory, used for storing the build log etc.
@@ -1050,7 +1050,7 @@
   }
 }
 
-/// Set a warning flag.  Returns false if Ninja should exit instead  of
+/// Set a warning flag.  Returns false if Ninja should exit instead of
 /// continuing.
 bool WarningEnable(const string& name, Options* options) {
   if (name == "list") {
diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc
index fc5543e..74785d1 100644
--- a/src/subprocess-posix.cc
+++ b/src/subprocess-posix.cc
@@ -18,13 +18,18 @@
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <poll.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/wait.h>
 #include <spawn.h>
 
+#if defined(USE_PPOLL)
+#include <poll.h>
+#else
+#include <sys/select.h>
+#endif
+
 extern char** environ;
 
 #include "util.h"
diff --git a/src/version.cc b/src/version.cc
index 74e1213..9d27e87 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -18,7 +18,7 @@
 
 #include "util.h"
 
-const char* kNinjaVersion = "1.10.0.git";
+const char* kNinjaVersion = "1.10.1.git";
 
 void ParseVersion(const string& version, int* major, int* minor) {
   size_t end = version.find('.');