penv.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. # Copyright (c) 2014-present PlatformIO <contact@platformio.org>
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. import json
  15. import logging
  16. import os
  17. import platform
  18. import subprocess
  19. import sys
  20. import time
  21. from pathlib import Path
  22. import click
  23. from pioinstaller import __version__, core, exception, python, util
  24. log = logging.getLogger(__name__)
  25. dir_path = ""
  26. if getattr(sys, 'frozen', False):
  27. dir_path = os.path.dirname(sys.executable)
  28. elif __file__:
  29. dir_path = os.path.dirname(__file__)
  30. VIRTUALENV_URL = "https://bootstrap.pypa.io/virtualenv/virtualenv.pyz"
  31. PIP_URL = "https://bootstrap.pypa.io/get-pip.py"
  32. def get_penv_dir(path=None):
  33. if os.getenv("PLATFORMIO_PENV_DIR"):
  34. return os.getenv("PLATFORMIO_PENV_DIR")
  35. core_dir = path or core.get_core_dir()
  36. return os.path.join(core_dir, "penv")
  37. def get_penv_bin_dir(path=None):
  38. penv_dir = path or get_penv_dir()
  39. return os.path.join(penv_dir, "Scripts" if util.IS_WINDOWS else "bin")
  40. def create_core_penv(penv_dir=None, ignore_pythons=None):
  41. penv_dir = penv_dir or get_penv_dir()
  42. click.echo("Creating a virtual environment at %s" % penv_dir)
  43. result_dir = None
  44. for python_exe in python.find_compatible_pythons(ignore_pythons):
  45. result_dir = create_virtualenv(python_exe, penv_dir)
  46. if result_dir:
  47. break
  48. if (
  49. not result_dir
  50. and util.get_systype() in python.PORTABLE_PYTHONS
  51. and not python.is_portable()
  52. ):
  53. python_exe = python.fetch_portable_python(os.path.dirname(penv_dir))
  54. if python_exe:
  55. result_dir = create_virtualenv(python_exe, penv_dir)
  56. if not result_dir:
  57. raise exception.PIOInstallerException(
  58. "Could not create PIO Core Virtual Environment. Please report to "
  59. "https://github.com/platformio/platformio-core-installer/issues"
  60. )
  61. python_exe = os.path.join(
  62. get_penv_bin_dir(penv_dir), "python.exe" if util.IS_WINDOWS else "python"
  63. )
  64. add_state_info(python_exe, penv_dir)
  65. install_pip(python_exe, penv_dir)
  66. click.echo("Virtual environment has been successfully created!")
  67. return result_dir
  68. def create_virtualenv(python_exe, penv_dir):
  69. log.debug("Using %s Python for virtual environment.", python_exe)
  70. try:
  71. return create_with_local_venv(python_exe, penv_dir)
  72. except Exception as e: # pylint:disable=broad-except
  73. log.debug(
  74. "Could not create virtualenv with local packages"
  75. " Trying download virtualenv script and using it. Error: %s",
  76. str(e),
  77. )
  78. try:
  79. return create_with_remote_venv(python_exe, penv_dir)
  80. except Exception as e: # pylint:disable=broad-except
  81. log.debug(
  82. "Could not create virtualenv with downloaded script. Error: %s", str(e),
  83. )
  84. def create_with_local_venv(python_exe, penv_dir):
  85. venv_cmd_options = [
  86. [python_exe, "-m", "venv", penv_dir],
  87. [python_exe, "-m", "virtualenv", "-p", python_exe, penv_dir],
  88. ["virtualenv", "-p", python_exe, penv_dir],
  89. [python_exe, "-m", "virtualenv", penv_dir],
  90. ["virtualenv", penv_dir],
  91. ]
  92. last_error = None
  93. for command in venv_cmd_options:
  94. util.safe_remove_dir(penv_dir)
  95. log.debug("Creating virtual environment: %s", " ".join(command))
  96. try:
  97. subprocess.check_output(command, stderr=subprocess.PIPE)
  98. return penv_dir
  99. except Exception as e: # pylint:disable=broad-except
  100. last_error = e
  101. raise last_error # pylint:disable=raising-bad-type
  102. def create_with_remote_venv(python_exe, penv_dir):
  103. util.safe_remove_dir(penv_dir)
  104. log.debug("Downloading virtualenv package archive")
  105. venv_script_path = Path(dir_path).joinpath("virtualenv.pyz")
  106. if not venv_script_path:
  107. raise exception.PIOInstallerException("Could not find virtualenv script")
  108. command = [python_exe, venv_script_path, penv_dir]
  109. log.debug("Creating virtual environment: %s", " ".join(command))
  110. subprocess.check_output(command, stderr=subprocess.PIPE)
  111. return penv_dir
  112. def add_state_info(python_exe, penv_dir):
  113. version_code = (
  114. "import sys; version=sys.version_info; "
  115. "print('%d.%d.%d'%(version[0],version[1],version[2]))"
  116. )
  117. python_version = (
  118. subprocess.check_output(
  119. [python_exe, "-c", version_code], stderr=subprocess.PIPE
  120. )
  121. .decode()
  122. .strip()
  123. )
  124. state = {
  125. "created_on": int(round(time.time())),
  126. "python": {"path": python_exe, "version": python_version, },
  127. "installer_version": __version__,
  128. "platform": platform.platform(terse=True),
  129. }
  130. with open(os.path.join(penv_dir, "state.json"), "w") as fp:
  131. json.dump(state, fp)
  132. return os.path.join(penv_dir, "state.json")
  133. def install_pip(python_exe, penv_dir):
  134. click.echo("Updating Python package manager (PIP) in a virtual environment")
  135. try:
  136. log.debug("Creating pip.conf file in %s", penv_dir)
  137. print(Path(dir_path).joinpath("../../../.rt_global").absolute().as_posix())
  138. if Path(dir_path).joinpath("../../../.rt_global").exists():
  139. click.echo("global")
  140. with open(os.path.join(penv_dir, "pip.conf"), "w") as fp:
  141. fp.write("\n".join(["[global]", "user=no"]))
  142. else:
  143. click.echo("cn")
  144. with open(os.path.join(penv_dir, "pip.conf"), "w") as fp:
  145. fp.write("\n".join(["[global]", "user=no", "index-url = https://pypi.tuna.tsinghua.edu.cn/simple",
  146. "timeout=8000",
  147. "trusted-host = pypi.tuna.tsinghua.edu.cn"]))
  148. log.debug("Downloading 'get-pip.py' installer...")
  149. get_pip_path = Path(dir_path).joinpath("get-pip.py")
  150. click.echo(str(get_pip_path))
  151. log.debug("Installing pip")
  152. subprocess.check_output([python_exe, get_pip_path], stderr=subprocess.PIPE)
  153. click.echo("PIP has been successfully updated!")
  154. return True
  155. except Exception as e: # pylint:disable=broad-except
  156. log.debug(
  157. "Could not install pip. Error: %s", str(e),
  158. )
  159. return False