diff options
author | Jan Niklas Hasse <jhasse@bixense.com> | 2020-09-19 08:43:27 (GMT) |
---|---|---|
committer | Jan Niklas Hasse <jhasse@bixense.com> | 2020-09-19 08:43:27 (GMT) |
commit | a2464f923cdc866a597ef9097944358cba075ed0 (patch) | |
tree | b82c70ddbcbac061b053abf764f9beaa333b2c27 | |
parent | aa6c4d88bddbb998a652f7ba8292ada660240f64 (diff) | |
parent | 54959b0f2c4950d97d94c03810b3b5185be0d69e (diff) | |
download | Ninja-a2464f923cdc866a597ef9097944358cba075ed0.zip Ninja-a2464f923cdc866a597ef9097944358cba075ed0.tar.gz Ninja-a2464f923cdc866a597ef9097944358cba075ed0.tar.bz2 |
Merge branch 'master' into github-actions-lint
-rw-r--r-- | .github/workflows/windows.yml | 4 | ||||
-rw-r--r-- | CMakeLists.txt | 137 | ||||
-rwxr-xr-x | configure.py | 2 | ||||
-rw-r--r-- | doc/manual.asciidoc | 4 | ||||
-rw-r--r-- | misc/ninja_syntax.py | 14 | ||||
-rw-r--r-- | src/build.cc | 16 | ||||
-rw-r--r-- | src/build_log.cc | 51 | ||||
-rw-r--r-- | src/build_log.h | 8 | ||||
-rw-r--r-- | src/build_test.cc | 2 | ||||
-rw-r--r-- | src/clean.cc | 2 | ||||
-rw-r--r-- | src/depfile_parser.cc | 87 | ||||
-rw-r--r-- | src/depfile_parser.in.cc | 23 | ||||
-rw-r--r-- | src/depfile_parser_test.cc | 35 | ||||
-rw-r--r-- | src/deps_log.cc | 74 | ||||
-rw-r--r-- | src/deps_log.h | 5 | ||||
-rw-r--r-- | src/disk_interface.cc | 2 | ||||
-rw-r--r-- | src/disk_interface_test.cc | 2 | ||||
-rwxr-xr-x | src/inline.sh | 13 | ||||
-rw-r--r-- | src/ninja.cc | 4 | ||||
-rw-r--r-- | src/string_piece_util.cc | 2 | ||||
-rw-r--r-- | src/subprocess-posix.cc | 7 | ||||
-rw-r--r-- | src/version.cc | 2 |
22 files changed, 356 insertions, 140 deletions
diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index d961582..04fc2f6 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -23,8 +23,8 @@ jobs: cmake --build build --parallel --config Release - name: Test ninja - run: ctest -vv - working-directory: build + run: .\ninja_test.exe + working-directory: build/Release - name: Create ninja archive shell: bash diff --git a/CMakeLists.txt b/CMakeLists.txt index 007c662..8e6bdd9 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 @@ if(RE2C) 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,45 +123,73 @@ endif() add_executable(ninja src/ninja.cc) target_link_libraries(ninja PRIVATE libninja libninja-re2c) -# Tests all build into ninja_test executable. -add_executable(ninja_test - src/build_log_test.cc - src/build_test.cc - src/clean_test.cc - src/clparser_test.cc - src/depfile_parser_test.cc - src/deps_log_test.cc - src/disk_interface_test.cc - src/dyndep_parser_test.cc - src/edit_distance_test.cc - src/graph_test.cc - src/lexer_test.cc - src/manifest_parser_test.cc - src/ninja_test.cc - src/state_test.cc - src/string_piece_util_test.cc - src/subprocess_test.cc - src/test.cc - src/util_test.cc -) -if(WIN32) - target_sources(ninja_test PRIVATE src/includes_normalize_test.cc src/msvc_helper_test.cc) +# 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() -target_link_libraries(ninja_test PRIVATE libninja libninja-re2c) - -foreach(perftest - build_log_perftest - canon_perftest - clparser_perftest - depfile_parser_perftest - hash_collision_bench - manifest_parser_perftest -) - add_executable(${perftest} src/${perftest}.cc) - target_link_libraries(${perftest} PRIVATE libninja libninja-re2c) -endforeach() -enable_testing() -add_test(NinjaTest ninja_test) +include(CTest) +if(BUILD_TESTING) + # Tests all build into ninja_test executable. + add_executable(ninja_test + src/build_log_test.cc + src/build_test.cc + src/clean_test.cc + src/clparser_test.cc + src/depfile_parser_test.cc + src/deps_log_test.cc + src/disk_interface_test.cc + src/dyndep_parser_test.cc + src/edit_distance_test.cc + src/graph_test.cc + src/lexer_test.cc + src/manifest_parser_test.cc + src/ninja_test.cc + src/state_test.cc + src/string_piece_util_test.cc + src/subprocess_test.cc + src/test.cc + src/util_test.cc + ) + if(WIN32) + target_sources(ninja_test PRIVATE src/includes_normalize_test.cc src/msvc_helper_test.cc) + endif() + target_link_libraries(ninja_test PRIVATE libninja libninja-re2c) + + foreach(perftest + build_log_perftest + canon_perftest + clparser_perftest + depfile_parser_perftest + hash_collision_bench + manifest_parser_perftest + ) + add_executable(${perftest} src/${perftest}.cc) + target_link_libraries(${perftest} PRIVATE libninja libninja-re2c) + endforeach() + + add_test(NinjaTest ninja_test) +endif() install(TARGETS ninja DESTINATION bin) diff --git a/configure.py b/configure.py index 8eef7e6..48c4821 100755 --- a/configure.py +++ b/configure.py @@ -269,7 +269,7 @@ if configure_env: 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 @@ set environment variables. 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 @@ There are three types of build dependencies which are subtly different. 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/misc/ninja_syntax.py b/misc/ninja_syntax.py index ebe6490..ab5c0d4 100644 --- a/misc/ninja_syntax.py +++ b/misc/ninja_syntax.py @@ -1,5 +1,19 @@ #!/usr/bin/python +# Copyright 2011 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + """Python module for generating .ninja files. Note that this is emphatically not a required piece of Ninja; it's diff --git a/src/build.cc b/src/build.cc index cd8df4e..8c6ada5 100644 --- a/src/build.cc +++ b/src/build.cc @@ -77,12 +77,9 @@ bool DryRunCommandRunner::WaitForCommand(Result* result) { } // 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 @@ void BuildStatus::BuildLoadDyndeps() { // 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 @@ -1033,7 +1030,7 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) { } if (!deps_type.empty() && !config_.dry_run) { - assert(edge->outputs_.size() >= 1 && "should have been rejected by parser"); + assert(!edge->outputs_.empty() && "should have been rejected by parser"); for (std::vector<Node*>::const_iterator o = edge->outputs_.begin(); o != edge->outputs_.end(); ++o) { TimeStamp deps_mtime = disk_interface_->Stat((*o)->path(), err); @@ -1067,8 +1064,7 @@ bool Builder::ExtractDeps(CommandRunner::Result* result, // complexity in IncludesNormalize::Relativize. deps_nodes->push_back(state_->GetNode(*i, ~0u)); } - } else - if (deps_type == "gcc") { + } else if (deps_type == "gcc") { string depfile = result->edge->GetUnescapedDepfile(); if (depfile.empty()) { *err = string("edge with deps=gcc but no depfile makes no sense"); 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 @@ bool BuildLog::OpenForWrite(const string& path, const BuildLogUser& user, 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 @@ bool BuildLog::RecordCommand(Edge* edge, int start_time, int end_time, 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 @@ bool BuildLog::RecordCommand(Edge* edge, int start_time, int end_time, } 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 @@ struct BuildLog { 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 @@ struct BuildLog { 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/build_test.cc b/src/build_test.cc index 426e825..12c3383 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -672,7 +672,7 @@ bool FakeCommandRunner::WaitForCommand(Result* result) { bool verify_active_edge_found = false; for (vector<Edge*>::iterator i = active_edges_.begin(); i != active_edges_.end(); ++i) { - if ((*i)->outputs_.size() >= 1 && + if (!(*i)->outputs_.empty() && (*i)->outputs_[0]->path() == verify_active_edge) { verify_active_edge_found = true; } 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 @@ Cleaner::Cleaner(State* state, : 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/depfile_parser.cc b/src/depfile_parser.cc index 90d4a8a..0b7dce1 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -1,4 +1,4 @@ -/* Generated by re2c 1.1.1 */ +/* Generated by re2c 1.3 */ // Copyright 2011 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -166,22 +166,23 @@ yy12: goto yy5; yy13: yych = *(yymarker = ++in); - if (yych <= 0x1F) { + if (yych <= ' ') { if (yych <= '\n') { if (yych <= 0x00) goto yy5; if (yych <= '\t') goto yy16; goto yy17; } else { if (yych == '\r') goto yy19; - goto yy16; + if (yych <= 0x1F) goto yy16; + goto yy21; } } else { - if (yych <= '#') { - if (yych <= ' ') goto yy21; - if (yych <= '"') goto yy16; - goto yy23; + if (yych <= '9') { + if (yych == '#') goto yy23; + goto yy16; } else { - if (yych == '\\') goto yy25; + if (yych <= ':') goto yy25; + if (yych == '\\') goto yy27; goto yy16; } } @@ -231,26 +232,63 @@ yy23: } yy25: yych = *++in; - if (yych <= 0x1F) { + if (yych <= '\f') { + if (yych <= 0x00) goto yy28; + if (yych <= 0x08) goto yy26; + if (yych <= '\n') goto yy28; + } else { + if (yych <= '\r') goto yy28; + if (yych == ' ') goto yy28; + } +yy26: + { + // De-escape colon sign, but preserve other leading backslashes. + // Regular expression uses lookahead to make sure that no whitespace + // nor EOF follows. In that case it'd be the : at the end of a target + int len = (int)(in - start); + if (len > 2 && out < start) + memset(out, '\\', len - 2); + out += len - 2; + *out++ = ':'; + continue; + } +yy27: + yych = *++in; + if (yych <= ' ') { if (yych <= '\n') { if (yych <= 0x00) goto yy11; if (yych <= '\t') goto yy16; goto yy11; } else { if (yych == '\r') goto yy11; - goto yy16; + if (yych <= 0x1F) goto yy16; + goto yy30; } } else { - if (yych <= '#') { - if (yych <= ' ') goto yy26; - if (yych <= '"') goto yy16; - goto yy23; + if (yych <= '9') { + if (yych == '#') goto yy23; + goto yy16; } else { - if (yych == '\\') goto yy28; + if (yych <= ':') goto yy25; + if (yych == '\\') goto yy32; goto yy16; } } -yy26: +yy28: + ++in; + { + // Backslash followed by : and whitespace. + // It is therefore normal text and not an escaped colon + int len = (int)(in - start - 1); + // Need to shift it over if we're overwriting backslashes. + if (out < start) + memmove(out, start, len); + out += len; + if (*(in - 1) == '\n') + have_newline = true; + break; + } +yy30: ++in; { // 2N backslashes plus space -> 2N backslashes, end of filename. @@ -260,24 +298,25 @@ yy26: out += len - 1; break; } -yy28: +yy32: yych = *++in; - if (yych <= 0x1F) { + if (yych <= ' ') { if (yych <= '\n') { if (yych <= 0x00) goto yy11; if (yych <= '\t') goto yy16; goto yy11; } else { if (yych == '\r') goto yy11; - goto yy16; + if (yych <= 0x1F) goto yy16; + goto yy21; } } else { - if (yych <= '#') { - if (yych <= ' ') goto yy21; - if (yych <= '"') goto yy16; - goto yy23; + if (yych <= '9') { + if (yych == '#') goto yy23; + goto yy16; } else { - if (yych == '\\') goto yy25; + if (yych <= ':') goto yy25; + if (yych == '\\') goto yy27; goto yy16; } } diff --git a/src/depfile_parser.in.cc b/src/depfile_parser.in.cc index b32b942..95b4346 100644 --- a/src/depfile_parser.in.cc +++ b/src/depfile_parser.in.cc @@ -103,6 +103,29 @@ bool DepfileParser::Parse(string* content, string* err) { *out++ = '#'; continue; } + '\\'+ ':' [\x00\x20\r\n\t] { + // Backslash followed by : and whitespace. + // It is therefore normal text and not an escaped colon + int len = (int)(in - start - 1); + // Need to shift it over if we're overwriting backslashes. + if (out < start) + memmove(out, start, len); + out += len; + if (*(in - 1) == '\n') + have_newline = true; + break; + } + '\\'+ ':' { + // De-escape colon sign, but preserve other leading backslashes. + // Regular expression uses lookahead to make sure that no whitespace + // nor EOF follows. In that case it'd be the : at the end of a target + int len = (int)(in - start); + if (len > 2 && out < start) + memset(out, '\\', len - 2); + out += len - 2; + *out++ = ':'; + continue; + } '$$' { // De-escape dollar character. *out++ = '$'; diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc index bf1a0bc..8e2cd25 100644 --- a/src/depfile_parser_test.cc +++ b/src/depfile_parser_test.cc @@ -142,6 +142,41 @@ TEST_F(DepfileParserTest, Escapes) { ASSERT_EQ(0u, parser_.ins_.size()); } +TEST_F(DepfileParserTest, EscapedColons) +{ + std::string err; + // Tests for correct parsing of depfiles produced on Windows + // by both Clang, GCC pre 10 and GCC 10 + EXPECT_TRUE(Parse( +"c\\:\\gcc\\x86_64-w64-mingw32\\include\\stddef.o: \\\n" +" c:\\gcc\\x86_64-w64-mingw32\\include\\stddef.h \n", + &err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, parser_.outs_.size()); + EXPECT_EQ("c:\\gcc\\x86_64-w64-mingw32\\include\\stddef.o", + parser_.outs_[0].AsString()); + ASSERT_EQ(1u, parser_.ins_.size()); + EXPECT_EQ("c:\\gcc\\x86_64-w64-mingw32\\include\\stddef.h", + parser_.ins_[0].AsString()); +} + +TEST_F(DepfileParserTest, EscapedTargetColon) +{ + std::string err; + EXPECT_TRUE(Parse( +"foo1\\: x\n" +"foo1\\:\n" +"foo1\\:\r\n" +"foo1\\:\t\n" +"foo1\\:", + &err)); + ASSERT_EQ("", err); + ASSERT_EQ(1u, parser_.outs_.size()); + EXPECT_EQ("foo1\\", parser_.outs_[0].AsString()); + ASSERT_EQ(1u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); +} + TEST_F(DepfileParserTest, SpecialChars) { // See filenames like istreambuf.iterator_op!= in // https://github.com/google/libcxx/tree/master/test/iterators/stream.iterators/istreambuf.iterator/ diff --git a/src/deps_log.cc b/src/deps_log.cc index cf55194..1fb65ae 100644 --- a/src/deps_log.cc +++ b/src/deps_log.cc @@ -49,34 +49,9 @@ bool DepsLog::OpenForWrite(const string& path, string* err) { 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 @@ bool DepsLog::RecordDeps(Node* node, TimeStamp mtime, 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 @@ bool DepsLog::RecordDeps(Node* node, TimeStamp mtime, } void DepsLog::Close() { + OpenForWriteIfNeeded(); // create the file even if nothing has been recorded if (file_) fclose(file_); file_ = NULL; @@ -396,10 +376,14 @@ bool DepsLog::RecordId(Node* node) { 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) { - assert(node->path().size() > 0); + assert(!node->path().empty()); return false; } if (padding && fwrite("\0\0", padding, 1, file_) < 1) @@ -416,3 +400,35 @@ bool DepsLog::RecordId(Node* node) { 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 @@ struct DepsLog { // 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/disk_interface_test.cc b/src/disk_interface_test.cc index bac515d..866d1d6 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -190,7 +190,7 @@ TEST_F(DiskInterfaceTest, ReadFile) { TEST_F(DiskInterfaceTest, MakeDirs) { string path = "path/with/double//slash/"; - EXPECT_TRUE(disk_.MakeDirs(path.c_str())); + EXPECT_TRUE(disk_.MakeDirs(path)); FILE* f = fopen((path + "a_file").c_str(), "w"); EXPECT_TRUE(f); EXPECT_EQ(0, fclose(f)); 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 @@ struct NinjaMain : public BuildLogUser { /// 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 @@ bool DebugEnable(const string& name) { } } -/// 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/string_piece_util.cc b/src/string_piece_util.cc index 8e1ecfd..69513f5 100644 --- a/src/string_piece_util.cc +++ b/src/string_piece_util.cc @@ -39,7 +39,7 @@ vector<StringPiece> SplitStringPiece(StringPiece input, char sep) { } string JoinStringPiece(const vector<StringPiece>& list, char sep) { - if (list.size() == 0){ + if (list.empty()) { return ""; } 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('.'); |