blob: d450427ef09b53928097ca301770b3dfa5456e2b [file] [log] [blame]
# Copyright 2016 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.
"""Command line interface to subpar compiler"""
import argparse
import io
import os
import re
from subpar.compiler import error
from subpar.compiler import python_archive
def make_command_line_parser():
"""Return an object that can parse this program's command line"""
parser = argparse.ArgumentParser(
description='Subpar Python Executable Builder')
parser.add_argument(
'main_filename',
help='Python source file to use as main entry point')
parser.add_argument(
'--manifest_file',
help='File listing all files to be included in this parfile. This is ' +
'typically generated by bazel in a target\'s .runfiles_manifest file.',
required=True)
parser.add_argument(
'--manifest_root',
help='Root directory of all relative paths in manifest file.',
default=os.getcwd())
parser.add_argument(
'--outputpar',
help='Filename of generated par file.',
required=True)
parser.add_argument(
'--stub_file',
help='Read imports and interpreter path from the specified stub file',
required=True)
return parser
def parse_stub(stub_filename):
"""Parse the imports and interpreter path from a py_binary() stub.
We assume the stub is utf-8 encoded.
TODO(b/29227737): Remove this once we can access imports from skylark.
Returns (list of relative paths, path to Python interpreter)
"""
# Find the list of import roots
imports_regex = re.compile(r'''^ python_imports = '([^']*)'$''')
interpreter_regex = re.compile(r'''^PYTHON_BINARY = '([^']*)'$''')
import_roots = None
interpreter = None
with io.open(stub_filename, 'rt', encoding='utf8') as stub_file:
for line in stub_file:
importers_match = imports_regex.match(line)
if importers_match:
import_roots = importers_match.group(1).split(':')
# Filter out empty paths
import_roots = [x for x in import_roots if x]
interpreter_match = interpreter_regex.match(line)
if interpreter_match:
interpreter = interpreter_match.group(1)
if import_roots is None or not interpreter:
raise error.Error('Failed to parse stub file [%s]' % stub_filename)
# Find the Python interpreter, matching the search logic in
# stub_template.txt
if interpreter.startswith('//'):
raise error.Error('Python interpreter must not be a label [%s]' %
stub_filename)
elif interpreter.startswith('/'):
pass
elif '/' in interpreter:
pass
else:
interpreter = '/usr/bin/env %s' % interpreter
return (import_roots, interpreter)
def main(argv):
"""Command line interface to Subpar"""
parser = make_command_line_parser()
args = parser.parse_args(argv[1:])
# Parse information from stub file that's too hard to compute in Skylark
import_roots, interpreter = parse_stub(args.stub_file)
par = python_archive.PythonArchive(
main_filename=args.main_filename,
import_roots=import_roots,
interpreter=interpreter,
output_filename=args.outputpar,
manifest_filename=args.manifest_file,
manifest_root=args.manifest_root,
)
par.create()