browse: support --port and --no-browser options
authorPeter Wu <peter@lekensteyn.nl>
Fri, 11 Mar 2016 23:24:00 +0000 (00:24 +0100)
committerPeter Wu <peter@lekensteyn.nl>
Fri, 11 Mar 2016 23:24:00 +0000 (00:24 +0100)
Add --port option to override the default port (8000).
Add --no-browser option to avoid opening a web browser (useful over
SSH).
Make the target name optional, using "all" if omitted.

doc/manual.asciidoc
src/browse.cc
src/browse.h
src/browse.py
src/ninja.cc

index 4e69ad4..9fc5fb9 100644 (file)
@@ -223,9 +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. The `PORT` environment variable
-can be used to override the default port number (8000).
-
+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:
 +
index 8673919..46434d7 100644 (file)
 #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);
index 263641f..842c6ff 100644 (file)
@@ -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_
index bf85e4d..63c60aa 100755 (executable)
@@ -26,6 +26,7 @@ try:
     import http.server as httpserver
 except ImportError:
     import BaseHTTPServer as httpserver
+import argparse
 import os
 import socket
 import subprocess
@@ -149,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,)
@@ -161,7 +162,7 @@ class RequestHandler(httpserver.BaseHTTPRequestHandler):
 
         if target == '':
             self.send_response(302)
-            self.send_header('Location', '?' + sys.argv[2])
+            self.send_header('Location', '?' + args.initial_target)
             self.end_headers()
             return
 
@@ -185,13 +186,26 @@ class RequestHandler(httpserver.BaseHTTPRequestHandler):
     def log_message(self, format, *args):
         pass  # Swallow console spam.
 
-port = int(os.getenv("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()
index 35f293b..b3b9bed 100644 (file)
@@ -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;
 }