Merge pull request #1473 from zdave/query-term-stdout
Query terminal width from STDOUT_FILENO
diff --git a/.gitignore b/.gitignore
index a86205b..11150c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,3 +32,6 @@
# Ninja output
.ninja_deps
.ninja_log
+
+# Visual Studio Code project files
+/.vscode/
diff --git a/.travis.yml b/.travis.yml
index 093139b..70b1fd0 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,4 +3,9 @@
compiler:
- gcc
- clang
-script: ./configure.py --bootstrap && ./ninja all && ./ninja_test --gtest_filter=-SubprocessTest.SetWithLots && ./misc/ninja_syntax_test.py
+script:
+ - ./configure.py --bootstrap
+ - ./ninja all
+ - ./ninja_test --gtest_filter=-SubprocessTest.SetWithLots
+ - ./misc/ninja_syntax_test.py
+ - ./misc/output_test.py
diff --git a/HACKING.md b/HACKING.md
index 9198bdf..5c2469b 100644
--- a/HACKING.md
+++ b/HACKING.md
@@ -19,8 +19,44 @@
On Windows, you'll need to install Python to run `configure.py`, and
run everything under a Visual Studio Tools Command Prompt (or after
-running `vcvarsall` in a normal command prompt). See below if you
-want to use mingw or some other compiler instead of Visual Studio.
+running `vcvarsall` in a normal command prompt).
+
+For other combinations such as gcc/clang you will need the compiler
+(gcc/cl) in your PATH and you will have to set the appropriate
+platform configuration script.
+
+See below if you want to use mingw or some other compiler instead of
+Visual Studio.
+
+##### Using Visual Studio
+Assuming that you now have python installed, then the steps for building under
+ Windows using Visual Studio are:
+
+Clone and checkout the latest release (or whatever branch you want). You
+can do this in either a command prompt or by opening a git bash prompt:
+
+```
+ $ git clone git://github.com/ninja-build/ninja.git && cd ninja
+ $ git checkout release
+```
+
+Then:
+
+1. Open a Windows command prompt in the folder where you checked out ninja.
+2. Select the Microsoft build environment by running
+`vcvarsall.bat` with the appropriate environment.
+3. Build ninja and test it.
+
+The steps for a Visual Studio 2015 64-bit build are outlined here:
+
+```
+ > "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
+ > python configure.py --bootstrap
+ > ninja --help
+```
+Copy the ninja executable to another location, if desired, e.g. C:\local\Ninja.
+
+Finally add the path where ninja.exe is to the PATH variable.
### Adjusting build flags
@@ -95,7 +131,7 @@
Generally it's the [Google C++ coding style][], but in brief:
* Function name are camelcase.
-* Member methods are camelcase, expect for trivial getters which are
+* Member methods are camelcase, except for trivial getters which are
underscore separated.
* Local variables are underscore separated.
* Member variables are underscore separated and suffixed by an extra
diff --git a/appveyor.yml b/appveyor.yml
index c8e1a9d..4c64f29 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,14 +1,40 @@
version: 1.0.{build}
image: Visual Studio 2017
-build_script:
-- cmd: >-
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
- python configure.py --bootstrap
+environment:
+ CLICOLOR_FORCE: 1
+ CHERE_INVOKING: 1 # Tell Bash to inherit the current working directory
+ matrix:
+ - MSYSTEM: MINGW64
+ - MSYSTEM: MSVC
- ninja.bootstrap.exe all
+for:
+ -
+ matrix:
+ only:
+ - MSYSTEM: MINGW64
+ build_script:
+ ps: "C:\\msys64\\usr\\bin\\bash -lc @\"\n
+ pacman -S --quiet --noconfirm --needed re2c 2>&1\n
+ sed -i 's|cmd /c $ar cqs $out.tmp $in && move /Y $out.tmp $out|$ar crs $out $in|g' configure.py\n
+ ./configure.py --bootstrap --platform mingw 2>&1\n
+ ./ninja all\n
+ ./ninja_test 2>&1\n
+ ./misc/ninja_syntax_test.py 2>&1\n\"@"
+ -
+ matrix:
+ only:
+ - MSYSTEM: MSVC
+ build_script:
+ - cmd: >-
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
- ninja_test
+ python configure.py --bootstrap
- python misc/ninja_syntax_test.py
+ ninja.bootstrap.exe all
+
+ ninja_test
+
+ python misc/ninja_syntax_test.py
+
test: off
diff --git a/configure.py b/configure.py
index 9e23a5a..cfe27f2 100755
--- a/configure.py
+++ b/configure.py
@@ -98,7 +98,7 @@
return self._platform == 'aix'
def uses_usr_local(self):
- return self._platform in ('freebsd', 'openbsd', 'bitrig', 'dragonfly')
+ return self._platform in ('freebsd', 'openbsd', 'bitrig', 'dragonfly', 'netbsd')
def supports_ppoll(self):
return self._platform in ('freebsd', 'linux', 'openbsd', 'bitrig',
@@ -356,6 +356,11 @@
if platform.uses_usr_local():
cflags.append('-I/usr/local/include')
ldflags.append('-L/usr/local/lib')
+ if platform.is_aix():
+ # printf formats for int64_t, uint64_t; large file support
+ cflags.append('-D__STDC_FORMAT_MACROS')
+ cflags.append('-D_LARGE_FILES')
+
libs = []
diff --git a/misc/ninja-mode.el b/misc/ninja-mode.el
index 639e537..8b975d5 100644
--- a/misc/ninja-mode.el
+++ b/misc/ninja-mode.el
@@ -56,7 +56,7 @@
(save-excursion
(goto-char (line-end-position 0))
(or
- ;; If we're continuting the previous line, it's not a
+ ;; If we're continuing the previous line, it's not a
;; comment.
(not (eq ?$ (char-before)))
;; Except if the previous line is a comment as well, as the
diff --git a/misc/ninja_syntax_test.py b/misc/ninja_syntax_test.py
index 07e3ed3..90ff9c6 100755
--- a/misc/ninja_syntax_test.py
+++ b/misc/ninja_syntax_test.py
@@ -46,13 +46,13 @@
self.out.getvalue())
def test_comment_wrap(self):
- # Filenames shoud not be wrapped
+ # Filenames should not be wrapped
self.n.comment('Hello /usr/local/build-tools/bin')
self.assertEqual('# Hello\n# /usr/local/build-tools/bin\n',
self.out.getvalue())
def test_short_words_indented(self):
- # Test that indent is taking into acount when breaking subsequent lines.
+ # Test that indent is taking into account when breaking subsequent lines.
# The second line should not be ' to tree', as that's longer than the
# test layout width of 8.
self.n._line('line_one to tree')
diff --git a/misc/output_test.py b/misc/output_test.py
new file mode 100755
index 0000000..d19cebc
--- /dev/null
+++ b/misc/output_test.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python3
+
+"""Runs ./ninja and checks if the output is correct.
+
+In order to simulate a smart terminal it uses the 'script' command.
+"""
+
+import subprocess
+import sys
+import tempfile
+import unittest
+
+def run(build_ninja, flags='', pipe=False):
+ with tempfile.NamedTemporaryFile('w') as f:
+ f.write(build_ninja)
+ f.flush()
+ ninja_cmd = './ninja {} -f {}'.format(flags, f.name)
+ try:
+ if pipe:
+ output = subprocess.check_output([ninja_cmd], shell=True)
+ else:
+ output = subprocess.check_output(['script', '-qfec', ninja_cmd, '/dev/null'])
+ except subprocess.CalledProcessError as err:
+ sys.stdout.buffer.write(err.output)
+ raise err
+ final_output = ''
+ for line in output.decode('utf-8').splitlines(True):
+ if len(line) > 0 and line[-1] == '\r':
+ continue
+ final_output += line.replace('\r', '')
+ return final_output
+
+class Output(unittest.TestCase):
+ def test_issue_1418(self):
+ self.assertEqual(run(
+'''rule echo
+ command = sleep 0.$delay && echo $out
+ description = echo $out
+
+build a: echo
+ delay = 3
+build b: echo
+ delay = 2
+build c: echo
+ delay = 1
+'''),
+'''[1/3] echo c\x1b[K
+c
+[2/3] echo b\x1b[K
+b
+[3/3] echo a\x1b[K
+a
+''')
+
+ def test_issue_1214(self):
+ print_red = '''rule echo
+ command = printf '\x1b[31mred\x1b[0m'
+ description = echo $out
+
+build a: echo
+'''
+ # Only strip color when ninja's output is piped.
+ self.assertEqual(run(print_red),
+'''[1/1] echo a\x1b[K
+\x1b[31mred\x1b[0m
+''')
+ self.assertEqual(run(print_red, pipe=True),
+'''[1/1] echo a
+red
+''')
+ # Even in verbose mode, colors should still only be stripped when piped.
+ self.assertEqual(run(print_red, flags='-v'),
+'''[1/1] printf '\x1b[31mred\x1b[0m'
+\x1b[31mred\x1b[0m
+''')
+ self.assertEqual(run(print_red, flags='-v', pipe=True),
+'''[1/1] printf '\x1b[31mred\x1b[0m'
+red
+''')
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/misc/zsh-completion b/misc/zsh-completion
index bf23fac..4cee3b8 100644
--- a/misc/zsh-completion
+++ b/misc/zsh-completion
@@ -14,7 +14,7 @@
# limitations under the License.
# Add the following to your .zshrc to tab-complete ninja targets
-# . path/to/ninja/misc/zsh-completion
+# fpath=(path/to/ninja/misc/zsh-completion $fpath)
__get_targets() {
dir="."
diff --git a/src/build.cc b/src/build.cc
index c24d6a9..6b33024 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -16,7 +16,6 @@
#include <assert.h>
#include <errno.h>
-#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <functional>
@@ -131,18 +130,6 @@
if (!edge->use_console())
PrintStatus(edge, kEdgeFinished);
- if (printer_.is_smart_terminal()) {
- int oldest_start = INT_MAX;
- Edge* oldest = NULL;
- for (i = running_edges_.begin(); i != running_edges_.end(); i++) {
- if (i->second < oldest_start) {
- oldest_start = i->second;
- oldest = i->first;
- }
- }
- if (oldest)
- PrintStatus(oldest, kEdgeRunning);
- }
// Print the command that is spewing before printing its output.
if (!success) {
@@ -169,7 +156,7 @@
// thousands of parallel compile commands.)
// TODO: There should be a flag to disable escape code stripping.
string final_output;
- if (!printer_.is_smart_terminal())
+ if (!printer_.supports_color())
final_output = StripAnsiEscapeCodes(output);
else
final_output = output;
diff --git a/src/build.h b/src/build.h
index ac7f951..9b90e8a 100644
--- a/src/build.h
+++ b/src/build.h
@@ -222,7 +222,6 @@
enum EdgeStatus {
kEdgeStarted,
- kEdgeRunning,
kEdgeFinished,
};
diff --git a/src/build_log.cc b/src/build_log.cc
index c75be95..2a65f9d 100644
--- a/src/build_log.cc
+++ b/src/build_log.cc
@@ -76,11 +76,17 @@
switch (len & 7)
{
case 7: h ^= uint64_t(data[6]) << 48;
+ NINJA_FALLTHROUGH;
case 6: h ^= uint64_t(data[5]) << 40;
+ NINJA_FALLTHROUGH;
case 5: h ^= uint64_t(data[4]) << 32;
+ NINJA_FALLTHROUGH;
case 4: h ^= uint64_t(data[3]) << 24;
+ NINJA_FALLTHROUGH;
case 3: h ^= uint64_t(data[2]) << 16;
+ NINJA_FALLTHROUGH;
case 2: h ^= uint64_t(data[1]) << 8;
+ NINJA_FALLTHROUGH;
case 1: h ^= uint64_t(data[0]);
h *= m;
};
diff --git a/src/clean.cc b/src/clean.cc
index 4f31a03..ce6a575 100644
--- a/src/clean.cc
+++ b/src/clean.cc
@@ -101,6 +101,7 @@
printf("\n");
else
printf(" ");
+ fflush(stdout);
}
void Cleaner::PrintFooter() {
diff --git a/src/deps_log_test.cc b/src/deps_log_test.cc
index 89f7be1..0cdeb45 100644
--- a/src/deps_log_test.cc
+++ b/src/deps_log_test.cc
@@ -143,7 +143,7 @@
ASSERT_GT(file_size, 0);
}
- // Now reload the file, and readd the same deps.
+ // Now reload the file, and read the same deps.
{
State state;
DepsLog log;
@@ -203,7 +203,7 @@
ASSERT_GT(file_size, 0);
}
- // Now reload the file, and add slighly different deps.
+ // Now reload the file, and add slightly different deps.
int file_size_2;
{
State state;
diff --git a/src/disk_interface.cc b/src/disk_interface.cc
index 504c679..7351715 100644
--- a/src/disk_interface.cc
+++ b/src/disk_interface.cc
@@ -205,13 +205,15 @@
return ((int64_t)st.st_mtimespec.tv_sec * 1000000000LL +
st.st_mtimespec.tv_nsec);
#elif (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700 || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || \
- defined(__BIONIC__) || (defined (__SVR4) && defined (__sun)))
+ defined(__BIONIC__) || (defined (__SVR4) && defined (__sun)) || defined(__FreeBSD__))
// For glibc, see "Timestamp files" in the Notes of http://www.kernel.org/doc/man-pages/online/pages/man2/stat.2.html
// newlib, uClibc and musl follow the kernel (or Cygwin) headers and define the right macro values above.
// For bsd, see https://github.com/freebsd/freebsd/blob/master/sys/sys/stat.h and similar
// For bionic, C and POSIX API is always enabled.
// For solaris, see https://docs.oracle.com/cd/E88353_01/html/E37841/stat-2.html.
return (int64_t)st.st_mtim.tv_sec * 1000000000LL + st.st_mtim.tv_nsec;
+#elif defined(_AIX)
+ return (int64_t)st.st_mtime * 1000000000LL + st.st_mtime_n;
#else
return (int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec;
#endif
diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc
index 81aa63a..2de4f28 100644
--- a/src/disk_interface_test.cc
+++ b/src/disk_interface_test.cc
@@ -139,11 +139,13 @@
EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1);
EXPECT_EQ("", err);
+#ifndef _MSC_VER // TODO: Investigate why. Also see https://github.com/ninja-build/ninja/pull/1423
EXPECT_EQ(disk_.Stat("subdir", &err),
disk_.Stat("subdir/.", &err));
EXPECT_EQ("", err);
EXPECT_EQ(disk_.Stat("subdir", &err),
disk_.Stat("subdir/subsubdir/..", &err));
+#endif
EXPECT_EQ("", err);
EXPECT_EQ(disk_.Stat("..", &err), parent_stat_uncached);
EXPECT_EQ("", err);
diff --git a/src/hash_map.h b/src/hash_map.h
index a91aeb9..55d2c9d 100644
--- a/src/hash_map.h
+++ b/src/hash_map.h
@@ -18,6 +18,7 @@
#include <algorithm>
#include <string.h>
#include "string_piece.h"
+#include "util.h"
// MurmurHash2, by Austin Appleby
static inline
@@ -40,7 +41,9 @@
}
switch (len) {
case 3: h ^= data[2] << 16;
+ NINJA_FALLTHROUGH;
case 2: h ^= data[1] << 8;
+ NINJA_FALLTHROUGH;
case 1: h ^= data[0];
h *= m;
};
diff --git a/src/line_printer.cc b/src/line_printer.cc
index 6bf9e1b..a3a551e 100644
--- a/src/line_printer.cc
+++ b/src/line_printer.cc
@@ -41,6 +41,7 @@
CONSOLE_SCREEN_BUFFER_INFO csbi;
smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
#endif
+ supports_color_ = smart_terminal_;
}
void LinePrinter::Print(string to_print, LineType type) {
diff --git a/src/line_printer.h b/src/line_printer.h
index 55225e5..92d4dc4 100644
--- a/src/line_printer.h
+++ b/src/line_printer.h
@@ -27,6 +27,8 @@
bool is_smart_terminal() const { return smart_terminal_; }
void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
+ bool supports_color() const { return supports_color_; }
+
enum LineType {
FULL,
ELIDE
@@ -46,6 +48,9 @@
/// Whether we can do fancy terminal control codes.
bool smart_terminal_;
+ /// Whether we can use ISO 6429 (ANSI) color sequences.
+ bool supports_color_;
+
/// Whether the caret is at the beginning of a blank line.
bool have_blank_line_;
diff --git a/src/ninja.cc b/src/ninja.cc
index f9efc77..8108f21 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -154,7 +154,7 @@
// Just checking n isn't enough: If an old output is both in the build log
// and in the deps log, it will have a Node object in state_. (It will also
// have an in edge if one of its inputs is another output that's in the deps
- // log, but having a deps edge product an output thats input to another deps
+ // log, but having a deps edge product an output that's input to another deps
// edge is rare, and the first recompaction will delete all old outputs from
// the deps log, and then a second recompaction will clear the build log,
// which seems good enough for this corner case.)
diff --git a/src/state.h b/src/state.h
index 54e9dc5..6fe886c 100644
--- a/src/state.h
+++ b/src/state.h
@@ -33,7 +33,7 @@
/// Pools are scoped to a State. Edges within a State will share Pools. A Pool
/// will keep a count of the total 'weight' of the currently scheduled edges. If
/// a Plan attempts to schedule an Edge which would cause the total weight to
-/// exceed the depth of the Pool, the Pool will enque the Edge instead of
+/// exceed the depth of the Pool, the Pool will enqueue the Edge instead of
/// allowing the Plan to schedule it. The Pool will relinquish queued Edges when
/// the total scheduled weight diminishes enough (i.e. when a scheduled edge
/// completes).
diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc
index 1de22c3..fc5543e 100644
--- a/src/subprocess-posix.cc
+++ b/src/subprocess-posix.cc
@@ -14,6 +14,7 @@
#include "subprocess.h"
+#include <sys/select.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
@@ -54,21 +55,25 @@
SetCloseOnExec(fd_);
posix_spawn_file_actions_t action;
- if (posix_spawn_file_actions_init(&action) != 0)
- Fatal("posix_spawn_file_actions_init: %s", strerror(errno));
+ int err = posix_spawn_file_actions_init(&action);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_init: %s", strerror(err));
- if (posix_spawn_file_actions_addclose(&action, output_pipe[0]) != 0)
- Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno));
+ err = posix_spawn_file_actions_addclose(&action, output_pipe[0]);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_addclose: %s", strerror(err));
posix_spawnattr_t attr;
- if (posix_spawnattr_init(&attr) != 0)
- Fatal("posix_spawnattr_init: %s", strerror(errno));
+ err = posix_spawnattr_init(&attr);
+ if (err != 0)
+ Fatal("posix_spawnattr_init: %s", strerror(err));
short flags = 0;
flags |= POSIX_SPAWN_SETSIGMASK;
- if (posix_spawnattr_setsigmask(&attr, &set->old_mask_) != 0)
- Fatal("posix_spawnattr_setsigmask: %s", strerror(errno));
+ err = posix_spawnattr_setsigmask(&attr, &set->old_mask_);
+ if (err != 0)
+ Fatal("posix_spawnattr_setsigmask: %s", strerror(err));
// Signals which are set to be caught in the calling process image are set to
// default action in the new process image, so no explicit
// POSIX_SPAWN_SETSIGDEF parameter is needed.
@@ -79,17 +84,21 @@
// No need to posix_spawnattr_setpgroup(&attr, 0), it's the default.
// Open /dev/null over stdin.
- if (posix_spawn_file_actions_addopen(&action, 0, "/dev/null", O_RDONLY,
- 0) != 0) {
- Fatal("posix_spawn_file_actions_addopen: %s", strerror(errno));
+ err = posix_spawn_file_actions_addopen(&action, 0, "/dev/null", O_RDONLY,
+ 0);
+ if (err != 0) {
+ Fatal("posix_spawn_file_actions_addopen: %s", strerror(err));
}
- if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 1) != 0)
- Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
- if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 2) != 0)
- Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno));
- if (posix_spawn_file_actions_addclose(&action, output_pipe[1]) != 0)
- Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno));
+ err = posix_spawn_file_actions_adddup2(&action, output_pipe[1], 1);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_adddup2: %s", strerror(err));
+ err = posix_spawn_file_actions_adddup2(&action, output_pipe[1], 2);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_adddup2: %s", strerror(err));
+ err = posix_spawn_file_actions_addclose(&action, output_pipe[1]);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_addclose: %s", strerror(err));
// In the console case, output_pipe is still inherited by the child and
// closed when the subprocess finishes, which then notifies ninja.
}
@@ -97,18 +106,22 @@
flags |= POSIX_SPAWN_USEVFORK;
#endif
- if (posix_spawnattr_setflags(&attr, flags) != 0)
- Fatal("posix_spawnattr_setflags: %s", strerror(errno));
+ err = posix_spawnattr_setflags(&attr, flags);
+ if (err != 0)
+ Fatal("posix_spawnattr_setflags: %s", strerror(err));
const char* spawned_args[] = { "/bin/sh", "-c", command.c_str(), NULL };
- if (posix_spawn(&pid_, "/bin/sh", &action, &attr,
- const_cast<char**>(spawned_args), environ) != 0)
- Fatal("posix_spawn: %s", strerror(errno));
+ err = posix_spawn(&pid_, "/bin/sh", &action, &attr,
+ const_cast<char**>(spawned_args), environ);
+ if (err != 0)
+ Fatal("posix_spawn: %s", strerror(err));
- if (posix_spawnattr_destroy(&attr) != 0)
- Fatal("posix_spawnattr_destroy: %s", strerror(errno));
- if (posix_spawn_file_actions_destroy(&action) != 0)
- Fatal("posix_spawn_file_actions_destroy: %s", strerror(errno));
+ err = posix_spawnattr_destroy(&attr);
+ if (err != 0)
+ Fatal("posix_spawnattr_destroy: %s", strerror(err));
+ err = posix_spawn_file_actions_destroy(&action);
+ if (err != 0)
+ Fatal("posix_spawn_file_actions_destroy: %s", strerror(err));
close(output_pipe[1]);
return true;
diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc
index 0a8c206..6e487db 100644
--- a/src/subprocess_test.cc
+++ b/src/subprocess_test.cc
@@ -182,7 +182,7 @@
"cmd /c echo hi",
"cmd /c time /t",
#else
- "whoami",
+ "id -u",
"pwd",
#endif
};
diff --git a/src/test.h b/src/test.h
index 3bce8f7..6af17b3 100644
--- a/src/test.h
+++ b/src/test.h
@@ -104,7 +104,7 @@
} \
}
-// Support utilites for tests.
+// Support utilities for tests.
struct Node;
diff --git a/src/util.cc b/src/util.cc
index 61a038b..760bc23 100644
--- a/src/util.cc
+++ b/src/util.cc
@@ -197,7 +197,7 @@
case '\\':
bits |= bits_mask;
*c = '/';
- // Intentional fallthrough.
+ NINJA_FALLTHROUGH;
case '/':
bits_mask <<= 1;
}
diff --git a/src/util.h b/src/util.h
index 4ee41a5..1b4227c 100644
--- a/src/util.h
+++ b/src/util.h
@@ -34,6 +34,20 @@
/// Log a fatal message and exit.
NORETURN void Fatal(const char* msg, ...);
+// Have a generic fall-through for different versions of C/C++.
+#if defined(__cplusplus) && __cplusplus >= 201703L
+#define NINJA_FALLTHROUGH [[fallthrough]]
+#elif defined(__cplusplus) && __cplusplus >= 201103L && defined(__clang__)
+#define NINJA_FALLTHROUGH [[clang::fallthrough]]
+#elif defined(__cplusplus) && __cplusplus >= 201103L && defined(__GNUC__) && \
+ __GNUC__ >= 7
+#define NINJA_FALLTHROUGH [[gnu::fallthrough]]
+#elif defined(__GNUC__) && __GNUC__ >= 7 // gcc 7
+#define NINJA_FALLTHROUGH __attribute__ ((fallthrough))
+#else // C++11 on gcc 6, and all other cases
+#define NINJA_FALLTHROUGH
+#endif
+
/// Log a warning message.
void Warning(const char* msg, ...);