summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/manual.asciidoc9
-rw-r--r--src/browse.cc18
-rw-r--r--src/browse.h5
-rwxr-xr-xsrc/browse.py29
-rw-r--r--src/ninja.cc6
5 files changed, 46 insertions, 21 deletions
diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 0d842f9..9fc5fb9 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -223,8 +223,13 @@ found useful during Ninja's development. The current tools are:
`browse`:: browse the dependency graph in a web browser. Clicking a
file focuses the view on that file, showing inputs and outputs. This
-feature requires a Python installation.
-
+feature requires a Python installation. By default port 8000 is used
+and a web browser will be opened. This can be changed as follows:
++
+----
+ninja -t browse --port=8000 --no-browser mytarget
+----
++
`graph`:: output a file in the syntax used by `graphviz`, a automatic
graph layout tool. Use it like:
+
diff --git a/src/browse.cc b/src/browse.cc
index 8673919..46434d7 100644
--- a/src/browse.cc
+++ b/src/browse.cc
@@ -17,11 +17,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
+#include <vector>
#include "build/browse_py.h"
void RunBrowsePython(State* state, const char* ninja_command,
- const char* initial_target) {
+ int argc, char* argv[]) {
// Fork off a Python process and have it run our code via its stdin.
// (Actually the Python process becomes the parent.)
int pipefd[2];
@@ -44,11 +45,16 @@ void RunBrowsePython(State* state, const char* ninja_command,
break;
}
- // exec Python, telling it to run the program from stdin.
- const char* command[] = {
- NINJA_PYTHON, "-", ninja_command, initial_target, NULL
- };
- execvp(command[0], (char**)command);
+ std::vector<const char *> command;
+ command.push_back(NINJA_PYTHON);
+ command.push_back("-");
+ command.push_back("--ninja-command");
+ command.push_back(ninja_command);
+ for (int i = 0; i < argc; i++) {
+ command.push_back(argv[i]);
+ }
+ command.push_back(NULL);
+ execvp(command[0], (char**)&command[0]);
perror("ninja: execvp");
} while (false);
_exit(1);
diff --git a/src/browse.h b/src/browse.h
index 263641f..842c6ff 100644
--- a/src/browse.h
+++ b/src/browse.h
@@ -19,9 +19,10 @@ struct State;
/// Run in "browse" mode, which execs a Python webserver.
/// \a ninja_command is the command used to invoke ninja.
-/// \a initial_target is the first target to load.
+/// \a args are the number of arguments to be passed to the Python script.
+/// \a argv are arguments to be passed to the Python script.
/// This function does not return if it runs successfully.
void RunBrowsePython(State* state, const char* ninja_command,
- const char* initial_target);
+ int argc, char* argv[]);
#endif // NINJA_BROWSE_H_
diff --git a/src/browse.py b/src/browse.py
index 9e59bd8..63c60aa 100755
--- a/src/browse.py
+++ b/src/browse.py
@@ -26,12 +26,16 @@ try:
import http.server as httpserver
except ImportError:
import BaseHTTPServer as httpserver
+import argparse
import os
import socket
import subprocess
import sys
import webbrowser
-import urllib2
+try:
+ from urllib.request import unquote
+except ImportError:
+ from urllib2 import unquote
from collections import namedtuple
Node = namedtuple('Node', ['inputs', 'rule', 'target', 'outputs'])
@@ -146,7 +150,7 @@ def generate_html(node):
return '\n'.join(document)
def ninja_dump(target):
- proc = subprocess.Popen([sys.argv[1], '-t', 'query', target],
+ proc = subprocess.Popen([args.ninja_command, '-t', 'query', target],
stdout=subprocess.PIPE, stderr=subprocess.PIPE,
universal_newlines=True)
return proc.communicate() + (proc.returncode,)
@@ -154,11 +158,11 @@ def ninja_dump(target):
class RequestHandler(httpserver.BaseHTTPRequestHandler):
def do_GET(self):
assert self.path[0] == '/'
- target = urllib2.unquote(self.path[1:])
+ target = unquote(self.path[1:])
if target == '':
self.send_response(302)
- self.send_header('Location', '?' + sys.argv[2])
+ self.send_header('Location', '?' + args.initial_target)
self.end_headers()
return
@@ -182,13 +186,26 @@ class RequestHandler(httpserver.BaseHTTPRequestHandler):
def log_message(self, format, *args):
pass # Swallow console spam.
-port = 8000
+parser = argparse.ArgumentParser(prog='ninja -t browse')
+parser.add_argument('--port', '-p', default=8000, type=int,
+ help='Port number to use (default %(default)d)')
+parser.add_argument('--no-browser', action='store_true',
+ help='Do not open a webbrowser on startup.')
+
+parser.add_argument('--ninja-command', default='ninja',
+ help='Path to ninja binary (default %(default)s)')
+parser.add_argument('initial_target', default='all', nargs='?',
+ help='Initial target to show (default %(default)s)')
+
+args = parser.parse_args()
+port = args.port
httpd = httpserver.HTTPServer(('',port), RequestHandler)
try:
hostname = socket.gethostname()
print('Web server running on %s:%d, ctl-C to abort...' % (hostname,port) )
print('Web server pid %d' % os.getpid(), file=sys.stderr )
- webbrowser.open_new('http://%s:%s' % (hostname, port) )
+ if not args.no_browser:
+ webbrowser.open_new('http://%s:%s' % (hostname, port) )
httpd.serve_forever()
except KeyboardInterrupt:
print()
diff --git a/src/ninja.cc b/src/ninja.cc
index 35f293b..b3b9bed 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -371,11 +371,7 @@ int NinjaMain::ToolQuery(int argc, char* argv[]) {
#if defined(NINJA_HAVE_BROWSE)
int NinjaMain::ToolBrowse(int argc, char* argv[]) {
- if (argc < 1) {
- Error("expected a target to browse");
- return 1;
- }
- RunBrowsePython(&state_, ninja_command_, argv[0]);
+ RunBrowsePython(&state_, ninja_command_, argc, argv);
// If we get here, the browse failed.
return 1;
}