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)
### Problem 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. ### Solution 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. ### Result 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
86c22f7
commit 3384a5c
Showing
25 changed files
with
708 additions
and
197 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
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
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() |
129 changes: 129 additions & 0 deletions
129
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,129 @@ | ||
# 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.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"] | ||
|
||
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) |
62 changes: 0 additions & 62 deletions
62
src/python/pants/backend/python/subsystems/pex_build_util.py
This file was deleted.
Oops, something went wrong.
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
Oops, something went wrong.