sima-cli 0.0.17__py3-none-any.whl → 0.0.19__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sima_cli/__version__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  # sima_cli/__version__.py
2
- __version__ = "0.0.17"
2
+ __version__ = "0.0.19"
sima_cli/auth/login.py CHANGED
@@ -61,14 +61,14 @@ def login_internal():
61
61
  click.echo(f"✅ Identity token is valid")
62
62
 
63
63
  # Step 2: Exchange for a short-lived access token (default: 7 days)
64
- access_token, user_name = exchange_identity_token(identity_token, exchange_url, expires_in=604800)
64
+ access_token, user_name = exchange_identity_token(identity_token, exchange_url, expires_in=1209600)
65
65
 
66
66
  if not access_token:
67
67
  return click.echo("❌ Failed to acquire short-lived access token.")
68
68
 
69
69
  # Step 3: Save token to internal auth config
70
70
  set_auth_token(access_token, internal=True)
71
- click.echo(f"💾 Short-lived access token saved successfully for {user_name} (valid for 7 days).")
71
+ click.echo(f"💾 Short-lived access token saved successfully for {user_name} (valid for 14 days).")
72
72
 
73
73
 
74
74
  def _login_external():
sima_cli/cli.py CHANGED
@@ -7,6 +7,8 @@ from sima_cli.utils.config_loader import internal_resource_exists
7
7
  from sima_cli.mla.meminfo import monitor_simaai_mem_chart
8
8
  from sima_cli.__version__ import __version__
9
9
  from sima_cli.utils.config import CONFIG_PATH
10
+ from sima_cli.install.optiview import install_optiview
11
+ from sima_cli.install.hostdriver import install_hostdriver
10
12
 
11
13
  # Entry point for the CLI tool using Click's command group decorator
12
14
  @click.group()
@@ -150,9 +152,6 @@ def update(ctx, version_or_url, ip, yes, passwd):
150
152
  version_or_url: The version string (e.g. '1.5.0') or a direct URL to the firmware package.
