forked from ninja-build/ninja
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Enable support for concatenated depfiles (depfile_group)
- Loading branch information
Showing
14 changed files
with
612 additions
and
67 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -518,6 +518,51 @@ TEST_F(BuildTest, DepFileParseError) { | |
err); | ||
} | ||
|
||
// Make sure a depfile_group attribute is correctly used | ||
TEST_F(BuildTest, DepFileGroup) { | ||
string err; | ||
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, | ||
"rule cc\n" | ||
" command = cc $in\n " | ||
" depfile = $out.d\n" | ||
" depfile_group = group.D\n" | ||
"build foo.o: cc foo.c\n")); | ||
fs_.Create("foo.c", now_, ""); | ||
fs_.Create("foo.o.d", now_, "foo.o: old and wrong dependencies\n"); | ||
|
||
// The .D file is more recent than the .d file | ||
now_++; | ||
fs_.Create("group.D", now_, "foo.o: corect dependencies\n"); | ||
|
||
// dependency information from foo.o.d is used | ||
EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); | ||
ASSERT_EQ("", err); | ||
ASSERT_EQ(1u, fs_.files_read_.size()); | ||
EXPECT_EQ("group.D", fs_.files_read_[0]); | ||
} | ||
|
||
// Make sure a depfile_group file is not used, if it is out of date | ||
TEST_F(BuildTest, DepFileFallback) { | ||
string err; | ||
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, | ||
"rule cc\n command = cc $in\n " | ||
" depfile = $out.d\n" | ||
" depfile_group = fallback.D\n" | ||
"build foo.o: cc foo.c\n")); | ||
fs_.Create("foo.c", now_, ""); | ||
fs_.Create("fallback.D", now_, "foo.o: old and wrong dependencies\n"); | ||
|
||
// The actual .o file is more recent than the .D file | ||
now_++; | ||
fs_.Create("foo.o", now_, "foo.o: corect dependencies\n"); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
PetrWolf
Author
Owner
|
||
|
||
// dependency information from foo.o.d is used | ||
EXPECT_TRUE(builder_.AddTarget("foo.o", &err)); | ||
ASSERT_EQ("", err); | ||
ASSERT_EQ(1u, fs_.files_read_.size()); | ||
EXPECT_EQ("foo.o.d", fs_.files_read_[0]); | ||
} | ||
|
||
TEST_F(BuildTest, OrderOnlyDeps) { | ||
string err; | ||
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
// Copyright 2012 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. | ||
|
||
#include <sstream> | ||
#include "depfile_reader.h" | ||
|
||
// A static cache for already parsed, but not yet used depfiles | ||
DepfileReader::DepfileCache DepfileReader::cache; | ||
|
||
DepfileReader::~DepfileReader() { | ||
delete contents_; | ||
delete parser_; | ||
} | ||
|
||
bool DepfileReader::Init(const string& contents, string* error) { | ||
// init and take a private copy of the string | ||
contents_ = new string(contents); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong. |
||
if (contents_->empty()) | ||
return true; | ||
|
||
parser_ = new DepfileParser(); | ||
if (!parser_->Parse(contents_, error)) { | ||
return false; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
// Method to open, read and divide and aggregated depfile, saving its individual | ||
// components into internal cache of DepFileReader | ||
bool DepfileReader::loadIntoCache(DiskInterface* disk_interface, | ||
const string& depfile_path, string* err) | ||
{ | ||
string contents = disk_interface->ReadFile(depfile_path, err); | ||
if (!err->empty()) | ||
return false; | ||
|
||
// create an entry in the cache | ||
DepfileCache::mapped_type & fileMap = cache[depfile_path]; | ||
|
||
// Populate it | ||
if (contents.empty()) | ||
return true; | ||
istringstream stream(contents); | ||
string line, depfile_contents; | ||
while (getline(stream, line)) { | ||
depfile_contents.append(line).append("\n"); | ||
if (line.empty() || line[line.length()-1] != '\\' || stream.eof()) { | ||
// One depfile has ended here, let's parse it and put in cache | ||
DepfileReader reader; | ||
|
||
// Parse the depfile (to get the filename) | ||
string depfile_err; | ||
if (!reader.Init(depfile_contents, &depfile_err)) { | ||
*err = depfile_path + ": " + depfile_err; | ||
return false; | ||
} | ||
|
||
// Save it in cache | ||
const string filename = reader.parser_->out().AsString(); | ||
swap(fileMap[filename], reader); | ||
|
||
// Reset | ||
depfile_contents.clear(); | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
|
||
bool DepfileReader::ReadGroup(const string& depfile_path, | ||
const string& output_name, | ||
DiskInterface * disk_interface, | ||
string* err) { | ||
DepfileCache::iterator in_cache = cache.find(depfile_path); | ||
if (cache.end() == in_cache) { | ||
// file was not yet cached -> read it | ||
loadIntoCache(disk_interface, depfile_path, err); | ||
if (!err->empty()) { | ||
return false; | ||
} | ||
|
||
in_cache = cache.find(depfile_path); | ||
} | ||
|
||
// locate the relevant part of the cached file | ||
DepfileCache::mapped_type::iterator depfile_element = in_cache->second.find(output_name); | ||
if (in_cache->second.end() == depfile_element) { | ||
// no record for the underlying .d file -> not an error (it may be a new file) | ||
return true; | ||
} | ||
|
||
// retrieve the cached contents | ||
swap(*this, depfile_element->second); | ||
|
||
// drop the element from the cache (it's only meant to be used once) | ||
in_cache->second.erase(depfile_element); | ||
|
||
return true; | ||
} | ||
|
||
bool DepfileReader::Read(const string& depfile_path, | ||
const string& output_name, | ||
DiskInterface * disk_interface, | ||
string* err) { | ||
const string contents = disk_interface->ReadFile(depfile_path, err); | ||
if (!err->empty()) | ||
return false; | ||
|
||
if (contents.empty()) | ||
return true; | ||
|
||
// Save and parse the file | ||
string depfile_err; | ||
if (!Init(contents, &depfile_err)) { | ||
*err = depfile_path + ": " + depfile_err; | ||
return false; | ||
} | ||
|
||
// Check that this depfile matches our output. | ||
StringPiece opath = StringPiece(output_name); | ||
if (opath != parser_->out()) { | ||
*err = "expected depfile '" + depfile_path + "' to mention '" + | ||
output_name + "', got '" + parser_->out().AsString() + "'"; | ||
return false; | ||
} | ||
|
||
return true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// 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. | ||
|
||
#ifndef NINJA_DEPFILE_READER_H_ | ||
#define NINJA_DEPFILE_READER_H_ | ||
|
||
#include <string> | ||
#include <map> | ||
#include "disk_interface.h" | ||
#include "depfile_parser.h" | ||
|
||
using namespace std; | ||
|
||
// Holds a DepfileParser together with its associated data. | ||
// In the case of grouped depfiles, uses internal cache to store | ||
// depfiles which have been read, but not used yet | ||
struct DepfileReader { | ||
DepfileReader() : contents_(NULL), parser_(NULL) {}; | ||
~DepfileReader(); | ||
|
||
// Read a depfile from disk and parse it | ||
bool Read(const string& depfile_path, | ||
const string& output_name, | ||
DiskInterface * disk_interface, | ||
string* err); | ||
|
||
// Read/retrieve from cache a part of a grouped depfile | ||
// associated with the given output and parse it. | ||
bool ReadGroup(const string& depfile_path, | ||
const string& output_name, | ||
DiskInterface * disk_interface, | ||
string* err); | ||
|
||
inline DepfileParser * Parser() { | ||
return parser_; | ||
} | ||
|
||
private: | ||
typedef map<string, map<string, DepfileReader> > DepfileCache; | ||
static DepfileCache cache; | ||
static bool loadIntoCache(DiskInterface* disk_interface, const string& depfile_path, string* err); | ||
bool Init(const string& contents, string* error); | ||
|
||
friend struct DepfileReaderTest; // needs access to cache | ||
|
||
string* contents_; | ||
DepfileParser* parser_; | ||
}; | ||
|
||
#endif // NINJA_DEPFILE_READER_H_ |
Oops, something went wrong.
Shouldn't it be "foo.o.d"?
And if it should, why the tests works?