Implementing HTTP serving

This commit is contained in:
Ben Vanik 2012-05-04 16:38:32 +09:00
parent 430eb05ad7
commit f908968dfd
3 changed files with 63 additions and 10 deletions

View File

@ -27,6 +27,7 @@ anvil serve --http_port=8080 --daemon_port=8081 :some_daemon
__author__ = 'benvanik@google.com (Ben Vanik)'
import copy
import os
import sys
@ -46,20 +47,64 @@ class ServeCommand(ManageCommand):
parser = super(ServeCommand, self).create_argument_parser()
# Add all common args
self._add_common_build_arguments(parser, targets=True)
self._add_common_build_arguments(
parser, targets=True, targets_optional=True)
# 'serve' specific
parser.add_argument('-p', '--http_port',
dest='http_port',
type=int,
default=8080,
help=('TCP port the HTTP server will listen on.'))
return parser
def execute(self, args, cwd):
# Handle --rebuild
if args.rebuild:
if not commandutil.clean_output(cwd):
return False
# Initial build
if len(args.targets):
(result, all_target_outputs) = commandutil.run_build(cwd, args)
print all_target_outputs
(result, all_target_outputs) = commandutil.run_build(cwd, args)
self._launch_http_server(args.http_port, cwd)
print all_target_outputs
return 0
return 0 if result else 1
def _launch_http_server(self, port, root_path):
"""Launches a simple static twisted HTTP server.
The server will automatically merge build-* paths in to a unified namespace.
Args:
port: TCP port to listen on.
root_path: Root path of the HTTP server.
"""
from twisted.internet import reactor
from twisted.web.resource import Resource, NoResource
from twisted.web.server import Site
from twisted.web.static import File
# Special site handler that merges various output and input paths into a
# single unifed file system
class MergedSite(Site):
def getResourceFor(self, request):
# Scan well-known search paths first
search_paths = ['build-out', 'build-gen',]
for search_path in search_paths:
resource = self.resource
prepath = copy.copy(request.prepath)
postpath = copy.copy(request.postpath)
postpath.insert(0, search_path)
while postpath and not resource.isLeaf:
path_element = postpath.pop(0)
prepath.append(path_element)
resource = resource.getChildWithDefault(path_element, request)
if resource and not isinstance(resource, NoResource):
return resource
# Fallback to normal handling
return Site.getResourceFor(self, request)
print 'Launching HTTP server on port %s...' % (port)
root = File(root_path)
factory = MergedSite(root)
reactor.listenTCP(port, factory)
reactor.run()

View File

@ -49,7 +49,13 @@ def run_build(cwd, parsed_args):
Args:
cwd: Current working directory.
parsed_args: Argument namespace from an ArgumentParser.
Returns:
(success, a list of all target output paths)
"""
if not len(parsed_args.targets):
return (True, [])
build_env = BuildEnvironment(root_path=cwd)
module_resolver = FileModuleResolver(cwd)

View File

@ -65,12 +65,14 @@ class ManageCommand(object):
'--stop_on_error',
])
def _add_common_build_arguments(self, parser, targets=False):
def _add_common_build_arguments(self, parser, targets=False,
targets_optional=False):
"""Adds common build arguments to an argument parser.
Args:
parser: ArgumentParser to modify.
targets: True to add variable target arguments.
targets_optional: Targets, if included, are optional
"""
# Threading/execution control
parser.add_argument('-j', '--jobs',
@ -97,7 +99,7 @@ class ManageCommand(object):
# Target specification
if targets:
parser.add_argument('targets',
nargs='+',
nargs='*' if targets_optional else '+',
metavar='target',
help='Target build rule (such as :a or foo/bar:a)')