Merge pull request #1591 from Kingtous/master

feat: windows flutter portable build script
This commit is contained in:
RustDesk 2022-09-21 13:58:20 +08:00 committed by GitHub
commit 752a94a5b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 611 additions and 2 deletions

View File

@ -119,7 +119,7 @@ jni = "0.19"
flutter_rust_bridge = { git = "https://github.com/SoLongAndThanksForAllThePizza/flutter_rust_bridge" }
[workspace]
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/simple_rc"]
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/simple_rc", "libs/portable"]
[package.metadata.winres]
LegalCopyright = "Copyright © 2022 Purslane, Inc."

View File

@ -73,6 +73,11 @@ def make_parser():
action='store_true',
help='Enable feature hwcodec'
)
parser.add_argument(
'--portable',
action='store_true',
help='Build windows portable'
)
return parser
@ -187,6 +192,20 @@ def build_flutter_arch_manjaro():
os.chdir('..')
os.system('HBB=`pwd` FLUTTER=1 makepkg -f')
def build_flutter_windows_portable():
os.system("cargo build --lib --features flutter --release")
os.chdir('flutter')
os.system("flutter build windows --release")
os.chdir('..')
os.chdir("libs/portable")
os.system("pip3 install -r requirements.txt")
os.system("python3 .\\generate.py -f ..\\..\\flutter\\build\\windows\\runner\Release\ -o . -e ..\\..\\flutter\\build\\windows\\runner\\Release\\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")
def main():
parser = make_parser()
@ -201,13 +220,19 @@ def main():
'//#![windows_subsystem', '#![windows_subsystem'))
if os.path.exists(exe_path):
os.unlink(exe_path)
os.system('python3 res/inline-sciter.py')
if os.path.isfile('/usr/bin/pacman'):
os.system('git checkout src/ui/common.tis')
version = get_version()
features = ",".join(get_features(args))
flutter = args.flutter
if not flutter:
# not flutter, is sciter
os.system('python3 res/inline-sciter.py')
portable = args.portable
if windows:
if portable:
build_flutter_windows_portable()
return
os.system('cargo build --release --features ' + features)
# os.system('upx.exe target/release/rustdesk.exe')
os.system('mv target/release/rustdesk.exe target/release/RustDesk.exe')

3
libs/portable/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
*.exe
*.bin

285
libs/portable/Cargo.lock generated Normal file
View File

