justfile 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. set unstable
  2. set windows-shell := ["powershell.exe", "-c"]
  3. set script-interpreter := ["python"]
  4. # Tools
  5. python := require(if os_family() == "windows" { "python.exe" } else { "python" })
  6. pip := which(if os_family() == "windows" { "pip.exe" } else { "pip" })
  7. pdm := which(if os_family() == "windows" { "pdm.exe" } else { "pdm" })
  8. lrelease := which(if os_family() == "windows" { \
  9. `pdm run python -c "
  10. import os
  11. try:
  12. import PySide6
  13. print(os.path.dirname(PySide6.__file__))
  14. except ModuleNotFoundError:
  15. print('.')
  16. "` / 'lrelease.exe'
  17. } else if path_exists("/usr/lib64/qt6/bin/lrelease") == "true" {
  18. "/usr/lib64/qt6/bin/lrelease"
  19. } else if path_exists("/usr/lib/qt6/bin/lrelease") == "true" {
  20. "/usr/lib/qt6/bin/lrelease"
  21. } else {
  22. "lrelease"
  23. })
  24. # Env
  25. dest_dir := env("DESTDIR", "/usr")
  26. python_lib := env("PYTHONLIBDIR", `python -c 'import site; print(site.getsitepackages()[0])'`)
  27. python_venv := env("VIRTUAL_ENV", "")
  28. flatpak_dir := absolute_path(env("FLATPAKBUILDDIR", './.flatpak'))
  29. # Version auto-detect
  30. version := `python -c "import tomllib
  31. try:
  32. print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])
  33. except Exception:
  34. print('0.0')
  35. "`
  36. # ------------------------------------------------
  37. # Shortcuts
  38. # ------------------------------------------------
  39. # List available actions
  40. @default:
  41. just -l
  42. # Start Qt version without instalation
  43. start:
  44. just build
  45. pdm run openfreebuds_qt -vcs
  46. # Start command shell without instalation
  47. start_cmd:
  48. pdm run openfreebuds_cmd
  49. # Start PyTest
  50. test:
  51. pdm run pytest
  52. # Prepare project environment (all build dependencies should exist at this time)
  53. prepare:
  54. pdm install
  55. vg_all:
  56. vagrant halt -f
  57. vagrant up --parallel
  58. # ------------------------------------------------
  59. # Project auto-install staff
  60. # ------------------------------------------------
  61. # Install OpenFreebuds
  62. [linux]
  63. install: install_check
  64. mkdir -p "{{python_lib}}"
  65. {{pip}} install -q --upgrade --no-dependencies --target "{{python_lib}}" \
  66. "./dist/openfreebuds-{{version}}-py3-none-any.whl"
  67. mkdir -p "{{dest_dir}}/bin" \
  68. "{{dest_dir}}/share/applications" \
  69. "{{dest_dir}}/share/metainfo" \
  70. "{{dest_dir}}/share/icons/hicolor/256x256/apps"
  71. # Install binaries
  72. cp "{{python_lib}}/bin/openfreebuds_qt" "{{dest_dir}}/bin/openfreebuds_qt"
  73. cp "{{python_lib}}/bin/openfreebuds_cmd" "{{dest_dir}}/bin/openfreebuds_cmd"
  74. ln -sf ./openfreebuds_qt "{{dest_dir}}/bin/openfreebuds"
  75. # Install desktop integration
  76. cp "{{python_lib}}/openfreebuds_qt/assets/pw.mmk.OpenFreebuds.desktop" \
  77. "{{dest_dir}}/share/applications"
  78. cp "{{python_lib}}/openfreebuds_qt/assets/pw.mmk.OpenFreebuds.metainfo.xml" \
  79. "{{dest_dir}}/share/metainfo"
  80. cp "{{python_lib}}/openfreebuds_qt/assets/pw.mmk.OpenFreebuds.png" \
  81. "{{dest_dir}}/share/icons/hicolor/256x256/apps"
  82. # Check Linux instalation restrictions
  83. [private,linux,script]
  84. install_check:
  85. import os
  86. assert os.path.isfile("./dist/openfreebuds-{{version}}-py3-none-any.whl"), \
  87. "Prebuilt wheel not found, did you called `just build` before?"
  88. assert "{{python_venv}}" == "" or os.environ.get("PYTHONLIBDIR", "") != "", \
  89. "Leave virtualenv or set PYTHONLIBDIR to install"
  90. # ------------------------------------------------
  91. # Project build staff
  92. # ------------------------------------------------
  93. # Build everything
  94. build: qt_i18n
  95. pdm run pyuic6 ./openfreebuds_qt/designer/
  96. pdm build
  97. # Compile Qt Linguist translation files
  98. [script]
  99. qt_i18n:
  100. import subprocess
  101. from pathlib import Path
  102. files_to_compile = []
  103. command = [r"{{lrelease}}"]
  104. for translation in Path("openfreebuds_qt/assets/i18n").iterdir():
  105. if translation.name.endswith(".ts"):
  106. command.append(str(translation))
  107. print("{{BOLD}}" + ' '.join(command) + "{{NORMAL}}")
  108. subprocess.run(command)
  109. # Check that all build requirements are resolved
  110. [private,script]
  111. build_check:
  112. assert "{{pdm}}" != "", "PDM not found"
  113. assert "{{lrelease}}" != "", "Qt lrelease not found"
  114. # ------------------------------------------------
  115. # Flatpak linux packaging
  116. # ------------------------------------------------
  117. # Install OpenFreebuds as Flatpak package
  118. [linux]
  119. flatpak_install: build
  120. mkdir -p {{flatpak_dir}}
  121. echo "*" > {{flatpak_dir}}/.gitignore
  122. flatpak run org.flatpak.Builder \
  123. --force-clean --user --install \
  124. --install-deps-from=flathub \
  125. --repo={{flatpak_dir}}/repo \
  126. --state-dir={{flatpak_dir}}/state \
  127. {{flatpak_dir}}/builddir \
  128. ./scripts/pw.mmk.OpenFreebuds.yml
  129. # Build Flatpak package file
  130. [linux]
  131. flatpak: flatpak_install
  132. mkdir -p dist/flatpak
  133. flatpak build-bundle \
  134. {{flatpak_dir}}/repo \
  135. ./dist/flatpak/openfreebuds_{{version}}.flatapk \
  136. pw.mmk.OpenFreebuds
  137. # Sync Python dependencies for Flatpak
  138. [linux]
  139. sync_flatpak:
  140. # TODO: Move inside Justfile
  141. mkdir -p ./.flatpak
  142. pdm run ./scripts/bump_version.py flatpak_deps
  143. # (Internal) Install OpenFreebuds inside Flatpak
  144. [private,linux]
  145. internal_flatpakinstall:
  146. # Unify release name (version constant won't work inside Flatpak)
  147. mkdir -p ./dist
  148. find ./dist -name '*.whl' -type f | head -1 | \
  149. xargs -I {} cp {} ./dist/openfreebuds-0.0-py3-none-any.whl
  150. # Install to /app
  151. touch /app/is_container
  152. DESTDIR=/app PYTHONLIBDIR=/app/lib/python3.11/site-packages just install
  153. # ------------------------------------------------
  154. # Debian staff
  155. # ------------------------------------------------
  156. # Build Debian binary package
  157. [linux]
  158. debian:
  159. mkdir -p dist/debian
  160. dpkg-buildpackage -b
  161. cp ../openfreebuds_*.deb ./dist
  162. # Build Debian source and binary package (use with causion, may remove files in parent folder)
  163. [linux]
  164. debian_full:
  165. mkdir -p dist/debian
  166. # Remove old build artifacts
  167. bash -c 'rm -f ../openfreebuds_*.{deb,changes,buildinfo,dsc} dist/debian/*'
  168. # Build and move to ./dist/debian
  169. dpkg-buildpackage -b
  170. cp ../openfreebuds_* ./dist/debian/
  171. # Install build dependencies for Debian\Ubuntu
  172. [linux]
  173. deps_debian:
  174. sudo apt install -y --no-install-recommends \
  175. build-essential \
  176. debhelper-compat \
  177. python3-dbus-next \
  178. python3-pyqt6 \
  179. python3-pil \
  180. python3-qasync \
  181. python3-aiohttp \
  182. python3-psutil \
  183. python3-pip \
  184. qt6-l10n-tools \
  185. fakeroot \
  186. make \
  187. git
  188. # ------------------------------------------------
  189. # Windows staff
  190. # ------------------------------------------------
  191. # Make windows portable & installer
  192. [windows]
  193. win32: win32_installer win32_portable
  194. # Make windows installer
  195. [windows]
  196. win32_installer: win32_bundle
  197. New-Item -ItemType Directory -Force -Path ./dist
  198. cd ./scripts; & "C:\Program Files (x86)\NSIS\Bin\makensis.exe" openfreebuds.nsi
  199. mv -Force ./scripts/dist/openfreebuds.install.exe ./dist/openfreebuds_{{version}}_win32.exe
  200. # Make windows portable executable
  201. [windows]
  202. win32_portable:
  203. New-Item -ItemType Directory -Force -Path ./dist
  204. cd ./scripts; & pdm run pyinstaller -y openfreebuds_portable.spec
  205. mv -Force ./scripts/dist/openfreebuds_portable.exe ./dist/openfreebuds_{{version}}_win32_portable.exe
  206. # Prepare ./dist/ for win32 installer build
  207. [windows,private]
  208. win32_bundle:
  209. cd ./scripts; pdm run pyinstaller -y openfreebuds.spec
  210. # ------------------------------------------------
  211. # Project maintainer staff
  212. # ------------------------------------------------
  213. # Bump application version
  214. [positional-arguments]
  215. bump_version code:
  216. python ./scripts/bump_version.py {{code}}
  217. # Bump application version to git commit identifier
  218. bump_version_git:
  219. python ./scripts/bump_version.py git
  220. # Sync Qt Linguist translation files
  221. [script]
  222. sync_qt_translations:
  223. import subprocess
  224. from pathlib import Path
  225. for ts_file in Path("./openfreebuds_qt/assets/i18n").iterdir():
  226. if not ts_file.name.endswith(".ts"):
  227. continue
  228. print(f"Sync {ts_file}")
  229. result = subprocess.run([r"{{pdm}}", "run", "pylupdate6",
  230. "--no-obsolete",
  231. "--exclude", "scripts",
  232. "--exclude", "debian",
  233. "--ts", ts_file, "."])
  234. if result.returncode != 0:
  235. print("-- pylupdate6 failed")
  236. raise SystemExit(1)