Mention pools in the discussion of ninja's toplevel declarations.
help='target platform (' + '/'.join(platform_helper.platforms()) + ')',
choices=platform_helper.platforms())
parser.add_option('--force-pselect', action='store_true',
- help="ppoll() is used by default on Linux and OpenBSD, but older versions might need to use pselect instead",)
+ help="ppoll() is used by default on Linux, OpenBSD and Bitrig, but older versions might need to use pselect instead",)
(options, conf_args) = parser.parse_args()
# g++ call as well as in the later configure.py.
cflags = os.environ.get('CFLAGS', '').split()
ldflags = os.environ.get('LDFLAGS', '').split()
-if platform.is_freebsd() or platform.is_openbsd():
+if platform.is_freebsd() or platform.is_openbsd() or platform.is_bitrig():
cflags.append('-I/usr/local/include')
ldflags.append('-L/usr/local/lib')
cflags.append('-D_WIN32_WINNT=0x0501')
if options.x64:
cflags.append('-m64')
-if (platform.is_linux() or platform.is_openbsd()) and not options.force_pselect:
+if (platform.is_linux() or platform.is_openbsd() or platform.is_bitrig()) and not options.force_pselect:
cflags.append('-DUSE_PPOLL')
if options.force_pselect:
conf_args.append("--force-pselect")
help='use EXE as the Python interpreter',
default=os.path.basename(sys.executable))
parser.add_option('--force-pselect', action='store_true',
- help="ppoll() is used by default on Linux and OpenBSD, but older versions might need to use pselect instead",)
+ help="ppoll() is used by default where available, but some platforms may need to use pselect instead",)
(options, args) = parser.parse_args()
if args:
print('ERROR: extra unparsed command-line arguments:', args)
cflags.append('-fno-omit-frame-pointer')
libs.extend(['-Wl,--no-as-needed', '-lprofiler'])
-if (platform.is_linux() or platform.is_openbsd()) and not options.force_pselect:
+if (platform.is_linux() or platform.is_openbsd() or platform.is_bitrig()) and not options.force_pselect:
cflags.append('-DUSE_PPOLL')
def shell_escape(str):
def platforms():
return ['linux', 'darwin', 'freebsd', 'openbsd', 'solaris', 'sunos5',
- 'mingw', 'msvc', 'gnukfreebsd8']
+ 'mingw', 'msvc', 'gnukfreebsd8', 'bitrig']
class Platform( object ):
def __init__( self, platform):
self._platform = 'mingw'
elif self._platform.startswith('win'):
self._platform = 'msvc'
+ elif self._platform.startswith('bitrig'):
+ self._platform = 'bitrig'
def platform(self):
def is_sunos5(self):
return self._platform == 'sunos5'
+
+ def is_bitrig(self):
+ return self._platform == 'bitrig'
const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995);
const int r = 47;
uint64_t h = seed ^ (len * m);
- const uint64_t * data = (const uint64_t *)key;
- const uint64_t * end = data + (len/8);
- while (data != end) {
- uint64_t k = *data++;
+ const unsigned char * data = (const unsigned char *)key;
+ while (len >= 8) {
+ uint64_t k;
+ memcpy(&k, data, sizeof k);
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
+ data += 8;
+ len -= 8;
}
- const unsigned char* data2 = (const unsigned char*)data;
switch (len & 7)
{
- case 7: h ^= uint64_t(data2[6]) << 48;
- case 6: h ^= uint64_t(data2[5]) << 40;
- case 5: h ^= uint64_t(data2[4]) << 32;
- case 4: h ^= uint64_t(data2[3]) << 24;
- case 3: h ^= uint64_t(data2[2]) << 16;
- case 2: h ^= uint64_t(data2[1]) << 8;
- case 1: h ^= uint64_t(data2[0]);
+ case 7: h ^= uint64_t(data[6]) << 48;
+ case 6: h ^= uint64_t(data[5]) << 40;
+ case 5: h ^= uint64_t(data[4]) << 32;
+ case 4: h ^= uint64_t(data[3]) << 24;
+ case 3: h ^= uint64_t(data[2]) << 16;
+ case 2: h ^= uint64_t(data[1]) << 8;
+ case 1: h ^= uint64_t(data[0]);
h *= m;
};
h ^= h >> r;
const char kFileSignature[] = "# ninjadeps\n";
const int kCurrentVersion = 1;
+// Since the size field is 2 bytes and the top bit marks deps entries, a single
+// record can be at most 32 kB. Set the buffer size to this and flush the file
+// buffer after every record to make sure records aren't written partially.
+const int kMaxBufferSize = 1 << 15;
+
DepsLog::~DepsLog() {
Close();
}
*err = strerror(errno);
return false;
}
+ setvbuf(file_, NULL, _IOFBF, kMaxBufferSize);
SetCloseOnExec(fileno(file_));
// Opening a file in append mode doesn't set the file pointer to the file's
return false;
}
}
+ fflush(file_);
return true;
}
id = nodes[i]->id();
fwrite(&id, 4, 1, file_);
}
+ fflush(file_);
// Update in-memory representation.
Deps* deps = new Deps(mtime, node_count);
uint16_t size = (uint16_t)node->path().size();
fwrite(&size, 2, 1, file_);
fwrite(node->path().data(), node->path().size(), 1, file_);
+ fflush(file_);
node->set_id(nodes_.size());
nodes_.push_back(node);
#ifndef NINJA_MAP_H_
#define NINJA_MAP_H_
+#include <string.h>
#include "string_piece.h"
// MurmurHash2, by Austin Appleby
unsigned int h = seed ^ len;
const unsigned char * data = (const unsigned char *)key;
while (len >= 4) {
- unsigned int k = *(unsigned int *)data;
+ unsigned int k;
+ memcpy(&k, data, sizeof k);
k *= m;
k ^= k >> r;
k *= m;
void LinePrinter::PrintOnNewLine(const string& to_print) {
if (!have_blank_line_)
printf("\n");
- printf("%s", to_print.c_str());
+ if (!to_print.empty()) {
+ // Avoid printf and C strings, since the actual output might contain null
+ // bytes like UTF-16 does (yuck).
+ fwrite(&to_print[0], sizeof(char), to_print.size(), stdout);
+ }
have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n';
}
#include "manifest_parser.h"
#include <stdio.h>
+#include <stdlib.h>
#include <vector>
#include "graph.h"
if (!ExpectToken(Lexer::NEWLINE, err))
return false;
- if (state_->LookupRule(name) != NULL) {
- *err = "duplicate rule '" + name + "'";
- return false;
- }
+ if (state_->LookupRule(name) != NULL)
+ return lexer_.Error("duplicate rule '" + name + "'", err);
Rule* rule = new Rule(name); // XXX scoped_ptr
if (!pool_name.empty()) {
Pool* pool = state_->LookupPool(pool_name);
if (pool == NULL)
- return lexer_.Error("unknown pool name", err);
+ return lexer_.Error("unknown pool name '" + pool_name + "'", err);
edge->pool_ = pool;
}
State state;
ManifestParser parser(&state, NULL);
string err;
+ EXPECT_FALSE(parser.ParseTest("build\n", &err));
+ EXPECT_EQ("input:1: expected path\n"
+ "build\n"
+ " ^ near here"
+ , err);
+ }
+
+ {
+ State state;
+ ManifestParser parser(&state, NULL);
+ string err;
EXPECT_FALSE(parser.ParseTest("build x: y z\n", &err));
EXPECT_EQ("input:1: unknown build rule 'y'\n"
"build x: y z\n"
ManifestParser parser(&state, NULL);
string err;
EXPECT_FALSE(parser.ParseTest("rule cat\n"
+ " command = echo\n"
+ "rule cat\n"
+ " command = echo\n", &err));
+ EXPECT_EQ("input:3: duplicate rule 'cat'\n"
+ "rule cat\n"
+ " ^ near here"
+ , err);
+ }
+
+ {
+ State state;
+ ManifestParser parser(&state, NULL);
+ string err;
+ EXPECT_FALSE(parser.ParseTest("rule cat\n"
+ " command = echo\n"
+ " rspfile = cat.rsp\n", &err));
+ EXPECT_EQ(
+ "input:4: rspfile and rspfile_content need to be both specified\n",
+ err);
+ }
+
+ {
+ State state;
+ ManifestParser parser(&state, NULL);
+ string err;
+ EXPECT_FALSE(parser.ParseTest("rule cat\n"
" command = ${fafsd\n"
"foo = bar\n",
&err));
" generator = 1\n", &err));
EXPECT_EQ("input:4: unexpected indent\n", err);
}
+
+ {
+ State state;
+ ManifestParser parser(&state, NULL);
+ string err;
+ EXPECT_FALSE(parser.ParseTest("pool\n", &err));
+ EXPECT_EQ("input:1: expected pool name\n", err);
+ }
+
+ {
+ State state;
+ ManifestParser parser(&state, NULL);
+ string err;
+ EXPECT_FALSE(parser.ParseTest("pool foo\n", &err));
+ EXPECT_EQ("input:2: expected 'depth =' line\n", err);
+ }
+
+ {
+ State state;
+ ManifestParser parser(&state, NULL);
+ string err;
+ EXPECT_FALSE(parser.ParseTest("pool foo\n"
+ " depth = 4\n"
+ "pool foo\n", &err));
+ EXPECT_EQ("input:3: duplicate pool 'foo'\n"
+ "pool foo\n"
+ " ^ near here"
+ , err);
+ }
+
+ {
+ State state;
+ ManifestParser parser(&state, NULL);
+ string err;
+ EXPECT_FALSE(parser.ParseTest("pool foo\n"
+ " depth = -1\n", &err));
+ EXPECT_EQ("input:2: invalid pool depth\n"
+ " depth = -1\n"
+ " ^ near here"
+ , err);
+ }
+
+ {
+ State state;
+ ManifestParser parser(&state, NULL);
+ string err;
+ EXPECT_FALSE(parser.ParseTest("pool foo\n"
+ " bar = 1\n", &err));
+ EXPECT_EQ("input:2: unexpected variable 'bar'\n"
+ " bar = 1\n"
+ " ^ near here"
+ , err);
+ }
+
+ {
+ State state;
+ ManifestParser parser(&state, NULL);
+ string err;
+ // Pool names are dereferenced at edge parsing time.
+ EXPECT_FALSE(parser.ParseTest("rule run\n"
+ " command = echo\n"
+ " pool = unnamed_pool\n"
+ "build out: run in\n", &err));
+ EXPECT_EQ("input:5: unknown pool name 'unnamed_pool'\n", err);
+ }
}
TEST_F(ParserTest, MissingInput) {
// The various subcommands, run via "-t XXX".
int ToolGraph(int argc, char* argv[]);
int ToolQuery(int argc, char* argv[]);
+ int ToolDeps(int argc, char* argv[]);
int ToolBrowse(int argc, char* argv[]);
int ToolMSVC(int argc, char* argv[]);
int ToolTargets(int argc, char* argv[]);
return 0;
}
+int NinjaMain::ToolDeps(int argc, char** argv) {
+ vector<Node*> nodes;
+ if (argc == 0) {
+ for (vector<Node*>::const_iterator ni = deps_log_.nodes().begin();
+ ni != deps_log_.nodes().end(); ++ni) {
+ // Only query for targets with an incoming edge and deps
+ Edge* e = (*ni)->in_edge();
+ if (e && !e->GetBinding("deps").empty())
+ nodes.push_back(*ni);
+ }
+ } else {
+ string err;
+ if (!CollectTargetsFromArgs(argc, argv, &nodes, &err)) {
+ Error("%s", err.c_str());
+ return 1;
+ }
+ }
+
+ RealDiskInterface disk_interface;
+ for (vector<Node*>::iterator it = nodes.begin(), end = nodes.end();
+ it != end; ++it) {
+ DepsLog::Deps* deps = deps_log_.GetDeps(*it);
+ if (!deps) {
+ printf("%s: deps not found\n", (*it)->path().c_str());
+ continue;
+ }
+
+ TimeStamp mtime = disk_interface.Stat((*it)->path());
+ printf("%s: #deps %d, deps mtime %d (%s)\n",
+ (*it)->path().c_str(), deps->node_count, deps->mtime,
+ (!mtime || mtime > deps->mtime ? "STALE":"VALID"));
+ for (int i = 0; i < deps->node_count; ++i)
+ printf(" %s\n", deps->nodes[i]->path().c_str());
+ printf("\n");
+ }
+
+ return 0;
+}
+
int NinjaMain::ToolTargets(int argc, char* argv[]) {
int depth = 1;
if (argc >= 1) {
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolClean },
{ "commands", "list all commands required to rebuild given targets",
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolCommands },
+ { "deps", "show dependencies stored in the deps log",
+ Tool::RUN_AFTER_LOGS, &NinjaMain::ToolDeps },
{ "graph", "output graphviz dot file for targets",
Tool::RUN_AFTER_LOAD, &NinjaMain::ToolGraph },
{ "query", "show inputs/outputs for a path",
Fatal("pipe: %s", strerror(errno));
fd_ = output_pipe[0];
#if !defined(USE_PPOLL)
- // On Linux and OpenBSD, we use ppoll in DoWork(); elsewhere we use pselect
+ // If available, we use ppoll in DoWork(); otherwise we use pselect
// and so must avoid overly-large FDs.
if (fd_ >= static_cast<int>(FD_SETSIZE))
Fatal("pipe: %s", strerror(EMFILE));
return interrupted_;
}
-#else // linux || __OpenBSD__
+#else // !defined(USE_PPOLL)
bool SubprocessSet::DoWork() {
fd_set set;
int nfds = 0;
return interrupted_;
}
-#endif // linux || __OpenBSD__
+#endif // !defined(USE_PPOLL)
Subprocess* SubprocessSet::NextFinished() {
if (finished_.empty())
// OS X's process limit is less than 1025 by default
// (|sysctl kern.maxprocperuid| is 709 on 10.7 and 10.8 and less prior to that).
-#if defined(linux) || defined(__OpenBSD__)
+#if !defined(__APPLE__) && !defined(_WIN32)
TEST_F(SubprocessTest, SetWithLots) {
// Arbitrary big number; needs to be over 1024 to confirm we're no longer
// hostage to pselect.
}
ASSERT_EQ(kNumProcs, subprocs_.finished_.size());
}
-#endif // linux || __OpenBSD__
+#endif // !__APPLE__ && !_WIN32
// TODO: this test could work on Windows, just not sure how to simply
// read stdin.