]> git.proxmox.com Git - ceph.git/blob - ceph/src/cephadm/build.py
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / cephadm / build.py
1 #!/usr/bin/python3
2 """Build cephadm from one or more files into a standalone executable.
3 """
4 # TODO: If cephadm is being built and packaged within a format such as RPM
5 # do we have to do anything special wrt passing in the version
6 # of python to build with? Even with the intermediate cmake layer?
7
8 import argparse
9 import compileall
10 import logging
11 import os
12 import pathlib
13 import shutil
14 import subprocess
15 import tempfile
16 import sys
17
18 HAS_ZIPAPP = False
19 try:
20 import zipapp
21
22 HAS_ZIPAPP = True
23 except ImportError:
24 pass
25
26
27 log = logging.getLogger(__name__)
28
29
30 def _reexec(python):
31 """Switch to the selected version of python by exec'ing into the desired
32 python path.
33 Sets the _BUILD_PYTHON_SET env variable as a sentinel to indicate exec has
34 been performed.
35 """
36 env = os.environ.copy()
37 env["_BUILD_PYTHON_SET"] = python
38 os.execvpe(python, [python, __file__] + sys.argv[1:], env)
39
40
41 def _did_rexec():
42 """Returns true if the process has already exec'ed into the desired python
43 version.
44 """
45 return bool(os.environ.get("_BUILD_PYTHON_SET", ""))
46
47
48 def _build(dest, src):
49 """Build the binary."""
50 os.chdir(src)
51 tempdir = pathlib.Path(tempfile.mkdtemp(suffix=".cephadm.build"))
52 log.debug("working in %s", tempdir)
53 try:
54 if os.path.isfile("requirements.txt"):
55 _install_deps(tempdir)
56 log.info("Copying contents")
57 # TODO: currently the only file relevant to a compiled cephadm is the
58 # cephadm.py file. Once cephadm is broken up into multiple py files
59 # (and possibly other libs from python-common, etc) we'll want some
60 # sort organized structure to track what gets copied into the
61 # dir to be zipped. For now we just have a simple call to copy
62 # (and rename) the one file we care about.
63 shutil.copy("cephadm.py", tempdir / "__main__.py")
64 _compile(dest, tempdir)
65 finally:
66 shutil.rmtree(tempdir)
67
68
69 def _compile(dest, tempdir):
70 """Compile the zipapp."""
71 log.info("Byte-compiling py to pyc")
72 compileall.compile_dir(
73 tempdir,
74 maxlevels=16,
75 legacy=True,
76 quiet=1,
77 workers=0,
78 )
79 # TODO we could explicitly pass a python version here
80 log.info("Constructing the zipapp file")
81 try:
82 zipapp.create_archive(
83 source=tempdir,
84 target=dest,
85 interpreter=sys.executable,
86 compressed=True,
87 )
88 log.info("Zipapp created with compression")
89 except TypeError:
90 # automatically fall back to uncompressed
91 zipapp.create_archive(
92 source=tempdir,
93 target=dest,
94 interpreter=sys.executable,
95 )
96 log.info("Zipapp created without compression")
97
98
99 def _install_deps(tempdir):
100 """Install dependencies with pip."""
101 # TODO we could explicitly pass a python version here
102 log.info("Installing dependencies")
103 # apparently pip doesn't have an API, just a cli.
104 subprocess.check_call(
105 [
106 sys.executable,
107 "-m",
108 "pip",
109 "install",
110 "--requirement",
111 "requirements.txt",
112 "--target",
113 tempdir,
114 ]
115 )
116
117
118 def main():
119 handler = logging.StreamHandler(sys.stdout)
120 handler.setFormatter(logging.Formatter("cephadm/build.py: %(message)s"))
121 log.addHandler(handler)
122 log.setLevel(logging.INFO)
123
124 log.debug("argv: %r", sys.argv)
125 parser = argparse.ArgumentParser()
126 parser.add_argument(
127 "dest", help="Destination path name for new cephadm binary"
128 )
129 parser.add_argument(
130 "--source", help="Directory containing cephadm sources"
131 )
132 parser.add_argument(
133 "--python", help="The path to the desired version of python"
134 )
135 args = parser.parse_args()
136
137 if not _did_rexec() and args.python:
138 _reexec(args.python)
139
140 log.info(
141 "Python Version: {v.major}.{v.minor}.{v.micro}".format(
142 v=sys.version_info
143 )
144 )
145 log.info("Args: %s", vars(args))
146 if not HAS_ZIPAPP:
147 # Unconditionally display an error that the version of python
148 # lacks zipapp (probably too old).
149 print("error: zipapp module not found", file=sys.stderr)
150 print(
151 "(zipapp is available in Python 3.5 or later."
152 " are you using a new enough version?)",
153 file=sys.stderr,
154 )
155 sys.exit(2)
156 if args.source:
157 source = pathlib.Path(args.source).absolute()
158 else:
159 source = pathlib.Path(__file__).absolute().parent
160 dest = pathlib.Path(args.dest).absolute()
161 log.info("Source Dir: %s", source)
162 log.info("Destination Path: %s", dest)
163 _build(dest, source)
164
165
166 if __name__ == "__main__":
167 main()