1extern crate curl;
2extern crate dirs;
3extern crate unzip;
4
5use curl::easy::Easy;
6use std::{
7 fs::{self, File},
8 io::{BufReader, Write},
9 path::{Path, PathBuf},
10 process::Command,
11 sync::{mpsc, Mutex},
12 thread,
13};
14
15pub mod util;
16
17pub use util::{get_flutter_version, Error};
18
19#[derive(PartialEq, Copy, Clone)]
20enum Target {
21 Linux,
22 Windows,
23 MacOS,
24}
25
26pub fn download(version: &str) -> (PathBuf, Result<mpsc::Receiver<(f64, f64)>, Error>) {
27 let libs_dir = dirs::cache_dir()
28 .expect("Cannot get cache dir")
29 .join("flutter-engine");
30
31 let url = download_url(version);
32 let dir = libs_dir.to_path_buf().join(version);
33
34 if !should_download(&dir) {
35 println!("Flutter engine already exist. Download not necessary");
36 return (libs_dir, Err(Error::AlreadyDownloaded));
37 }
38
39 let (tx, rx) = mpsc::channel();
40 thread::spawn(move || {
41 tx.send((0.0, 0.0)).unwrap();
45 fs::create_dir_all(&dir).unwrap();
48
49 let download_file = dir.join("engine.zip");
50
51 let mut file = File::create(&download_file).unwrap();
52
53 let tx = Mutex::new(tx);
54
55 let mut easy = Easy::new();
56
57 println!("Starting download from {}", url);
58 easy.url(&url).unwrap();
59 easy.follow_location(true).unwrap();
60 easy.progress(true).unwrap();
61 easy.progress_function(move |total, done, _, _| {
62 tx.lock().unwrap().send((total, done)).unwrap();
63 true
64 })
65 .unwrap();
66 easy.write_function(move |data| Ok(file.write(data).unwrap()))
67 .unwrap();
68 easy.perform().unwrap();
69
70 println!("Download finished");
71
72 println!("Extracting...");
73 let zip_file = File::open(&download_file).unwrap();
74 let reader = BufReader::new(zip_file);
75 let unzipper = unzip::Unzipper::new(reader, &dir);
76 unzipper.unzip().unwrap();
77
78 if target() == Target::MacOS {
80 Command::new("unzip")
81 .args(&[
82 "FlutterEmbedder.framework.zip",
83 "-d",
84 "FlutterEmbedder.framework",
85 ])
86 .current_dir(&dir)
87 .status()
88 .unwrap();
89
90 }
97 });
98
99 (libs_dir, Ok(rx))
100}
101
102pub fn download_url(version: &str) -> String {
103 let url = match target() {
104 Target::Linux => "{base_url}/flutter_infra/flutter/{version}/linux-x64/linux-x64-embedder",
105 Target::MacOS => {
106 "{base_url}/flutter_infra/flutter/{version}/darwin-x64/FlutterEmbedder.framework.zip"
107 }
108 Target::Windows => {
109 "{base_url}/flutter_infra/flutter/{version}/windows-x64/windows-x64-embedder.zip"
110 }
111 };
112 let base_url = std::env::var("FLUTTER_STORAGE_BASE_URL");
113 let base_url = base_url
114 .as_ref()
115 .map(String::as_str)
116 .unwrap_or("https://storage.googleapis.com");
117 url.replace("{base_url}", base_url)
118 .replace("{version}", version)
119}
120
121fn should_download(path: &Path) -> bool {
122 match target() {
123 Target::Linux => !path.join("libflutter_engine.so").exists(),
124 Target::MacOS => !path.join("FlutterEmbedder.framework").exists(),
125 Target::Windows => !path.join("flutter_engine.dll").exists(),
126 }
127}
128
129fn target() -> Target {
130 let target = std::env::var("TARGET").expect("Cannot determine target");
131 if target.contains("linux") {
132 Target::Linux
133 } else if target.contains("apple") {
134 Target::MacOS
135 } else if target.contains("windows") {
136 Target::Windows
137 } else {
138 panic!("Unknown target {}", target)
139 }
140}