build.py 26 KB

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