@ -0,0 +1,285 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
[[package]]
name = "alloc-stdlib"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "brotli"
version = "3.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
]
[[package]]
name = "brotli-decompressor"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
]
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "dirs"
version = "4.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
dependencies = [
"libc",
"redox_users",
"winapi",
]
[[package]]
name = "embed-resource"
version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936c1354206a875581696369aef920e12396e93bbd251c43a7a3f3fa85023a7d"
dependencies = [
"cc",
"rustc_version",
"toml",
"vswhom",
"winreg",
]
[[package]]
name = "getrandom"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
[[package]]
name = "md5"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771"
[[package]]
name = "proc-macro2"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom",
"redox_syscall",
"thiserror",
]
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "rustdesk-portable-packer"
version = "0.1.0"
dependencies = [
"brotli",
"dirs",
"embed-resource",
"md5",
]
[[package]]
name = "semver"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
[[package]]
name = "serde"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
[[package]]
name = "syn"
version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c53f98874615aea268107765aa1ed8f6116782501d18e53d08b471733bea6c85"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8b463991b4eab2d801e724172285ec4195c650e8ec79b149e6c2a8e6dd3f783"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa"
dependencies = [
"serde",
]
[[package]]
name = "unicode-ident"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
[[package]]
name = "vswhom"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b"
dependencies = [
"libc",
"vswhom-sys",
]
[[package]]
name = "vswhom-sys"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22025f6d8eb903ebf920ea6933b70b1e495be37e2cb4099e62c80454aaf57c39"
dependencies = [
"cc",
"libc",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "winreg"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
dependencies = [
"winapi",
]

16
libs/portable/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "rustdesk-portable-packer"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
build = "build.rs"
[build-dependencies]
embed-resource = "1.7"
[dependencies]
brotli = "3.3.4"
dirs = "4.0.0"
md5 = "0.7.0"

5
libs/portable/build.rs Normal file
View File

@ -0,0 +1,5 @@
extern crate embed_resource;
fn main() {
embed_resource::compile("icon.rc");
}

88
libs/portable/generate.py Normal file
View File

@ -0,0 +1,88 @@
from ast import parse
import os
import optparse
from hashlib import md5
import brotli
# file compress level(0-11)
compress_level = 11
# 4GB maximum
length_count = 4
# encoding
encoding = 'utf-8'
# output: {path: (compressed_data, file_md5)}
def generate_md5_table(folder: str) -> dict:
res: dict = dict()
curdir = os.curdir
os.chdir(folder)
for root, _, files in os.walk('.'):
# remove ./
for f in files:
md5_generator = md5()
full_path = os.path.join(root, f)
print(f"processing {full_path}...")
f = open(full_path, "rb")
content = f.read()
content_compressed = brotli.compress(
content, quality=compress_level)
md5_generator.update(content)
md5_code = md5_generator.hexdigest().encode(encoding=encoding)
res[full_path] = (content_compressed, md5_code)
os.chdir(curdir)
return res
def write_metadata(md5_table: dict, output_folder: str, exe: str):
output_path = os.path.join(output_folder, "data.bin")
with open(output_path, "wb") as f:
f.write("rustdesk".encode(encoding=encoding))
for path in md5_table.keys():
(compressed_data, md5_code) = md5_table[path]
data_length = len(compressed_data)
path = path.encode(encoding=encoding)
# path length & path
f.write((len(path)).to_bytes(length=length_count, byteorder='big'))
f.write(path)
# data length & compressed data
f.write((data_length).to_bytes(
length=length_count, byteorder='big'))
f.write(compressed_data)
# md5 code
f.write(md5_code)
# end
f.write("rustdesk".encode(encoding=encoding))
# executable
f.write(exe.encode(encoding='utf-8'))
print(f"metadata had written to {output_path}")
def build_portable(output_folder: str):
os.chdir(output_folder)
os.system("cargo build --release")
# Linux: python3 generate.py -f ../rustdesk-portable-packer/test -o . -e ./test/main.py
# Windows: python3 .\generate.py -f ..\rustdesk\flutter\build\windows\runner\Debug\ -o . -e ..\rustdesk\flutter\build\windows\runner\Debug\rustdesk.exe
if __name__ == '__main__':
parser = optparse.OptionParser()
parser.add_option("-f", "--folder", dest="folder",
help="folder to compress")
parser.add_option("-o", "--output", dest="output_folder",
help="the root of portable packer project")
parser.add_option("-e", "--executable", dest="executable",
help="specify startup file")
(options, args) = parser.parse_args()
folder = options.folder
output_folder = os.path.abspath(options.output_folder)
exe: str = os.path.abspath(options.executable)
if not exe.startswith(os.path.abspath(folder)):
print("the executable must locate in source folder")
exit(-1)
exe = '.' + exe[len(os.path.abspath(folder)):]
print("executable path: " + exe)
md5_table = generate_md5_table(folder)
write_metadata(md5_table, output_folder, exe)
build_portable(output_folder)

1
libs/portable/icon.rc Normal file
View File

@ -0,0 +1 @@
rustdesk_icon ICON "../../res/icon.ico"

View File

@ -0,0 +1 @@
brotli

View File

@ -0,0 +1,134 @@
use std::{
fs::{self},
io::{Cursor, Read},
path::PathBuf,
};
const BIN_DATA: &[u8] = include_bytes!("../data.bin");
// 4bytes
const LENGTH: usize = 4;
const IDENTIFIER_LENGTH: usize = 8;
const MD5_LENGTH: usize = 32;
const BUF_SIZE: usize = 4096;
pub(crate) struct BinaryData {
pub md5_code: &'static [u8],
// compressed gzip data
pub raw: &'static [u8],
pub path: String,
}
pub(crate) struct BinaryReader {
pub files: Vec<BinaryData>,
pub exe: String,
}
impl Default for BinaryReader {
fn default() -> Self {
let (files, exe) = BinaryReader::read();
Self { files, exe }
}
}
impl BinaryData {
fn decompress(&self) -> Vec<u8> {
let cursor = Cursor::new(self.raw);
let mut decoder = brotli::Decompressor::new(cursor, BUF_SIZE);
let mut buf = Vec::new();
decoder.read_to_end(&mut buf).unwrap();
buf
}
pub fn write_to_file(&self, prefix: &PathBuf) {
let p = prefix.join(&self.path);
if let Some(parent) = p.parent() {
if !parent.exists() {
let _ = fs::create_dir_all(parent);
}
}
if p.exists() {
// check md5
let f = fs::read(p.clone()).unwrap();
let digest = format!("{:x}", md5::compute(&f));
let md5_record = String::from_utf8_lossy(self.md5_code);
if digest == md5_record {
// same, skip this file
println!("skip {}", &self.path);
return;
} else {
println!("writing {}", p.display());
println!("{} -> {}", md5_record, digest)
}
}
let _ = fs::write(p, self.decompress());
}
}
impl BinaryReader {
fn read() -> (Vec<BinaryData>, String) {
let mut base: usize = 0;
let mut parsed = vec![];
assert!(BIN_DATA.len() > IDENTIFIER_LENGTH, "bin data invalid!");
let mut iden = String::from_utf8_lossy(&BIN_DATA[base..base + IDENTIFIER_LENGTH]);
if iden != "rustdesk" {
panic!("bin file is not vaild!");
}
base += IDENTIFIER_LENGTH;
loop {
iden = String::from_utf8_lossy(&BIN_DATA[base..base + IDENTIFIER_LENGTH]);
if iden == "rustdesk" {
base += IDENTIFIER_LENGTH;
break;
}
// start reading
let mut offset = 0;
let path_length = u32::from_be_bytes([
BIN_DATA[base + offset],
BIN_DATA[base + offset + 1],
BIN_DATA[base + offset + 2],
BIN_DATA[base + offset + 3],
]) as usize;
offset += LENGTH;
let path =
String::from_utf8_lossy(&BIN_DATA[base + offset..base + offset + path_length])
.to_string();
offset += path_length;
// file sz
let file_length = u32::from_be_bytes([
BIN_DATA[base + offset],
BIN_DATA[base + offset + 1],
BIN_DATA[base + offset + 2],
BIN_DATA[base + offset + 3],
]) as usize;
offset += LENGTH;
let raw = &BIN_DATA[base + offset..base + offset + file_length];
offset += file_length;
// md5
let md5 = &BIN_DATA[base + offset..base + offset + MD5_LENGTH];
offset += MD5_LENGTH;
parsed.push(BinaryData {
md5_code: md5,
raw: raw,
path: path,
});
base += offset;
}
// executable
let executable = String::from_utf8_lossy(&BIN_DATA[base..]).to_string();
(parsed, executable)
}
#[cfg(unix)]
pub fn configure_permission(&self, prefix: &PathBuf) {
use std::os::unix::prelude::PermissionsExt;
let exe_path = prefix.join(&self.exe);
if exe_path.exists() {
let f = File::open(exe_path).unwrap();
let meta = f.metadata().unwrap();
let mut permissions = meta.permissions();
permissions.set_mode(0o755);
f.set_permissions(permissions).unwrap();
}
}
}

51
libs/portable/src/main.rs Normal file
View File

@ -0,0 +1,51 @@
#![windows_subsystem = "windows"]
use std::{
path::PathBuf,
process::{Command, Stdio},
};
use bin_reader::BinaryReader;
pub mod bin_reader;
const APP_PREFIX: &str = "rustdesk";
const APPNAME_RUNTIME_ENV_KEY: &str = "RUSTDESK_APPNAME";
fn setup(reader: BinaryReader) -> Option<PathBuf> {
// home dir
if let Some(dir) = dirs::data_local_dir() {
let dir = dir.join(APP_PREFIX);
for file in reader.files.iter() {
file.write_to_file(&dir);
}
#[cfg(unix)]
reader.configure_permission(&dir);
Some(dir.join(&reader.exe))
} else {
eprintln!("not found data local dir");
None
}
}
fn execute(path: PathBuf) {
println!("executing {}", path.display());
// setup env
let exe = std::env::current_exe().unwrap();
let exe_name = exe.file_name().unwrap();
// run executable
Command::new(path)
.env(APPNAME_RUNTIME_ENV_KEY, exe_name)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.output()
.expect(&format!("failed to execute {:?}", exe_name));
}
fn main() {
let reader = BinaryReader::default();
if let Some(exe) = setup(reader) {
execute(exe);
}
}