build.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import warnings
  2. import os
  3. import platform
  4. import subprocess
  5. import time
  6. import threading
  7. # Ignore specific SyntaxWarning
  8. warnings.filterwarnings("ignore", category=SyntaxWarning, module="DrissionPage")
  9. CURSOR_LOGO = """
  10. ██████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗
  11. ██╔════╝██║ ██║██╔══██╗██╔════╝██╔═══██╗██╔══██╗
  12. ██║ ██║ ██║██████╔╝███████╗██║ ██║██████╔╝
  13. ██║ ██║ ██║██╔══██╗╚════██║██║ ██║██╔══██╗
  14. ╚██████╗╚██████╔╝██║ ██║███████║╚██████╔╝██║ ██║
  15. ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝
  16. """
  17. class LoadingAnimation:
  18. def __init__(self):
  19. self.is_running = False
  20. self.animation_thread = None
  21. def start(self, message="Building"):
  22. self.is_running = True
  23. self.animation_thread = threading.Thread(target=self._animate, args=(message,))
  24. self.animation_thread.start()
  25. def stop(self):
  26. self.is_running = False
  27. if self.animation_thread:
  28. self.animation_thread.join()
  29. print("\r" + " " * 70 + "\r", end="", flush=True) # Clear the line
  30. def _animate(self, message):
  31. animation = "|/-\\"
  32. idx = 0
  33. while self.is_running:
  34. print(f"\r{message} {animation[idx % len(animation)]}", end="", flush=True)
  35. idx += 1
  36. time.sleep(0.1)
  37. def print_logo():
  38. print("\033[96m" + CURSOR_LOGO + "\033[0m")
  39. print("\033[93m" + "Building Cursor Keep Alive...".center(56) + "\033[0m\n")
  40. def progress_bar(progress, total, prefix="", length=50):
  41. filled = int(length * progress // total)
  42. bar = "█" * filled + "░" * (length - filled)
  43. percent = f"{100 * progress / total:.1f}"
  44. print(f"\r{prefix} |{bar}| {percent}% Complete", end="", flush=True)
  45. if progress == total:
  46. print()
  47. def simulate_progress(message, duration=1.0, steps=20):
  48. print(f"\033[94m{message}\033[0m")
  49. for i in range(steps + 1):
  50. time.sleep(duration / steps)
  51. progress_bar(i, steps, prefix="Progress:", length=40)
  52. def filter_output(output):
  53. """ImportantMessage"""
  54. if not output:
  55. return ""
  56. important_lines = []
  57. for line in output.split("\n"):
  58. # Only keep lines containing specific keywords
  59. if any(
  60. keyword in line.lower()
  61. for keyword in ["error:", "failed:", "completed", "directory:"]
  62. ):
  63. important_lines.append(line)
  64. return "\n".join(important_lines)
  65. def build():
  66. # Clear screen
  67. os.system("cls" if platform.system().lower() == "windows" else "clear")
  68. # Print logo
  69. print_logo()
  70. system = platform.system().lower()
  71. spec_file = os.path.join("CursorKeepAlive.spec")
  72. # if system not in ["darwin", "windows"]:
  73. # print(f"\033[91mUnsupported operating system: {system}\033[0m")
  74. # return
  75. output_dir = f"dist/{system if system != 'darwin' else 'mac'}"
  76. # Create output directory
  77. os.makedirs(output_dir, exist_ok=True)
  78. simulate_progress("Creating output directory...", 0.5)
  79. # Run PyInstaller with loading animation
  80. pyinstaller_command = [
  81. "pyinstaller",
  82. spec_file,
  83. "--distpath",
  84. output_dir,
  85. "--workpath",
  86. f"build/{system}",
  87. "--noconfirm",
  88. ]
  89. loading = LoadingAnimation()
  90. try:
  91. simulate_progress("Running PyInstaller...", 2.0)
  92. loading.start("Building in progress")
  93. result = subprocess.run(
  94. pyinstaller_command, check=True, capture_output=True, text=True
  95. )
  96. loading.stop()
  97. if result.stderr:
  98. filtered_errors = [
  99. line
  100. for line in result.stderr.split("\n")
  101. if any(
  102. keyword in line.lower()
  103. for keyword in ["error:", "failed:", "completed", "directory:"]
  104. )
  105. ]
  106. if filtered_errors:
  107. print("\033[93mBuild Warnings/Errors:\033[0m")
  108. print("\n".join(filtered_errors))
  109. except subprocess.CalledProcessError as e:
  110. loading.stop()
  111. print(f"\033[91mBuild failed with error code {e.returncode}\033[0m")
  112. if e.stderr:
  113. print("\033[91mError Details:\033[0m")
  114. print(e.stderr)
  115. return
  116. except FileNotFoundError:
  117. loading.stop()
  118. print(
  119. "\033[91mError: Please ensure PyInstaller is installed (pip install pyinstaller)\033[0m"
  120. )
  121. return
  122. except KeyboardInterrupt:
  123. loading.stop()
  124. print("\n\033[91mBuild cancelled by user\033[0m")
  125. return
  126. finally:
  127. loading.stop()
  128. # Copy config file
  129. if os.path.exists("config.ini.example"):
  130. simulate_progress("Copying configuration file...", 0.5)
  131. if system == "windows":
  132. subprocess.run(
  133. ["copy", "config.ini.example", f"{output_dir}\\config.ini"], shell=True
  134. )
  135. else:
  136. subprocess.run(["cp", "config.ini.example", f"{output_dir}/config.ini"])
  137. # Copy .env.example file
  138. if os.path.exists(".env.example"):
  139. simulate_progress("Copying environment file...", 0.5)
  140. if system == "windows":
  141. subprocess.run(["copy", ".env.example", f"{output_dir}\\.env"], shell=True)
  142. else:
  143. subprocess.run(["cp", ".env.example", f"{output_dir}/.env"])
  144. print(
  145. f"\n\033[92mBuild completed successfully! Output directory: {output_dir}\033[0m"
  146. )
  147. if __name__ == "__main__":
  148. build()