build.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648
  1. #!/usr/bin/env python3
  2. import os
  3. import pathlib
  4. import platform
  5. import zipfile
  6. import urllib.request
  7. import shutil
  8. import hashlib
  9. import argparse
  10. import sys
  11. from pathlib import Path
  12. windows = platform.platform().startswith('Windows')
  13. osx = platform.platform().startswith(
  14. 'Darwin') or platform.platform().startswith("macOS")
  15. hbb_name = 'rustdesk' + ('.exe' if windows else '')
  16. exe_path = 'target/release/' + hbb_name
  17. if windows:
  18. flutter_build_dir = 'build/windows/x64/runner/Release/'
  19. elif osx:
  20. flutter_build_dir = 'build/macos/Build/Products/Release/'
  21. else:
  22. flutter_build_dir = 'build/linux/x64/release/bundle/'
  23. flutter_build_dir_2 = f'flutter/{flutter_build_dir}'
  24. skip_cargo = False
  25. def get_deb_arch() -> str:
  26. custom_arch = os.environ.get("DEB_ARCH")
  27. if custom_arch is None:
  28. return "amd64"
  29. return custom_arch
  30. def get_deb_extra_depends() -> str:
  31. custom_arch = os.environ.get("DEB_ARCH")
  32. if custom_arch == "armhf": # for arm32v7 libsciter-gtk.so
  33. return ", libatomic1"
  34. return ""
  35. def system2(cmd):
  36. exit_code = os.system(cmd)
  37. if exit_code != 0:
  38. sys.stderr.write(f"Error occurred when executing: `{cmd}`. Exiting.\n")
  39. sys.exit(-1)
  40. def get_version():
  41. with open("Cargo.toml", encoding="utf-8") as fh:
  42. for line in fh:
  43. if line.startswith("version"):
  44. return line.replace("version", "").replace("=", "").replace('"', '').strip()
  45. return ''
  46. def parse_rc_features(feature):
  47. available_features = {}
  48. apply_features = {}
  49. if not feature:
  50. feature = []
  51. def platform_check(platforms):
  52. if windows:
  53. return 'windows' in platforms
  54. elif osx:
  55. return 'osx' in platforms
  56. else:
  57. return 'linux' in platforms
  58. def get_all_features():
  59. features = []
  60. for (feat, feat_info) in available_features.items():
  61. if platform_check(feat_info['platform']):
  62. features.append(feat)
  63. return features
  64. if isinstance(feature, str) and feature.upper() == 'ALL':
  65. return get_all_features()
  66. elif isinstance(feature, list):
  67. if windows:
  68. # download third party is deprecated, we use github ci instead.
  69. # feature.append('PrivacyMode')
  70. pass
  71. for feat in feature:
  72. if isinstance(feat, str) and feat.upper() == 'ALL':
  73. return get_all_features()
  74. if feat in available_features:
  75. if platform_check(available_features[feat]['platform']):
  76. apply_features[feat] = available_features[feat]
  77. else:
  78. print(f'Unrecognized feature {feat}')
  79. return apply_features
  80. else:
  81. raise Exception(f'Unsupported features param {feature}')
  82. def make_parser():
  83. parser = argparse.ArgumentParser(description='Build script.')
  84. parser.add_argument(
  85. '-f',
  86. '--feature',
  87. dest='feature',
  88. metavar='N',
  89. type=str,
  90. nargs='+',
  91. default='',
  92. help='Integrate features, windows only.'
  93. 'Available: [Not used for now]. Special value is "ALL" and empty "". Default is empty.')
  94. parser.add_argument('--flutter', action='store_true',
  95. help='Build flutter package', default=False)
  96. parser.add_argument(
  97. '--hwcodec',
  98. action='store_true',
  99. help='Enable feature hwcodec' + (
  100. '' if windows or osx else ', need libva-dev.')
  101. )
  102. parser.add_argument(
  103. '--vram',
  104. action='store_true',
  105. help='Enable feature vram, only available on windows now.'
  106. )
  107. parser.add_argument(
  108. '--portable',
  109. action='store_true',
  110. help='Build windows portable'
  111. )
  112. parser.add_argument(
  113. '--unix-file-copy-paste',
  114. action='store_true',
  115. help='Build with unix file copy paste feature'
  116. )
  117. parser.add_argument(
  118. '--skip-cargo',
  119. action='store_true',
  120. help='Skip cargo build process, only flutter version + Linux supported currently'
  121. )
  122. if windows:
  123. parser.add_argument(
  124. '--skip-portable-pack',
  125. action='store_true',
  126. help='Skip packing, only flutter version + Windows supported'
  127. )
  128. parser.add_argument(
  129. "--package",
  130. type=str
  131. )
  132. if osx:
  133. parser.add_argument(
  134. '--screencapturekit',
  135. action='store_true',
  136. help='Enable feature screencapturekit'
  137. )
  138. return parser
  139. # Generate build script for docker
  140. #
  141. # it assumes all build dependencies are installed in environments
  142. # Note: do not use it in bare metal, or may break build environments
  143. def generate_build_script_for_docker():
  144. with open("/tmp/build.sh", "w") as f:
  145. f.write('''
  146. #!/bin/bash
  147. # environment
  148. export CPATH="$(clang -v 2>&1 | grep "Selected GCC installation: " | cut -d' ' -f4-)/include"
  149. # flutter
  150. pushd /opt
  151. wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.0.5-stable.tar.xz
  152. tar -xvf flutter_linux_3.0.5-stable.tar.xz
  153. export PATH=`pwd`/flutter/bin:$PATH
  154. popd
  155. # flutter_rust_bridge
  156. dart pub global activate ffigen --version 5.0.1
  157. pushd /tmp && git clone https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge --depth=1 && popd
  158. pushd /tmp/flutter_rust_bridge/frb_codegen && cargo install --path . && popd
  159. pushd flutter && flutter pub get && popd
  160. ~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart
  161. # install vcpkg
  162. pushd /opt
  163. export VCPKG_ROOT=`pwd`/vcpkg
  164. git clone https://github.com/microsoft/vcpkg
  165. vcpkg/bootstrap-vcpkg.sh
  166. popd
  167. $VCPKG_ROOT/vcpkg install --x-install-root="$VCPKG_ROOT/installed"
  168. # build rustdesk
  169. ./build.py --flutter --hwcodec
  170. ''')
  171. system2("chmod +x /tmp/build.sh")
  172. system2("bash /tmp/build.sh")
  173. # Downloading third party resources is deprecated.
  174. # We can use this function in an offline build environment.
  175. # Even in an online environment, we recommend building third-party resources yourself.
  176. def download_extract_features(features, res_dir):
  177. import re
  178. proxy = ''
  179. def req(url):
  180. if not proxy:
  181. return url
  182. else:
  183. r = urllib.request.Request(url)
  184. r.set_proxy(proxy, 'http')
  185. r.set_proxy(proxy, 'https')
  186. return r
  187. for (feat, feat_info) in features.items():
  188. includes = feat_info['include'] if 'include' in feat_info and feat_info['include'] else []
  189. includes = [re.compile(p) for p in includes]
  190. excludes = feat_info['exclude'] if 'exclude' in feat_info and feat_info['exclude'] else []
  191. excludes = [re.compile(p) for p in excludes]
  192. print(f'{feat} download begin')
  193. download_filename = feat_info['zip_url'].split('/')[-1]
  194. checksum_md5_response = urllib.request.urlopen(
  195. req(feat_info['checksum_url']))
  196. for line in checksum_md5_response.read().decode('utf-8').splitlines():
  197. if line.split()[1] == download_filename:
  198. checksum_md5 = line.split()[0]
  199. filename, _headers = urllib.request.urlretrieve(feat_info['zip_url'],
  200. download_filename)
  201. md5 = hashlib.md5(open(filename, 'rb').read()).hexdigest()
  202. if checksum_md5 != md5:
  203. raise Exception(f'{feat} download failed')
  204. print(f'{feat} download end. extract bein')
  205. zip_file = zipfile.ZipFile(filename)
  206. zip_list = zip_file.namelist()
  207. for f in zip_list:
  208. file_exclude = False
  209. for p in excludes:
  210. if p.match(f) is not None:
  211. file_exclude = True
  212. break
  213. if file_exclude:
  214. continue
  215. file_include = False if includes else True
  216. for p in includes:
  217. if p.match(f) is not None:
  218. file_include = True
  219. break
  220. if file_include:
  221. print(f'extract file {f}')
  222. zip_file.extract(f, res_dir)
  223. zip_file.close()
  224. os.remove(download_filename)
  225. print(f'{feat} extract end')
  226. def external_resources(flutter, args, res_dir):
  227. features = parse_rc_features(args.feature)
  228. if not features:
  229. return
  230. print(f'Build with features {list(features.keys())}')
  231. if os.path.isdir(res_dir) and not os.path.islink(res_dir):
  232. shutil.rmtree(res_dir)
  233. elif os.path.exists(res_dir):
  234. raise Exception(f'Find file {res_dir}, not a directory')
  235. os.makedirs(res_dir, exist_ok=True)
  236. download_extract_features(features, res_dir)
  237. if flutter:
  238. os.makedirs(flutter_build_dir_2, exist_ok=True)
  239. for f in pathlib.Path(res_dir).iterdir():
  240. print(f'{f}')
  241. if f.is_file():
  242. shutil.copy2(f, flutter_build_dir_2)
  243. else:
  244. shutil.copytree(f, f'{flutter_build_dir_2}{f.stem}')
  245. def get_features(args):
  246. features = ['inline'] if not args.flutter else []
  247. if args.hwcodec:
  248. features.append('hwcodec')
  249. if args.vram:
  250. features.append('vram')
  251. if args.flutter:
  252. features.append('flutter')
  253. if args.unix_file_copy_paste:
  254. features.append('unix-file-copy-paste')
  255. if osx:
  256. if args.screencapturekit:
  257. features.append('screencapturekit')
  258. print("features:", features)
  259. return features
  260. def generate_control_file(version):
  261. control_file_path = "../res/DEBIAN/control"
  262. system2('/bin/rm -rf %s' % control_file_path)
  263. content = """Package: rustdesk
  264. Section: net
  265. Priority: optional
  266. Version: %s
  267. Architecture: %s
  268. Maintainer: rustdesk <info@rustdesk.com>
  269. Homepage: https://rustdesk.com
  270. Depends: libgtk-3-0, libxcb-randr0, libxdo3, libxfixes3, libxcb-shape0, libxcb-xfixes0, libasound2, libsystemd0, curl, libva2, libva-drm2, libva-x11-2, libgstreamer-plugins-base1.0-0, libpam0g, gstreamer1.0-pipewire%s
  271. Recommends: libayatana-appindicator3-1
  272. Description: A remote control software.
  273. """ % (version, get_deb_arch(), get_deb_extra_depends())
  274. file = open(control_file_path, "w")
  275. file.write(content)
  276. file.close()
  277. def ffi_bindgen_function_refactor():
  278. # workaround ffigen
  279. system2(
  280. 'sed -i "s/ffi.NativeFunction<ffi.Bool Function(DartPort/ffi.NativeFunction<ffi.Uint8 Function(DartPort/g" flutter/lib/generated_bridge.dart')
  281. def build_flutter_deb(version, features):
  282. if not skip_cargo:
  283. system2(f'cargo build --features {features} --lib --release')
  284. ffi_bindgen_function_refactor()
  285. os.chdir('flutter')
  286. system2('flutter build linux --release')
  287. system2('mkdir -p tmpdeb/usr/bin/')
  288. system2('mkdir -p tmpdeb/usr/share/rustdesk')
  289. system2('mkdir -p tmpdeb/etc/rustdesk/')
  290. system2('mkdir -p tmpdeb/etc/pam.d/')
  291. system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
  292. system2('mkdir -p tmpdeb/usr/share/icons/hicolor/256x256/apps/')
  293. system2('mkdir -p tmpdeb/usr/share/icons/hicolor/scalable/apps/')
  294. system2('mkdir -p tmpdeb/usr/share/applications/')
  295. system2('mkdir -p tmpdeb/usr/share/polkit-1/actions')
  296. system2('rm tmpdeb/usr/bin/rustdesk || true')
  297. system2(
  298. f'cp -r {flutter_build_dir}/* tmpdeb/usr/share/rustdesk/')
  299. system2(
  300. 'cp ../res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
  301. system2(
  302. 'cp ../res/128x128@2x.png tmpdeb/usr/share/icons/hicolor/256x256/apps/rustdesk.png')
  303. system2(
  304. 'cp ../res/scalable.svg tmpdeb/usr/share/icons/hicolor/scalable/apps/rustdesk.svg')
  305. system2(
  306. 'cp ../res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop')
  307. system2(
  308. 'cp ../res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop')
  309. system2(
  310. 'cp ../res/startwm.sh tmpdeb/etc/rustdesk/')
  311. system2(
  312. 'cp ../res/xorg.conf tmpdeb/etc/rustdesk/')
  313. system2(
  314. 'cp ../res/pam.d/rustdesk.debian tmpdeb/etc/pam.d/rustdesk')
  315. system2(
  316. "echo \"#!/bin/sh\" >> tmpdeb/usr/share/rustdesk/files/polkit && chmod a+x tmpdeb/usr/share/rustdesk/files/polkit")
  317. system2('mkdir -p tmpdeb/DEBIAN')
  318. generate_control_file(version)
  319. system2('cp -a ../res/DEBIAN/* tmpdeb/DEBIAN/')
  320. md5_file_folder("tmpdeb/")
  321. system2('dpkg-deb -b tmpdeb rustdesk.deb;')
  322. system2('/bin/rm -rf tmpdeb/')
  323. system2('/bin/rm -rf ../res/DEBIAN/control')
  324. os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
  325. os.chdir("..")
  326. def build_deb_from_folder(version, binary_folder):
  327. os.chdir('flutter')
  328. system2('mkdir -p tmpdeb/usr/bin/')
  329. system2('mkdir -p tmpdeb/usr/share/rustdesk')
  330. system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
  331. system2('mkdir -p tmpdeb/usr/share/icons/hicolor/256x256/apps/')
  332. system2('mkdir -p tmpdeb/usr/share/icons/hicolor/scalable/apps/')
  333. system2('mkdir -p tmpdeb/usr/share/applications/')
  334. system2('mkdir -p tmpdeb/usr/share/polkit-1/actions')
  335. system2('rm tmpdeb/usr/bin/rustdesk || true')
  336. system2(
  337. f'cp -r ../{binary_folder}/* tmpdeb/usr/share/rustdesk/')
  338. system2(
  339. 'cp ../res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
  340. system2(
  341. 'cp ../res/128x128@2x.png tmpdeb/usr/share/icons/hicolor/256x256/apps/rustdesk.png')
  342. system2(
  343. 'cp ../res/scalable.svg tmpdeb/usr/share/icons/hicolor/scalable/apps/rustdesk.svg')
  344. system2(
  345. 'cp ../res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop')
  346. system2(
  347. 'cp ../res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop')
  348. system2(
  349. "echo \"#!/bin/sh\" >> tmpdeb/usr/share/rustdesk/files/polkit && chmod a+x tmpdeb/usr/share/rustdesk/files/polkit")
  350. system2('mkdir -p tmpdeb/DEBIAN')
  351. generate_control_file(version)
  352. system2('cp -a ../res/DEBIAN/* tmpdeb/DEBIAN/')
  353. md5_file_folder("tmpdeb/")
  354. system2('dpkg-deb -b tmpdeb rustdesk.deb;')
  355. system2('/bin/rm -rf tmpdeb/')
  356. system2('/bin/rm -rf ../res/DEBIAN/control')
  357. os.rename('rustdesk.deb', '../rustdesk-%s.deb' % version)
  358. os.chdir("..")
  359. def build_flutter_dmg(version, features):
  360. if not skip_cargo:
  361. # set minimum osx build target, now is 10.14, which is the same as the flutter xcode project
  362. system2(
  363. f'MACOSX_DEPLOYMENT_TARGET=10.14 cargo build --features {features} --release')
  364. # copy dylib
  365. system2(
  366. "cp target/release/liblibrustdesk.dylib target/release/librustdesk.dylib")
  367. os.chdir('flutter')
  368. system2('flutter build macos --release')
  369. system2('cp -rf ../target/release/service ./build/macos/Build/Products/Release/RustDesk.app/Contents/MacOS/')
  370. '''
  371. system2(
  372. "create-dmg --volname \"RustDesk Installer\" --window-pos 200 120 --window-size 800 400 --icon-size 100 --app-drop-link 600 185 --icon RustDesk.app 200 190 --hide-extension RustDesk.app rustdesk.dmg ./build/macos/Build/Products/Release/RustDesk.app")
  373. os.rename("rustdesk.dmg", f"../rustdesk-{version}.dmg")
  374. '''
  375. os.chdir("..")
  376. def build_flutter_arch_manjaro(version, features):
  377. if not skip_cargo:
  378. system2(f'cargo build --features {features} --lib --release')
  379. ffi_bindgen_function_refactor()
  380. os.chdir('flutter')
  381. system2('flutter build linux --release')
  382. system2(f'strip {flutter_build_dir}/lib/librustdesk.so')
  383. os.chdir('../res')
  384. system2('HBB=`pwd`/.. FLUTTER=1 makepkg -f')
  385. def build_flutter_windows(version, features, skip_portable_pack):
  386. if not skip_cargo:
  387. system2(f'cargo build --features {features} --lib --release')
  388. if not os.path.exists("target/release/librustdesk.dll"):
  389. print("cargo build failed, please check rust source code.")
  390. exit(-1)
  391. os.chdir('flutter')
  392. system2('flutter build windows --release')
  393. os.chdir('..')
  394. shutil.copy2('target/release/deps/dylib_virtual_display.dll',
  395. flutter_build_dir_2)
  396. if skip_portable_pack:
  397. return
  398. os.chdir('libs/portable')
  399. system2('pip3 install -r requirements.txt')
  400. system2(
  401. f'python3 ./generate.py -f ../../{flutter_build_dir_2} -o . -e ../../{flutter_build_dir_2}/rustdesk.exe')
  402. os.chdir('../..')
  403. if os.path.exists('./rustdesk_portable.exe'):
  404. os.replace('./target/release/rustdesk-portable-packer.exe',
  405. './rustdesk_portable.exe')
  406. else:
  407. os.rename('./target/release/rustdesk-portable-packer.exe',
  408. './rustdesk_portable.exe')
  409. print(
  410. f'output location: {os.path.abspath(os.curdir)}/rustdesk_portable.exe')
  411. os.rename('./rustdesk_portable.exe', f'./rustdesk-{version}-install.exe')
  412. print(
  413. f'output location: {os.path.abspath(os.curdir)}/rustdesk-{version}-install.exe')
  414. def main():
  415. global skip_cargo
  416. parser = make_parser()
  417. args = parser.parse_args()
  418. if os.path.exists(exe_path):
  419. os.unlink(exe_path)
  420. if os.path.isfile('/usr/bin/pacman'):
  421. system2('git checkout src/ui/common.tis')
  422. version = get_version()
  423. features = ','.join(get_features(args))
  424. flutter = args.flutter
  425. if not flutter:
  426. system2('python3 res/inline-sciter.py')
  427. print(args.skip_cargo)
  428. if args.skip_cargo:
  429. skip_cargo = True
  430. portable = args.portable
  431. package = args.package
  432. if package:
  433. build_deb_from_folder(version, package)
  434. return
  435. res_dir = 'resources'
  436. external_resources(flutter, args, res_dir)
  437. if windows:
  438. # build virtual display dynamic library
  439. os.chdir('libs/virtual_display/dylib')
  440. system2('cargo build --release')
  441. os.chdir('../../..')
  442. if flutter:
  443. build_flutter_windows(version, features, args.skip_portable_pack)
  444. return
  445. system2('cargo build --release --features ' + features)
  446. # system2('upx.exe target/release/rustdesk.exe')
  447. system2('mv target/release/rustdesk.exe target/release/RustDesk.exe')
  448. pa = os.environ.get('P')
  449. if pa:
  450. # https://certera.com/kb/tutorial-guide-for-safenet-authentication-client-for-code-signing/
  451. system2(
  452. f'signtool sign /a /v /p {pa} /debug /f .\\cert.pfx /t http://timestamp.digicert.com '
  453. 'target\\release\\rustdesk.exe')
  454. else:
  455. print('Not signed')
  456. system2(
  457. f'cp -rf target/release/RustDesk.exe {res_dir}')
  458. os.chdir('libs/portable')
  459. system2('pip3 install -r requirements.txt')
  460. system2(
  461. f'python3 ./generate.py -f ../../{res_dir} -o . -e ../../{res_dir}/rustdesk-{version}-win7-install.exe')
  462. system2('mv ../../{res_dir}/rustdesk-{version}-win7-install.exe ../..')
  463. elif os.path.isfile('/usr/bin/pacman'):
  464. # pacman -S -needed base-devel
  465. system2("sed -i 's/pkgver=.*/pkgver=%s/g' res/PKGBUILD" % version)
  466. if flutter:
  467. build_flutter_arch_manjaro(version, features)
  468. else:
  469. system2('cargo build --release --features ' + features)
  470. system2('git checkout src/ui/common.tis')
  471. system2('strip target/release/rustdesk')
  472. system2('ln -s res/pacman_install && ln -s res/PKGBUILD')
  473. system2('HBB=`pwd` makepkg -f')
  474. system2('mv rustdesk-%s-0-x86_64.pkg.tar.zst rustdesk-%s-manjaro-arch.pkg.tar.zst' % (
  475. version, version))
  476. # pacman -U ./rustdesk.pkg.tar.zst
  477. elif os.path.isfile('/usr/bin/yum'):
  478. system2('cargo build --release --features ' + features)
  479. system2('strip target/release/rustdesk')
  480. system2(
  481. "sed -i 's/Version: .*/Version: %s/g' res/rpm.spec" % version)
  482. system2('HBB=`pwd` rpmbuild -ba res/rpm.spec')
  483. system2(
  484. 'mv $HOME/rpmbuild/RPMS/x86_64/rustdesk-%s-0.x86_64.rpm ./rustdesk-%s-fedora28-centos8.rpm' % (
  485. version, version))
  486. # yum localinstall rustdesk.rpm
  487. elif os.path.isfile('/usr/bin/zypper'):
  488. system2('cargo build --release --features ' + features)
  489. system2('strip target/release/rustdesk')
  490. system2(
  491. "sed -i 's/Version: .*/Version: %s/g' res/rpm-suse.spec" % version)
  492. system2('HBB=`pwd` rpmbuild -ba res/rpm-suse.spec')
  493. system2(
  494. 'mv $HOME/rpmbuild/RPMS/x86_64/rustdesk-%s-0.x86_64.rpm ./rustdesk-%s-suse.rpm' % (
  495. version, version))
  496. # yum localinstall rustdesk.rpm
  497. else:
  498. if flutter:
  499. if osx:
  500. build_flutter_dmg(version, features)
  501. pass
  502. else:
  503. # system2(
  504. # 'mv target/release/bundle/deb/rustdesk*.deb ./flutter/rustdesk.deb')
  505. build_flutter_deb(version, features)
  506. else:
  507. system2('cargo bundle --release --features ' + features)
  508. if osx:
  509. system2(
  510. 'strip target/release/bundle/osx/RustDesk.app/Contents/MacOS/rustdesk')
  511. system2(
  512. 'cp libsciter.dylib target/release/bundle/osx/RustDesk.app/Contents/MacOS/')
  513. # https://github.com/sindresorhus/create-dmg
  514. system2('/bin/rm -rf *.dmg')
  515. pa = os.environ.get('P')
  516. if pa:
  517. system2('''
  518. # buggy: rcodesign sign ... path/*, have to sign one by one
  519. # install rcodesign via cargo install apple-codesign
  520. #rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/rustdesk
  521. #rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/libsciter.dylib
  522. #rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./target/release/bundle/osx/RustDesk.app
  523. # goto "Keychain Access" -> "My Certificates" for below id which starts with "Developer ID Application:"
  524. codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app/Contents/MacOS/*
  525. codesign -s "Developer ID Application: {0}" --force --options runtime ./target/release/bundle/osx/RustDesk.app
  526. '''.format(pa))
  527. system2(
  528. 'create-dmg "RustDesk %s.dmg" "target/release/bundle/osx/RustDesk.app"' % version)
  529. os.rename('RustDesk %s.dmg' %
  530. version, 'rustdesk-%s.dmg' % version)
  531. if pa:
  532. system2('''
  533. # https://pyoxidizer.readthedocs.io/en/apple-codesign-0.14.0/apple_codesign.html
  534. # https://pyoxidizer.readthedocs.io/en/stable/tugger_code_signing.html
  535. # https://developer.apple.com/developer-id/
  536. # goto xcode and login with apple id, manager certificates (Developer ID Application and/or Developer ID Installer) online there (only download and double click (install) cer file can not export p12 because no private key)
  537. #rcodesign sign --p12-file ~/.p12/rustdesk-developer-id.p12 --p12-password-file ~/.p12/.cert-pass --code-signature-flags runtime ./rustdesk-{1}.dmg
  538. codesign -s "Developer ID Application: {0}" --force --options runtime ./rustdesk-{1}.dmg
  539. # https://appstoreconnect.apple.com/access/api
  540. # https://gregoryszorc.com/docs/apple-codesign/stable/apple_codesign_getting_started.html#apple-codesign-app-store-connect-api-key
  541. # p8 file is generated when you generate api key (can download only once)
  542. rcodesign notary-submit --api-key-path ../.p12/api-key.json --staple rustdesk-{1}.dmg
  543. # verify: spctl -a -t exec -v /Applications/RustDesk.app
  544. '''.format(pa, version))
  545. else:
  546. print('Not signed')
  547. else:
  548. # build deb package
  549. system2(
  550. 'mv target/release/bundle/deb/rustdesk*.deb ./rustdesk.deb')
  551. system2('dpkg-deb -R rustdesk.deb tmpdeb')
  552. system2('mkdir -p tmpdeb/usr/share/rustdesk/files/systemd/')
  553. system2('mkdir -p tmpdeb/usr/share/icons/hicolor/256x256/apps/')
  554. system2('mkdir -p tmpdeb/usr/share/icons/hicolor/scalable/apps/')
  555. system2(
  556. 'cp res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
  557. system2(
  558. 'cp res/128x128@2x.png tmpdeb/usr/share/icons/hicolor/256x256/apps/rustdesk.png')
  559. system2(
  560. 'cp res/scalable.svg tmpdeb/usr/share/icons/hicolor/scalable/apps/rustdesk.svg')
  561. system2(
  562. 'cp res/rustdesk.desktop tmpdeb/usr/share/applications/rustdesk.desktop')
  563. system2(
  564. 'cp res/rustdesk-link.desktop tmpdeb/usr/share/applications/rustdesk-link.desktop')
  565. os.system('mkdir -p tmpdeb/etc/rustdesk/')
  566. os.system('cp -a res/startwm.sh tmpdeb/etc/rustdesk/')
  567. os.system('mkdir -p tmpdeb/etc/X11/rustdesk/')
  568. os.system('cp res/xorg.conf tmpdeb/etc/X11/rustdesk/')
  569. os.system('cp -a DEBIAN/* tmpdeb/DEBIAN/')
  570. os.system('mkdir -p tmpdeb/etc/pam.d/')
  571. os.system('cp pam.d/rustdesk.debian tmpdeb/etc/pam.d/rustdesk')
  572. system2('strip tmpdeb/usr/bin/rustdesk')
  573. system2('mkdir -p tmpdeb/usr/share/rustdesk')
  574. system2('mv tmpdeb/usr/bin/rustdesk tmpdeb/usr/share/rustdesk/')
  575. system2('cp libsciter-gtk.so tmpdeb/usr/share/rustdesk/')
  576. md5_file_folder("tmpdeb/")
  577. system2('dpkg-deb -b tmpdeb rustdesk.deb; /bin/rm -rf tmpdeb/')
  578. os.rename('rustdesk.deb', 'rustdesk-%s.deb' % version)
  579. def md5_file(fn):
  580. md5 = hashlib.md5(open('tmpdeb/' + fn, 'rb').read()).hexdigest()
  581. system2('echo "%s /%s" >> tmpdeb/DEBIAN/md5sums' % (md5, fn))
  582. def md5_file_folder(base_dir):
  583. base_path = Path(base_dir)
  584. for file in base_path.rglob('*'):
  585. if file.is_file() and 'DEBIAN' not in file.parts:
  586. relative_path = file.relative_to(base_path)
  587. md5_file(str(relative_path))
  588. if __name__ == "__main__":
  589. main()