forked from pantsbuild/pants
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
first implement v1 of robot impl (pantsbuild#8793)
See pex-tool/pex#789 for a description of the issue, and https://docs.google.com/document/d/1B_g0Ofs8aQsJtrePPR1PCtSAKgBG1o59AhS_NwfFnbI/edit for a google doc with pros and cons of different approaches. @jsirois was extremely helpful throughout the development of this feature, and pex-tool/pex#819 and pex-tool/pex#821 in pex `2.0.3` will help to optimize several other aspects of this process when we can unrevert pantsbuild#8787. **Note:** `src/python/pants/backend/python/subsystems/pex_build_util.py` was removed in this PR, along with all floating references to it. With `--binary-py-generate-ipex`, a `.ipex` file will be created when `./pants binary` is run against a `python_binary()` target. This `.ipex` archive will create a `.pex` file and run it when first executed. The `.ipex` archive contains: - in `IPEX-INFO`: the source files to inject into the resulting `.pex`, and pypi indices to resolve requirements from. - in `BOOSTRAP-PEX-INFO`: the `PEX-INFO` of the pex file that *would* have been generated if `--generate-ipex` was False. - in `ipex.py`: A bootstrap script which will generate a `.pex` file when the `.ipex` file is first executed. For a `.ipex` file which hydrates the `tensorflow==1.14.0` dependency when it is first run, this translates to a >100x decrease in file size: ```bash X> ls dist total 145M -rwxr-xr-x 1 dmcclanahan staff 267k Dec 10 21:11 dehydrated.ipex* -rwxr-xr-x 1 dmcclanahan staff 134M Dec 10 21:11 dehydrated.pex* ```
- Loading branch information
1 parent
9145352
commit 1776dea
Showing
38 changed files
with
1,143 additions
and
555 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
6 changes: 6 additions & 0 deletions
6
examples/src/python/example/tensorflow_custom_op/show_tf_version.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
import tensorflow as tf | ||
|
||
print(f"tf version: {tf.__version__}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
# NB: This target is written into an .ipex file as the main script, and should not have any | ||
# dependencies on another python code! .ipex files should always contain pex and setuptools | ||
# requirements in order to run the main script! | ||
python_library() |
131 changes: 131 additions & 0 deletions
131
src/python/pants/backend/python/subsystems/ipex/ipex_launcher.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md). | ||
# Licensed under the Apache License, Version 2.0 (see LICENSE). | ||
|
||
"""Entrypoint script for a "dehydrated" .ipex file generated with --generate-ipex. | ||
This script will "hydrate" a normal .pex file in the same directory, then execute it. | ||
""" | ||
|
||
import json | ||
import os | ||
import sys | ||
import tempfile | ||
|
||
from pex import resolver | ||
from pex.common import open_zip | ||
from pex.fetcher import Fetcher, PyPIFetcher | ||
from pex.interpreter import PythonInterpreter | ||
from pex.pex_builder import PEXBuilder | ||
from pex.pex_info import PexInfo | ||
from pkg_resources import Requirement | ||
|
||
|
||
APP_CODE_PREFIX = 'user_files/' | ||
|
||
|
||
def _strip_app_code_prefix(path): | ||
if not path.startswith(APP_CODE_PREFIX): | ||
raise ValueError("Path {path} in IPEX-INFO did not begin with '{APP_CODE_PREFIX}'." | ||
.format(path=path, APP_CODE_PREFIX=APP_CODE_PREFIX)) | ||
return path[len(APP_CODE_PREFIX):] | ||
|
||
|
||
def _log(message): | ||
sys.stderr.write(message + '\n') | ||
|
||
|
||
def _sanitize_requirements(requirements): | ||
""" | ||
Remove duplicate keys such as setuptools or pex which may be injected multiple times into the | ||
resulting ipex when first executed. | ||
""" | ||
project_names = [] | ||
new_requirements = {} | ||
|
||
for r in requirements: | ||
r = Requirement(r) | ||
if r.marker and not r.marker.evaluate(): | ||
continue | ||
if r.name not in new_requirements: | ||
project_names.append(r.name) | ||
new_requirements[r.name] = str(r) | ||
sanitized_requirements = [new_requirements[n] for n in project_names] | ||
|
||
return sanitized_requirements | ||
|
||
|
||
def modify_pex_info(pex_info, **kwargs): | ||
new_info = json.loads(pex_info.dump()) | ||
new_info.update(kwargs) | ||
return PexInfo.from_json(json.dumps(new_info)) | ||
|
||
|
||
def _hydrate_pex_file(self, hydrated_pex_file): | ||
# We extract source files into a temporary directory before creating the pex. | ||
td = tempfile.mkdtemp() | ||
|
||
with open_zip(self) as zf: | ||
# Populate the pex with the pinned requirements and distribution names & hashes. | ||
bootstrap_info = PexInfo.from_json(zf.read('BOOTSTRAP-PEX-INFO')) | ||
bootstrap_builder = PEXBuilder(pex_info=bootstrap_info, interpreter=PythonInterpreter.get()) | ||
|
||
# Populate the pex with the needed code. | ||
try: | ||
ipex_info = json.loads(zf.read('IPEX-INFO').decode('utf-8')) | ||
for path in ipex_info['code']: | ||
unzipped_source = zf.extract(path, td) | ||
bootstrap_builder.add_source(unzipped_source, env_filename=_strip_app_code_prefix(path)) | ||
except Exception as e: | ||
raise ValueError("Error: {e}. The IPEX-INFO for this .ipex file was:\n{info}" | ||
.format(e=e, info=json.dumps(ipex_info, indent=4))) | ||
|
||
# Perform a fully pinned intransitive resolve to hydrate the install cache. | ||
resolver_settings = ipex_info['resolver_settings'] | ||
# TODO: Here we convert .indexes and .find_links into the old .fetchers until pants upgrades to | ||
# pex 2.0. At that time, we can remove anything relating to fetchers from `resolver_settings`, and | ||
# avoid removing the 'indexes' and 'find_links' keys, which are correct for pex 2.0. | ||
fetchers = [PyPIFetcher(url) for url in resolver_settings.pop('indexes')] | ||
fetchers.extend(Fetcher([url]) for url in resolver_settings.pop('find_links')) | ||
resolver_settings['fetchers'] = fetchers | ||
|
||
sanitized_requirements = _sanitize_requirements(bootstrap_info.requirements) | ||
bootstrap_info = modify_pex_info(bootstrap_info, requirements=sanitized_requirements) | ||
bootstrap_builder.info = bootstrap_info | ||
|
||
resolved_distributions = resolver.resolve( | ||
requirements=bootstrap_info.requirements, | ||
cache=bootstrap_info.pex_root, | ||
platform='current', | ||
transitive=False, | ||
interpreter=bootstrap_builder.interpreter, | ||
**resolver_settings | ||
) | ||
# TODO: this shouldn't be necessary, as we should be able to use the same 'distributions' from | ||
# BOOTSTRAP-PEX-INFO. When the .ipex is executed, the normal pex bootstrap fails to see these | ||
# requirements or recognize that they should be pulled from the cache for some reason. | ||
for resolved_dist in resolved_distributions: | ||
bootstrap_builder.add_distribution(resolved_dist.distribution) | ||
|
||
bootstrap_builder.build(hydrated_pex_file, bytecode_compile=False) | ||
|
||
|
||
def main(self): | ||
filename_base, ext = os.path.splitext(self) | ||
|
||
# If the ipex (this pex) is already named '.pex', ensure the output filename doesn't collide by | ||
# inserting an intermediate '.ipex'! | ||
if ext == '.pex': | ||
hydrated_pex_file = '{filename_base}.ipex.pex'.format(filename_base=filename_base) | ||
else: | ||
hydrated_pex_file = '{filename_base}.pex'.format(filename_base=filename_base) | ||
|
||
if not os.path.exists(hydrated_pex_file): | ||
_log('Hydrating {} to {}...'.format(self, hydrated_pex_file)) | ||
_hydrate_pex_file(self, hydrated_pex_file) | ||
|
||
os.execv(sys.executable, [sys.executable, hydrated_pex_file] + sys.argv[1:]) | ||
|
||
|
||
if __name__ == '__main__': | ||
self = sys.argv[0] | ||
main(self) |
Oops, something went wrong.