blob: 0a54d33b3b275bc17b49b654a3ac188aefe01d6f [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2014 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.
""" Pex builder wrapper """
import pex.bin.pex as pexbin
from pex.common import safe_delete
from pex.tracer import TRACER
from pex.variables import ENV
import json
import os
import sys
def dereference_symlinks(src):
"""
Resolve all symbolic references that `src` points to. Note that this
is different than `os.path.realpath` as path components leading up to
the final location may still be symbolic links.
"""
while os.path.islink(src):
src = os.path.join(os.path.dirname(src), os.readlink(src))
return src
def parse_manifest(manifest_text):
"""Parse a json manifest.
Manifest format:
{
"modules": [ {"src": "path_on_disk", "dest": "path_in_pex"}, ... ],
"resources": [ {"src": "path_on_disk", "dest": "path_in_pex"}, ... ],
"prebuiltLibraries": [ "path1", "path2", ... ],
"requirements": [ "thing1", "thing2==version", ... ],
}
"""
return json.loads(manifest_text)
def main():
pparser, resolver_options_builder = pexbin.configure_clp()
poptions, args = pparser.parse_args(sys.argv)
manifest_file = args[1]
manifest_text = open(manifest_file, 'r').read()
manifest = parse_manifest(manifest_text)
if poptions.pex_root:
ENV.set('PEX_ROOT', poptions.pex_root)
else:
poptions.pex_root = ENV.PEX_ROOT
if poptions.cache_dir:
poptions.cache_dir = pexbin.make_relative_to_root(poptions.cache_dir)
poptions.interpreter_cache_dir = pexbin.make_relative_to_root(
poptions.interpreter_cache_dir)
reqs = manifest.get('requirements', [])
with ENV.patch(PEX_VERBOSE=str(poptions.verbosity)):
with TRACER.timed('Building pex'):
pex_builder = pexbin.build_pex(reqs, poptions,
resolver_options_builder)
# Add source files from the manifest
for modmap in manifest.get('modules', []):
src = modmap.get('src')
dst = modmap.get('dest')
# NOTE(agallagher): calls the `add_source` and `add_resource` below
# hard-link the given source into the PEX temp dir. Since OS X and
# Linux behave different when hard-linking a source that is a
# symbolic link (Linux does *not* follow symlinks), resolve any
# layers of symlinks here to get consistent behavior.
try:
pex_builder.add_source(dereference_symlinks(src), dst)
except OSError as err:
# Maybe we just can't use hardlinks? Try again.
if not pex_builder._copy:
pex_builder._copy = True
pex_builder.add_source(dereference_symlinks(src), dst)
else:
raise RuntimeError("Failed to add %s: %s" % (src, err))
# Add resources from the manifest
for reqmap in manifest.get('resources', []):
src = reqmap.get('src')
dst = reqmap.get('dest')
pex_builder.add_resource(dereference_symlinks(src), dst)
# Add eggs/wheels from the manifest
for egg in manifest.get('prebuiltLibraries', []):
try:
pex_builder.add_dist_location(egg)
except Exception as err:
raise RuntimeError("Failed to add %s: %s" % (egg, err))
# TODO(mikekap): Do something about manifest['nativeLibraries'].
pexbin.log('Saving PEX file to %s' % poptions.pex_name,
v=poptions.verbosity)
tmp_name = poptions.pex_name + '~'
safe_delete(tmp_name)
pex_builder.build(tmp_name)
os.rename(tmp_name, poptions.pex_name)
if __name__ == '__main__':
main()