build.py 27 KB

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