151
153
  """
152
154
  internal = ctx.obj.get("internal", False)
153
- if not internal:
154
- click.echo("External environment is not supported yet.")
155
- exit(0)
156
155
  perform_update(version_or_url, ip, internal, passwd=passwd, auto_confirm=yes)
157
156
 
158
157
  # ----------------------
@@ -167,7 +166,7 @@ def modelzoo(ctx, ver):
167
166
  ctx.obj['ver'] = ver
168
167
  internal = ctx.obj.get("internal", False)
169
168
  if not internal:
170
- click.echo(f"external environment is not supported yet..")
169
+ click.echo(f"Public developer portal environment is not supported yet..")
171
170
  exit(0)
172
171
 
173
172
  pass
@@ -220,6 +219,84 @@ def show_mla_memory_usage(ctx):
220
219
  monitor_simaai_mem_chart()
221
220
  pass
222
221
 
222
+
223
+ # ----------------------
224
+ # bootimg Command
225
+ # ----------------------
226
+ @main.command(name="bootimg")
227
+ @click.option("-v", "--version", required=True, help="Firmware version to download and write (e.g., 1.6.0)")
228
+ @click.option("--boardtype", type=click.Choice(["modalix", "mlsoc"], case_sensitive=False), default="mlsoc", show_default=True, help="Target board type.")
229
+ @click.pass_context
230
+ def bootimg_cmd(ctx, version, boardtype):
231
+ """
232
+ Download and burn a bootable image onto removable media (e.g., SD card or USB stick).
233
+
234
+ Examples:
235
+ sima-cli bootimg -v 1.6.0
236
+ sima-cli bootimg -v 1.6.0 --boardtype mlsoc
237
+ sima-cli bootimg -v 1.6.0 --boardtype modalix
238
+ """
239
+
240
+ from sima_cli.update.bootimg import write_image
241
+
242
+ internal = ctx.obj.get("internal", False)
243
+
244
+ click.echo(f"📦 Preparing boot image:")
245
+ click.echo(f" 🔹 Version : {version}")
246
+ click.echo(f" 🔹 Board Type: {boardtype}")
247
+
248
+ try:
249
+ boardtype = boardtype if boardtype != 'mlsoc' else 'davinci'
250
+ write_image(version, boardtype, 'yocto', internal)
251
+ click.echo("✅ Boot image successfully written.")
252
+ except Exception as e:
253
+ click.echo(f"❌ Failed to write boot image: {e}", err=True)
254
+ ctx.exit(1)
255
+
256
+ # ----------------------
257
+ # install Command
258
+ # ----------------------
259
+ SDK_DEPENDENT_COMPONENTS = {"palette", "hostdriver"}
260
+ SDK_INDEPENDENT_COMPONENTS = {"optiview"}
261
+ ALL_COMPONENTS = SDK_DEPENDENT_COMPONENTS | SDK_INDEPENDENT_COMPONENTS
262
+
263
+ @main.command(name="install")
264
+ @click.argument("component", type=click.Choice(ALL_COMPONENTS, case_sensitive=False))
265
+ @click.option("-v", "--version", help="SDK version (required for SDK-dependent components)")
266
+ @click.pass_context
267
+ def install_cmd(ctx, component, version):
268
+ """
269
+ Install supported components such as SDKs or tools.
270
+
271
+ Examples:
272
+ cli install palette -v 1.6.0
273
+ cli install hostdriver -v 1.6.0
274
+
275
+ cli install optiview
276
+ """
277
+ component = component.lower()
278
+ internal = ctx.obj.get("internal", False)
279
+
280
+ if component in SDK_DEPENDENT_COMPONENTS and not version:
281
+ click.echo(f"❌ The component '{component}' requires a specific SDK version. Please provide one using -v.")
282
+ ctx.exit(1)
283
+
284
+ if component in SDK_INDEPENDENT_COMPONENTS and version:
285
+ click.echo(f"ℹ️ The component '{component}' does not require an SDK version. Ignoring -v {version}.")
286
+
287
+ # Perform the installation logic
288
+ if component == "palette":
289
+ click.echo(f"🔧 Installing SDK component 'palette' for version {version} is not implemented yet...")
290
+ elif component == "hostdriver":
291
+ click.echo(f"🔧 Installing SDK component 'hostdriver' for version {version}...")
292
+ install_hostdriver(version=version, internal=internal)
293
+ elif component == "optiview":
294
+ click.echo("🔧 Installing tool 'optiview'...")
295
+ install_optiview()
296
+
297
+ click.echo("✅ Installation complete.")
298
+
299
+
223
300
  # ----------------------
224
301
  # App Zoo Subcommands
225
302
  # ----------------------
@@ -6,4 +6,4 @@ auth:
6
6
  validate_url: "artifactory/api/security/encryptedPassword"
7
7
 
8
8
  download:
9
- download_url: "artifactory/"
9
+ download_url: "https://docs.sima.ai/pkg_downloads/"
@@ -127,6 +127,7 @@ def download_file_from_url(url: str, dest_folder: str = ".", internal: bool = Fa
127
127
 
128
128
  except Exception as e:
129
129
  raise RuntimeError(f"Download failed: {e}")
130
+
130
131
 
131
132
  return dest_path
132
133
 
File without changes
@@ -0,0 +1,152 @@
1
+ import sys
2
+ import click
3
+ import os
4
+ import subprocess
5
+ from pathlib import Path
6
+
7
+ from sima_cli.update.updater import download_image
8
+ from sima_cli.utils.env import is_pcie_host
9
+
10
+ def _check_driver_installation():
11
+ """
12
+ Checks whether the PCIe host driver (sima_mla_drv) and related components are correctly installed.
13
+
14
+ Returns:
15
+ dict: A dictionary with validation results for gstreamer, kernel module, and PCI device.
16
+ """
17
+ results = {}
18
+
19
+ # 1. Check GStreamer plugin
20
+ try:
21
+ gst_output = subprocess.check_output(["gst-inspect-1.0", "pciehost"], stderr=subprocess.STDOUT, text=True)
22
+ if "pciehost" in gst_output:
23
+ results["gstreamer"] = {
24
+ "success": True,
25
+ "message": "✅ GStreamer plugin 'pciehost' is installed."
26
+ }
27
+ else:
28
+ results["gstreamer"] = {
29
+ "success": False,
30
+ "message": "❌ GStreamer plugin 'pciehost' not found."
31
+ }
32
+ except subprocess.CalledProcessError as e:
33
+ results["gstreamer"] = {
34
+ "success": False,
35
+ "message": f"❌ GStreamer check failed: {e.output.strip()}"
36
+ }
37
+
38
+ # 2. Check kernel module
39
+ try:
40
+ modinfo_output = subprocess.check_output(["modinfo", "sima_mla_drv"], stderr=subprocess.STDOUT, text=True)
41
+ if "filename:" in modinfo_output and "sima_mla_drv" in modinfo_output:
42
+ results["kernel_module"] = {
43
+ "success": True,
44
+ "message": "✅ Kernel module 'sima_mla_drv' is installed."
45
+ }
46
+ else:
47
+ results["kernel_module"] = {
48
+ "success": False,
49
+ "message": "❌ Kernel module 'sima_mla_drv' not found."
50
+ }
51
+ except subprocess.CalledProcessError as e:
52
+ results["kernel_module"] = {
53
+ "success": False,
54
+ "message": f"❌ Kernel module check failed: {e.output.strip()}"
55
+ }
56
+
57
+ # 3. Check PCI device presence
58
+ try:
59
+ lspci_output = subprocess.check_output(["lspci", "-vd", "1f06:abcd"], stderr=subprocess.STDOUT, text=True)
60
+ if "sima_mla_drv" in lspci_output or "1f06:abcd" in lspci_output:
61
+ results["pci_device"] = {
62
+ "success": True,
63
+ "message": "✅ PCIe device (1f06:abcd) detected and bound to 'sima_mla_drv'."
64
+ }
65
+ else:
66
+ results["pci_device"] = {
67
+ "success": False,
68
+ "message": "❌ PCIe device not detected."
69
+ }
70
+ except subprocess.CalledProcessError as e:
71
+ results["pci_device"] = {
72
+ "success": False,
73
+ "message": f"❌ PCIe device check failed: {e.output.strip()}"
74
+ }
75
+
76
+ return results
77
+
78
+ def _print_driver_validation_table(results: dict):
79
+ """
80
+ Prints the driver validation results in a table format with fixed-width columns.
81
+ """
82
+ print("\nDriver Installation Validation:\n")
83
+ print(f"{'Component':<20} | {'Status':<10} | {'Details'}")
84
+ print("-" * 60)
85
+
86
+ for section, result in results.items():
87
+ component = section.replace("_", " ").title()
88
+ status = "PASS" if result["success"] else "FAIL"
89
+ message = result["message"]
90
+ print(f"{component:<20} | {status:<10} | {message}")
91
+
92
+ def install_hostdriver(version: str, internal: bool = False):
93
+ """
94
+ Install PCIe host driver on supported platforms.
95
+
96
+ This function is only valid on PCIe host machines. It downloads the appropriate image
97
+ package and installs the host driver script if present.
98
+
99
+ Args:
100
+ version (str): Firmware version string (e.g., "1.6.0").
101
+ internal (bool): Whether to use internal sources for the download.
102
+
103
+ Raises:
104
+ RuntimeError: If the platform is not supported or the driver script is missing.
105
+ """
106
+ if not is_pcie_host():
107
+ click.echo("❌ This command is only supported on PCIe host Linux machines.")
108
+ sys.exit(1)
109
+
110
+ try:
111
+ # Step 1: Install system dependencies
112
+ click.echo("🔧 Installing required system packages...")
113
+ try:
114
+ subprocess.run([
115
+ "sudo", "apt-get", "update"
116
+ ], check=True)
117
+ subprocess.run([
118
+ "sudo", "apt-get", "install", "-y",
119
+ "make", "cmake", "gcc", "g++", "dkms", "doxygen",
120
+ "libjson-c-dev", "libjsoncpp-dev", "build-essential", "linux-headers-generic"
121
+ ], check=True)
122
+ click.echo("✅ Dependencies installed successfully.")
123
+ except subprocess.CalledProcessError as e:
124
+ raise RuntimeError(f"❌ Failed to install dependencies: {e}")
125
+
126
+ # Step 2: Download driver package which is part of the firmware release.
127
+ click.echo(f"⬇️ Downloading host driver package for version {version}...")
128
+ extracted_files = download_image(version_or_url=version, board="davinci", swtype="yocto", internal=internal)
129
+
130
+ # Find the host driver script in extracted files
131
+ script_path = next((f for f in extracted_files if f.endswith("sima_pcie_host_pkg.sh")), None)
132
+
133
+ if not script_path or not os.path.isfile(script_path):
134
+ raise RuntimeError("sima_pcie_host_pkg.sh not found in the downloaded package.")
135
+
136
+ click.echo(f"📦 Host driver script found: {script_path}")
137
+ click.confirm("⚠️ This will install drivers on your system using sudo. Continue?", abort=True)
138
+
139
+ # Make sure the script is executable
140
+ os.chmod(script_path, 0o755)
141
+
142
+ # Run the script with sudo
143
+ subprocess.run(["sudo", script_path], check=True)
144
+
145
+ click.echo("✅ Host driver installation completed.")
146
+
147
+ results = _check_driver_installation()
148
+ _print_driver_validation_table(results)
149
+
150
+ except Exception as e:
151
+ raise RuntimeError(f"❌ Failed to install host driver: {e}")
152
+
@@ -0,0 +1,80 @@
1
+ import sys
2
+ import platform
3
+ import click
4
+ import urllib
5
+ import json
6
+ import tempfile
7
+ import tarfile
8
+ import os
9
+ import subprocess
10
+ from pathlib import Path
11
+ from sima_cli.download import download_file_from_url
12
+ from sima_cli.utils.env import is_sima_board
13
+ from sima_cli.utils.config_loader import load_resource_config
14
+
15
+ def install_optiview():
16
+ try:
17
+ # Special path for SiMa DevKit
18
+ if is_sima_board():
19
+ click.echo("🛠 Detected SiMa DevKit. Cleaning up existing installation...")
20
+ subprocess.run(["sudo", "pip3", "uninstall", "-y", "optiview"], check=True)
21
+ click.echo("📦 Installing Optiview via pip...")
22
+ subprocess.run(["sudo", "pip3", "install", "optiview"], check=True)
23
+ click.echo("✅ Optiview installed successfully on DevKit.")
24
+ return
25
+
26
+ cfg = load_resource_config()
27
+ download_url_base = cfg.get('public').get('download').get('download_url')
28
+
29
+ # Normal flow for other platforms
30
+ click.echo("📥 Fetching Optiview version info...")
31
+ version_url = f"{download_url_base}optiview/metadata.json"
32
+ downloads_dir = Path.home() / "Downloads" / "optiview-installer"
33
+ downloads_dir.mkdir(parents=True, exist_ok=True)
34
+ metadata_path = download_file_from_url(version_url, dest_folder=downloads_dir, internal=False)
35
+
36
+ with open(metadata_path, "r") as f:
37
+ version_data = json.load(f)
38
+ latest_version = version_data.get("latest")
39
+
40
+ if not latest_version:
41
+ raise Exception("Unable to retrieve latest version info.")
42
+
43
+ # Determine architecture
44
+ system = platform.system().lower()
45
+ machine = platform.machine().lower()
46
+
47
+ if system == "darwin" and machine == "arm64":
48
+ arch = "aarch64"
49
+ else:
50
+ arch = "x86_64"
51
+
52
+ pkg_url = f"{download_url_base}optiview/optiview-installation-{arch}-{latest_version}.tar.gz"
53
+ click.echo(f"🌐 Downloading Optiview ({arch} v{latest_version})...")
54
+
55
+ archive_path = download_file_from_url(pkg_url, dest_folder=downloads_dir)
56
+
57
+ click.echo(f"📦 Extracting to {downloads_dir}...")
58
+ with tarfile.open(archive_path, "r:gz") as tar:
59
+ tar.extractall(path=downloads_dir)
60
+
61
+ install_script = "install.bat" if system == "windows" else "install.sh"
62
+ script_path = os.path.join(downloads_dir, install_script)
63
+
64
+ if not os.path.isfile(script_path):
65
+ raise Exception(f"Installation script not found: {script_path}")
66
+
67
+ click.echo(f"🚀 Running installer: {install_script}")
68
+ if system == "windows":
69
+ subprocess.run(["cmd", "/c", os.path.basename(script_path)], check=True, cwd=downloads_dir)
70
+ else:
71
+ subprocess.run(["bash", os.path.basename(script_path)], check=True, cwd=downloads_dir)
72
+
73
+ click.echo(f"✅ Optiview installed successfully. Run {downloads_dir}/run.sh to start optiview")
74
+
75
+ except Exception as e:
76
+ click.echo(f"❌ Installation failed: {e}")
77
+ sys.exit(1)
78
+
79
+ if __name__ == "__main__":
80
+ install_optiview()
@@ -0,0 +1,87 @@
1
+ import sys
2
+ import platform
3
+ import click
4
+ import json
5
+ import tarfile
6
+ import os
7
+ import subprocess
8
+ from pathlib import Path
9
+ from sima_cli.download import download_file_from_url
10
+ from sima_cli.utils.env import is_sima_board
11
+ from sima_cli.utils.config_loader import load_resource_config
12
+
13
+ def build_internal_url(download_url_base, latest_filename):
14
+ """Build the URL for internal Palette package."""
15
+ return f"{download_url_base}palette/{latest_filename}"
16
+
17
+ def build_external_url(download_url_base, latest_filename, version):
18
+ """Build the URL for external Palette package from SDKx.y.z folder."""
19
+ return f"{download_url_base}palette/SDK{version}/{latest_filename}"
20
+
21
+ def install_palette(internal=False):
22
+ try:
23
+ # Check for unsupported platforms
24
+ system = platform.system().lower()
25
+ if is_sima_board():
26
+ click.echo("❌ Palette installation is not supported on SiMa DevKit, please install on Ubuntu 22.04 or Windows 10/11.")
27
+ sys.exit(1)
28
+ if system == "darwin":
29
+ click.echo("❌ Palette installation is not supported on macOS, please install on Ubuntu 22.04 or Windows 10/11.")
30
+ sys.exit(1)
31
+
32
+ # Load configuration for resources
33
+ cfg = load_resource_config()
34
+ download_url_base = cfg.get('internal' if internal else 'public').get('download').get('download_url')
35
+
36
+ # Fetch Palette version info
37
+ click.echo(f"📥 Fetching Palette version info ({'internal' if internal else 'external'})...")
38
+ version_url = f"{download_url_base}palette/metadata.json"
39
+ downloads_dir = Path.home() / "Downloads" / "palette-installer"
40
+ downloads_dir.mkdir(parents=True, exist_ok=True)
41
+ metadata_path = download_file_from_url(version_url, dest_folder=downloads_dir, internal=internal)
42
+
43
+ # Read version data
44
+ with open(metadata_path, "r") as f:
45
+ version_data = json.load(f)
46
+ latest_filename = version_data.get("latest")
47
+
48
+ if not latest_filename:
49
+ raise Exception("Unable to retrieve latest filename info.")
50
+
51
+ # Extract version from filename (assuming format like palette-installation-<arch>-x.y.z.tar.gz)
52
+ version = latest_filename.split('-')[-1].replace('.tar.gz', '')
53
+
54
+ # Build package URL based on internal or external
55
+ pkg_url = build_internal_url(download_url_base, latest_filename) if internal else build_external_url(download_url_base, latest_filename, version)
56
+ click.echo(f"🌐 Downloading Palette ({'internal' if internal else 'external'}: {latest_filename})...")
57
+
58
+ # Download the package
59
+ archive_path = download_file_from_url(pkg_url, dest_folder=downloads_dir, internal=internal)
60
+
61
+ # Extract the package
62
+ click.echo(f"📦 Extracting to {downloads_dir}...")
63
+ with tarfile.open(archive_path, "r:gz") as tar:
64
+ tar.extractall(path=downloads_dir)
65
+
66
+ # Determine installation script based on platform
67
+ install_script = "install.bat" if system == "windows" else "install.sh"
68
+ script_path = os.path.join(downloads_dir, install_script)
69
+
70
+ if not os.path.isfile(script_path):
71
+ raise Exception(f"Installation script not found: {script_path}")
72
+
73
+ # Run the installer
74
+ click.echo(f"🚀 Running installer: {install_script}")
75
+ if system == "windows":
76
+ subprocess.run(["cmd", "/c", os.path.basename(script_path)], check=True, cwd=downloads_dir)
77
+ else:
78
+ subprocess.run(["bash", os.path.basename(script_path)], check=True, cwd=downloads_dir)
79
+
80
+ click.echo(f"✅ Palette installed successfully ({'internal' if internal else 'external'}). Run {downloads_dir}/run.sh to start palette")
81
+
82
+ except Exception as e:
83
+ click.echo(f"❌ {'Internal' if internal else 'External'} installation failed: {e}")
84
+ sys.exit(1)
85
+
86
+ if __name__ == "__main__":
87
+ install_palette(internal=False)
@@ -0,0 +1,137 @@
1
+ import subprocess
2
+ import sys
3
+ import os
4
+ import urllib.request
5
+ import tarfile
6
+ import tempfile
7
+ import json
8
+ import shutil
9
+
10
+ def run_command(command, check=True, shell=False, cwd=None):
11
+ """Run a shell command and return its output or raise an exception if it fails."""
12
+ try:
13
+ result = subprocess.run(
14
+ command,
15
+ check=check,
16
+ shell=shell,
17
+ stdout=subprocess.PIPE,
18
+ stderr=subprocess.PIPE,
19
+ text=True,
20
+ cwd=cwd
21
+ )
22
+ return result.stdout.strip(), result.stderr.strip()
23
+ except subprocess.CalledProcessError as e:
24
+ if check:
25
+ print(f"Error running command {command}: {e.stderr}")
26
+ raise
27
+ return "", str(e)
28
+
29
+ def is_bmaptool_installed():
30
+ """Check if bmaptool is installed and callable from the shell."""
31
+ try:
32
+ result = subprocess.run(
33
+ ["bmaptool", "--version"],
34
+ stdout=subprocess.PIPE,
35
+ stderr=subprocess.PIPE,
36
+ text=True,
37
+ timeout=5
38
+ )
39
+ if result.returncode == 0:
40
+ print(f"✅ bmaptool is installed: {result.stdout.strip()}")
41
+ return True
42
+ else:
43
+ print(f"⚠️ bmaptool returned non-zero exit: {result.stderr.strip()}")
44
+ return False
45
+ except (FileNotFoundError, subprocess.TimeoutExpired) as e:
46
+ print(f"❌ bmaptool is not installed or not in PATH: {e}")
47
+ return False
48
+
49
+ def clone_bmaptool_repo(temp_dir, branch_or_tag="main"):
50
+ """Clone the bmaptool repository to a temporary directory."""
51
+ repo_url = "https://github.com/yoctoproject/bmaptool.git"
52
+ print(f"Cloning bmaptool from {repo_url} (branch/tag: {branch_or_tag})...")
53
+ try:
54
+ subprocess.run(
55
+ ["git", "clone", "--depth", "1", "--branch", branch_or_tag, repo_url, temp_dir],
56
+ check=True,
57
+ capture_output=True,
58
+ text=True
59
+ )
60
+ print(f"Cloned bmaptool to {temp_dir}")
61
+ return temp_dir
62
+ except subprocess.CalledProcessError as e:
63
+ print(f"Failed to clone repository: {e.stderr}")
64
+ sys.exit(1)
65
+
66
+ def get_python_and_pip_bins():
67
+ """Get the Python and pip binaries from the active virtual environment or system."""
68
+ if "VIRTUAL_ENV" in os.environ:
69
+ venv_dir = os.environ["VIRTUAL_ENV"]
70
+ print(f"Using existing virtual environment: {venv_dir}")
71
+ if sys.platform == "win32":
72
+ python_bin = os.path.join(venv_dir, "Scripts", "python.exe")
73
+ pip_bin = os.path.join(venv_dir, "Scripts", "pip.exe")
74
+ else:
75
+ python_bin = os.path.join(venv_dir, "bin", "python3")
76
+ pip_bin = os.path.join(venv_dir, "bin", "pip3")
77
+ else:
78
+ print("No virtual environment detected; using system Python.")
79
+ python_bin = shutil.which("python3") or sys.executable
80
+ pip_bin = shutil.which("pip3") or "pip3"
81
+ return python_bin, pip_bin
82
+
83
+ def install_bmaptool(temp_dir):
84
+ """Install bmaptool from the cloned repository."""
85
+ try:
86
+ subprocess.run(
87
+ [sys.executable, "-m", "pip", "install", temp_dir],
88
+ check=True,
89
+ capture_output=True,
90
+ text=True
91
+ )
92
+ print("Installed bmaptool successfully")
93
+ except subprocess.CalledProcessError as e:
94
+ print(f"Failed to install bmaptool: {e.stderr}")
95
+ sys.exit(1)
96
+
97
+ def verify_installation(python_bin):
98
+ """Verify bmaptool installation by running python -m bmaptool --version."""
99
+ stdout, stderr = run_command([python_bin, "-m", "bmaptool", "--version"])
100
+ if stdout:
101
+ print(f"bmaptool installation verified: {stdout}")
102
+ else:
103
+ print(f"Verification failed: {stderr}")
104
+ sys.exit(1)
105
+
106
+ def check_bmap_and_install():
107
+ # Get Python and pip binaries from the active virtual environment or system
108
+ python_bin, _ = get_python_and_pip_bins()
109
+
110
+ # Check if bmaptool is already installed
111
+ if is_bmaptool_installed():
112
+ return
113
+
114
+ # Create a temporary directory
115
+ temp_dir = "bmaptool_temp"
116
+ with tempfile.TemporaryDirectory() as temp_dir:
117
+ if os.path.exists(temp_dir):
118
+ shutil.rmtree(temp_dir)
119
+ os.makedirs(temp_dir)
120
+
121
+ clone_bmaptool_repo(temp_dir, branch_or_tag="main") # Or specify a tag like "v3.8"
122
+ install_bmaptool(temp_dir)
123
+
124
+ # Verify installation
125
+ verify_installation(python_bin)
126
+
127
+ # Provide usage instructions
128
+ if "VIRTUAL_ENV" in os.environ:
129
+ print(f"\nbmaptool is installed in the virtual environment at {os.environ['VIRTUAL_ENV']}")
130
+ print("The virtual environment is already active.")
131
+ print("Run 'bmaptool --help' to see available commands.")
132
+ else:
133
+ print("\nbmaptool is installed in the system Python environment.")
134
+ print("Run 'bmaptool --help' to see available commands.")
135
+
136
+ if __name__ == "__main__":
137
+ check_bmap_and_install()