Merge pull request #4085 from fufesou/refact/remove_simple_rc
Refact/remove simple rc
This commit is contained in:
commit
87b4453870
12
Cargo.lock
generated
12
Cargo.lock
generated
@ -5163,7 +5163,6 @@ dependencies = [
|
||||
"sha2",
|
||||
"shared_memory",
|
||||
"shutdown_hooks",
|
||||
"simple_rc",
|
||||
"sys-locale",
|
||||
"system_shutdown",
|
||||
"tao",
|
||||
@ -5571,17 +5570,6 @@ version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f"
|
||||
|
||||
[[package]]
|
||||
name = "simple_rc"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"confy",
|
||||
"hbb_common",
|
||||
"serde 1.0.159",
|
||||
"serde_derive",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.2.3"
|
||||
|
@ -19,7 +19,6 @@ path = "src/naming.rs"
|
||||
inline = []
|
||||
hbbs = []
|
||||
cli = []
|
||||
with_rc = ["simple_rc"]
|
||||
flutter_texture_render = []
|
||||
appimage = []
|
||||
flatpak = []
|
||||
@ -135,7 +134,7 @@ jni = "0.19"
|
||||
flutter_rust_bridge = "1.61.1"
|
||||
|
||||
[workspace]
|
||||
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/simple_rc", "libs/portable"]
|
||||
members = ["libs/scrap", "libs/hbb_common", "libs/enigo", "libs/clipboard", "libs/virtual_display", "libs/virtual_display/dylib", "libs/portable"]
|
||||
exclude = ["vdi/host", "examples/custom_plugin"]
|
||||
|
||||
[package.metadata.winres]
|
||||
@ -150,7 +149,6 @@ winapi = { version = "0.3", features = [ "winnt" ] }
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
hbb_common = { path = "libs/hbb_common" }
|
||||
simple_rc = { path = "libs/simple_rc", optional = true }
|
||||
flutter_rust_bridge_codegen = "1.61.1"
|
||||
os-version = "0.2"
|
||||
|
||||
|
71
build.py
71
build.py
@ -15,7 +15,13 @@ 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/'
|
||||
if windows:
|
||||
flutter_build_dir = 'build/windows/runner/Release/'
|
||||
elif osx:
|
||||
flutter_build_dir = 'build/macos/Build/Products/Release/'
|
||||
else:
|
||||
flutter_build_dir = 'build/linux/x64/release/bundle/'
|
||||
flutter_build_dir_2 = f'flutter/{flutter_build_dir}'
|
||||
skip_cargo = False
|
||||
|
||||
def get_arch() -> str:
|
||||
@ -41,11 +47,13 @@ def get_version():
|
||||
def parse_rc_features(feature):
|
||||
available_features = {
|
||||
'IddDriver': {
|
||||
'platform': ['windows'],
|
||||
'zip_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.3/RustDeskIddDriver_x64.zip',
|
||||
'checksum_url': 'https://github.com/fufesou/RustDeskIddDriver/releases/download/v0.3/checksum_md5',
|
||||
'exclude': ['README.md', 'certmgr.exe', 'install_cert_runas_admin.bat'],
|
||||
},
|
||||
'PrivacyMode': {
|
||||
'platform': ['windows'],
|
||||
'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',
|
||||
@ -55,16 +63,34 @@ def parse_rc_features(feature):
|
||||
apply_features = {}
|
||||
if not feature:
|
||||
feature = []
|
||||
|
||||
def platform_check(platforms):
|
||||
if windows:
|
||||
return 'windows' in platforms
|
||||
elif osx:
|
||||
return 'osx' in platforms
|
||||
else:
|
||||
return 'linux' in platforms
|
||||
|
||||
def get_all_features():
|
||||
features = []
|
||||
for (feat, feat_info) in available_features.items():
|
||||
if platform_check(feat_info['platform']):
|
||||
features.append(feat)
|
||||
return features
|
||||
|
||||
if isinstance(feature, str) and feature.upper() == 'ALL':
|
||||
return available_features
|
||||
return get_all_features()
|
||||
elif isinstance(feature, list):
|
||||
# force add PrivacyMode
|
||||
feature.append('PrivacyMode')
|
||||
if windows:
|
||||
# force add PrivacyMode
|
||||
feature.append('PrivacyMode')
|
||||
for feat in feature:
|
||||
if isinstance(feat, str) and feat.upper() == 'ALL':
|
||||
return available_features
|
||||
return get_all_features()
|
||||
if feat in available_features:
|
||||
apply_features[feat] = available_features[feat]
|
||||
if platform_check(available_features[feat]['platform']):
|
||||
apply_features[feat] = available_features[feat]
|
||||
else:
|
||||
print(f'Unrecognized feature {feat}')
|
||||
return apply_features
|
||||
@ -211,14 +237,12 @@ def download_extract_features(features, res_dir):
|
||||
print(f'{feat} extract end')
|
||||
|
||||
|
||||
def get_rc_features(args):
|
||||
flutter = args.flutter
|
||||
def external_resources(flutter, args, res_dir):
|
||||
features = parse_rc_features(args.feature)
|
||||
if not features:
|
||||
return []
|
||||
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):
|
||||
@ -226,22 +250,18 @@ def get_rc_features(args):
|
||||
os.makedirs(res_dir, exist_ok=True)
|
||||
download_extract_features(features, res_dir)
|
||||
if flutter:
|
||||
os.makedirs(flutter_win_target_dir, exist_ok=True)
|
||||
os.makedirs(flutter_build_dir_2, 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)
|
||||
shutil.copy2(f, flutter_build_dir_2)
|
||||
else:
|
||||
shutil.copytree(f, f'{flutter_win_target_dir}{f.stem}')
|
||||
return []
|
||||
else:
|
||||
return ['with_rc']
|
||||
shutil.copytree(f, f'{flutter_build_dir_2}{f.stem}')
|
||||
|
||||
|
||||
def get_features(args):
|
||||
features = ['inline'] if not args.flutter else []
|
||||
if windows:
|
||||
features.extend(get_rc_features(args))
|
||||
features.append('virtual_display_driver')
|
||||
if args.hwcodec:
|
||||
features.append('hwcodec')
|
||||
@ -295,7 +315,7 @@ def build_flutter_deb(version, features):
|
||||
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/')
|
||||
f'cp -r {flutter_build_dir}/* tmpdeb/usr/lib/rustdesk/')
|
||||
system2(
|
||||
'cp ../res/rustdesk.service tmpdeb/usr/share/rustdesk/files/systemd/')
|
||||
system2(
|
||||
@ -381,7 +401,7 @@ def build_flutter_arch_manjaro(version, features):
|
||||
ffi_bindgen_function_refactor()
|
||||
os.chdir('flutter')
|
||||
system2('flutter build linux --release')
|
||||
system2('strip build/linux/x64/release/bundle/lib/librustdesk.so')
|
||||
system2(f'strip {flutter_build_dir}/lib/librustdesk.so')
|
||||
os.chdir('../res')
|
||||
system2('HBB=`pwd`/.. FLUTTER=1 makepkg -f')
|
||||
|
||||
@ -396,11 +416,11 @@ def build_flutter_windows(version, features):
|
||||
system2('flutter build windows --release')
|
||||
os.chdir('..')
|
||||
shutil.copy2('target/release/deps/dylib_virtual_display.dll',
|
||||
flutter_win_target_dir)
|
||||
flutter_build_dir_2)
|
||||
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')
|
||||
f'python3 ./generate.py -f ../../{flutter_build_dir_2} -o . -e ../../{flutter_build_dir_2}/rustdesk.exe')
|
||||
os.chdir('../..')
|
||||
if os.path.exists('./rustdesk_portable.exe'):
|
||||
os.replace('./target/release/rustdesk-portable-packer.exe',
|
||||
@ -437,6 +457,8 @@ def main():
|
||||
if package:
|
||||
build_deb_from_folder(version, package)
|
||||
return
|
||||
res_dir = 'resources'
|
||||
external_resources(flutter, args, res_dir)
|
||||
if windows:
|
||||
# build virtual display dynamic library
|
||||
os.chdir('libs/virtual_display/dylib')
|
||||
@ -457,7 +479,12 @@ def main():
|
||||
else:
|
||||
print('Not signed')
|
||||
system2(
|
||||
f'cp -rf target/release/RustDesk.exe rustdesk-{version}-win7-install.exe')
|
||||
f'cp -rf target/release/RustDesk.exe {res_dir}')
|
||||
os.chdir('libs/portable')
|
||||
system2('pip3 install -r requirements.txt')
|
||||
system2(
|
||||
f'python3 ./generate.py -f ../../{res_dir} -o . -e ../../{res_dir}/rustdesk-{version}-win7-install.exe')
|
||||
system2('mv ../../{res_dir}/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)
|
||||
|
16
build.rs
16
build.rs
@ -41,20 +41,6 @@ fn build_manifest() {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(windows, feature = "with_rc"))]
|
||||
fn build_rc_source() {
|
||||
use simple_rc::{generate_with_conf, Config, ConfigItem};
|
||||
generate_with_conf(&Config {
|
||||
outfile: "src/rc.rs".to_owned(),
|
||||
confs: vec![ConfigItem {
|
||||
inc: "resources".to_owned(),
|
||||
exc: vec![],
|
||||
suppressed_front: "resources".to_owned(),
|
||||
}],
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn install_oboe() {
|
||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
|
||||
if target_os != "android" {
|
||||
@ -133,8 +119,6 @@ fn main() {
|
||||
gen_flutter_rust_bridge();
|
||||
// return;
|
||||
// }
|
||||
#[cfg(all(windows, feature = "with_rc"))]
|
||||
build_rc_source();
|
||||
#[cfg(all(windows, feature = "inline"))]
|
||||
build_manifest();
|
||||
#[cfg(windows)]
|
||||
|
@ -1,13 +0,0 @@
|
||||
[package]
|
||||
name = "simple_rc"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde_derive = "1.0"
|
||||
serde = "1.0"
|
||||
walkdir = "2"
|
||||
confy = { git = "https://github.com/open-trade/confy" }
|
||||
hbb_common = { path = "../hbb_common" }
|
@ -1,23 +0,0 @@
|
||||
extern crate simple_rc;
|
||||
|
||||
use simple_rc::*;
|
||||
|
||||
fn main() {
|
||||
{
|
||||
const CONF_FILE: &str = "simple_rc.toml";
|
||||
generate(CONF_FILE).unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
generate_with_conf(&Config {
|
||||
outfile: "src/rc.rs".to_owned(),
|
||||
confs: vec![ConfigItem {
|
||||
inc: "D:/projects/windows/RustDeskTempTopMostWindow/x64/Release/xxx".to_owned(),
|
||||
// exc: vec!["*.dll".to_owned(), "*.exe".to_owned()],
|
||||
exc: vec![],
|
||||
suppressed_front: "D:/projects/windows".to_owned(),
|
||||
}],
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
# The output source file
|
||||
outfile = "src/rc.rs"
|
||||
|
||||
# The resource config list.
|
||||
[[confs]]
|
||||
# The file or director to integrate.
|
||||
inc = "D:/projects/windows/RustDeskTempTopMostWindow/x64/Release/xxx"
|
||||
# The exclusions.
|
||||
exc = ["*.dll", "*.exe"]
|
||||
# The front path that will ignore for extracting.
|
||||
# The following config will make base output path to be "RustDeskTempTopMostWindow/x64/Release/xxx".
|
||||
suppressed_front = "D:/projects/windows"
|
@ -1,208 +0,0 @@
|
||||
use hbb_common::{bail, ResultType};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, fs::File, io::prelude::*, path::Path};
|
||||
use walkdir::WalkDir;
|
||||
|
||||
//mod rc;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)]
|
||||
pub struct ConfigItem {
|
||||
// include directory or file
|
||||
pub inc: String,
|
||||
// exclude files
|
||||
pub exc: Vec<String>,
|
||||
// out_path = origin_path - suppressed_front
|
||||
pub suppressed_front: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Serialize, Deserialize, Clone)]
|
||||
pub struct Config {
|
||||
// output source file
|
||||
pub outfile: String,
|
||||
// config items
|
||||
pub confs: Vec<ConfigItem>,
|
||||
}
|
||||
|
||||
pub fn get_outin_files<'a>(item: &'a ConfigItem) -> ResultType<HashMap<String, String>> {
|
||||
let mut outin_filemap = HashMap::new();
|
||||
|
||||
for entry in WalkDir::new(&item.inc).follow_links(true) {
|
||||
let path = entry?.into_path();
|
||||
if path.is_file() {
|
||||
let mut exclude = false;
|
||||
for excfile in item.exc.iter() {
|
||||
if excfile.starts_with("*.") {
|
||||
if let Some(ext) = path.extension().and_then(|x| x.to_str()) {
|
||||
if excfile.ends_with(&format!(".{}", ext)) {
|
||||
exclude = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if path.ends_with(Path::new(excfile)) {
|
||||
exclude = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if exclude {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut suppressed_front = item.suppressed_front.clone();
|
||||
if !suppressed_front.is_empty() && suppressed_front.ends_with('/') {
|
||||
suppressed_front.push('/');
|
||||
}
|
||||
let outpath = path.strip_prefix(Path::new(&suppressed_front))?;
|
||||
let outfile = if outpath.is_absolute() {
|
||||
match outpath
|
||||
.file_name()
|
||||
.and_then(|f| f.to_str())
|
||||
.map(|f| f.to_string())
|
||||
{
|
||||
None => {
|
||||
bail!("Failed to get filename of {}", outpath.display());
|
||||
}
|
||||
Some(s) => s,
|
||||
}
|
||||
} else {
|
||||
match outpath.to_str() {
|
||||
None => {
|
||||
bail!("Failed to convert {} to string", outpath.display());
|
||||
}
|
||||
// Simple replace \ to / here.
|
||||
// A better way is to use lib [path-slash](https://github.com/rhysd/path-slash)
|
||||
Some(s) => s.to_string().replace("\\", "/"),
|
||||
}
|
||||
};
|
||||
let infile = match path.canonicalize()?.to_str() {
|
||||
None => {
|
||||
bail!("Failed to get file path of {}", path.display());
|
||||
}
|
||||
Some(s) => s.to_string(),
|
||||
};
|
||||
if let Some(_) = outin_filemap.insert(outfile.clone(), infile) {
|
||||
bail!("outfile {} is set before", outfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(outin_filemap)
|
||||
}
|
||||
|
||||
pub fn generate(conf_file: &str) -> ResultType<()> {
|
||||
let conf = confy::load_path(conf_file)?;
|
||||
generate_with_conf(&conf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_with_conf<'a>(conf: &'a Config) -> ResultType<()> {
|
||||
let mut outfile = File::create(&conf.outfile)?;
|
||||
|
||||
outfile.write(
|
||||
br##"use hbb_common::{bail, ResultType};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::prelude::*,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
"##,
|
||||
)?;
|
||||
|
||||
outfile.write(b"#[allow(dead_code)]\n")?;
|
||||
outfile.write(b"pub fn extract_resources(root_path: &str) -> ResultType<()> {\n")?;
|
||||
outfile.write(b" let mut resources: Vec<(&str, &[u8])> = Vec::new();\n")?;
|
||||
|
||||
let mut outin_files = HashMap::new();
|
||||
for item in conf.confs.iter() {
|
||||
for (o, i) in get_outin_files(item)?.into_iter() {
|
||||
if let Some(_) = outin_files.insert(o.clone(), i) {
|
||||
bail!("outfile {} is set before", o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut count = 1;
|
||||
for (o, i) in outin_files.iter() {
|
||||
let mut infile = File::open(&i)?;
|
||||
let mut buffer = Vec::<u8>::new();
|
||||
infile.read_to_end(&mut buffer)?;
|
||||
|
||||
let var_outfile = format!("outfile_{}", count);
|
||||
let var_outdata = format!("outdata_{}", count);
|
||||
|
||||
write!(outfile, " let {} = \"{}\";\n", var_outfile, o)?;
|
||||
write!(outfile, " let {}: &[u8] = &[\n ", var_outdata)?;
|
||||
|
||||
let mut line_num = 20;
|
||||
for v in buffer {
|
||||
if line_num == 0 {
|
||||
write!(outfile, "\n ")?;
|
||||
line_num = 20;
|
||||
}
|
||||
write!(outfile, "{:#04x}, ", v)?;
|
||||
line_num -= 1;
|
||||
}
|
||||
write!(outfile, "\n ];\n")?;
|
||||
|
||||
write!(
|
||||
outfile,
|
||||
" resources.push(({}, &{}));\n",
|
||||
var_outfile, var_outdata
|
||||
)?;
|
||||
|
||||
count += 1;
|
||||
}
|
||||
|
||||
outfile.write(b" do_extract(root_path, resources)?;\n")?;
|
||||
outfile.write(b" Ok(())\n")?;
|
||||
outfile.write(b"}\n")?;
|
||||
|
||||
outfile.write(
|
||||
br##"
|
||||
#[allow(dead_code)]
|
||||
fn do_extract(root_path: &str, resources: Vec<(&str, &[u8])>) -> ResultType<()> {
|
||||
let mut root_path = root_path.replace("\\", "/");
|
||||
if !root_path.ends_with('/') {
|
||||
root_path.push('/');
|
||||
}
|
||||
let root_path = Path::new(&root_path);
|
||||
for (outfile, data) in resources {
|
||||
let outfile_path = root_path.join(outfile);
|
||||
match outfile_path.parent().and_then(|p| p.to_str()) {
|
||||
None => {
|
||||
bail!("Failed to get parent of {}", outfile_path.display());
|
||||
}
|
||||
Some(p) => {
|
||||
fs::create_dir_all(p)?;
|
||||
let mut of = File::create(outfile_path)?;
|
||||
of.write_all(data)?;
|
||||
of.flush()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
"##,
|
||||
)?;
|
||||
|
||||
outfile.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let result = 2 + 2;
|
||||
assert_eq!(result, 4);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_extract() {
|
||||
// use super::*;
|
||||
// rc::extract_resources("D:").unwrap();
|
||||
// }
|
||||
}
|
@ -145,10 +145,6 @@ pub fn core_main() -> Option<Vec<String>> {
|
||||
args.len() > 1,
|
||||
));
|
||||
return None;
|
||||
} else if args[0] == "--extract" {
|
||||
#[cfg(feature = "with_rc")]
|
||||
hbb_common::allow_err!(crate::rc::extract_resources(&args[1]));
|
||||
return None;
|
||||
} else if args[0] == "--install-cert" {
|
||||
#[cfg(windows)]
|
||||
hbb_common::allow_err!(crate::platform::windows::install_cert(&args[1]));
|
||||
|
@ -64,9 +64,6 @@ mod hbbs_http;
|
||||
#[cfg(windows)]
|
||||
pub mod clipboard_file;
|
||||
|
||||
#[cfg(all(windows, feature = "with_rc"))]
|
||||
pub mod rc;
|
||||
|
||||
#[cfg(windows)]
|
||||
pub mod privacy_win_mag;
|
||||
|
||||
|
@ -928,7 +928,6 @@ pub fn copy_exe_cmd(src_exe: &str, exe: &str, path: &str) -> String {
|
||||
"
|
||||
{main_exe}
|
||||
copy /Y \"{ORIGIN_PROCESS_EXE}\" \"{path}\\{broker_exe}\"
|
||||
\"{src_exe}\" --extract \"{path}\"
|
||||
",
|
||||
main_exe = main_exe,
|
||||
path = path,
|
||||
|
38
src/rc.rs
38
src/rc.rs
@ -1,38 +0,0 @@
|
||||
use hbb_common::{bail, ResultType};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::prelude::*,
|
||||
path::Path,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn extract_resources(root_path: &str) -> ResultType<()> {
|
||||
let mut resources: Vec<(&str, &[u8])> = Vec::new();
|
||||
resources.push((outfile_4, &outdata_4));
|
||||
do_extract(root_path, resources)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn do_extract(root_path: &str, resources: Vec<(&str, &[u8])>) -> ResultType<()> {
|
||||
let mut root_path = root_path.replace("\\", "/");
|
||||
if !root_path.ends_with('/') {
|
||||
root_path.push('/');
|
||||
}
|
||||
let root_path = Path::new(&root_path);
|
||||
for (outfile, data) in resources {
|
||||
let outfile_path = root_path.join(outfile);
|
||||
match outfile_path.parent().and_then(|p| p.to_str()) {
|
||||
None => {
|
||||
bail!("Failed to get parent of {}", outfile_path.display());
|
||||
}
|
||||
Some(p) => {
|
||||
fs::create_dir_all(p)?;
|
||||
let mut of = File::create(outfile_path)?;
|
||||
of.write_all(data)?;
|
||||
of.flush()?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user