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