1use crate::cargo::Cargo;
2use crate::engine::{Build, Engine};
3use crate::error::Error;
4use std::path::{Path, PathBuf};
5use std::process::Command;
6
7pub struct Flutter {
8 root: PathBuf,
9}
10
11impl Flutter {
12 pub fn new() -> Result<Self, Error> {
13 let root = if let Ok(root) = std::env::var("FLUTTER_ROOT") {
14 PathBuf::from(root)
15 } else {
16 let flutter = which::which("flutter").or(Err(Error::FlutterNotFound))?;
17 let flutter = std::fs::canonicalize(flutter)?;
18 flutter
19 .parent()
20 .ok_or(Error::FlutterNotFound)?
21 .parent()
22 .ok_or(Error::FlutterNotFound)?
23 .to_owned()
24 };
25 Ok(Flutter { root })
26 }
27
28 pub fn root(&self) -> &Path {
29 &self.root
30 }
31
32 pub fn flutter(&self) -> Result<PathBuf, Error> {
33 which::which("flutter").or(Err(Error::FlutterNotFound))
34 }
35
36 pub fn engine_version(&self) -> Result<String, Error> {
37 let path = self
38 .root
39 .join("bin")
40 .join("internal")
41 .join("engine.version");
42 Ok(std::fs::read_to_string(path).map(|v| v.trim().to_owned())?)
43 }
44
45 pub fn bundle(&self, cargo: &Cargo, build: Build, dart_main: &Path) -> Result<(), Error> {
46 let flag = match build {
47 Build::Debug => "--debug",
48 Build::Release => "--release",
49 Build::Profile => "--profile",
50 };
51 let status = Command::new(self.flutter()?)
52 .current_dir(cargo.workspace().root())
53 .arg("build")
54 .arg("bundle")
55 .arg(flag)
56 .arg("--track-widget-creation")
57 .arg("--asset-dir")
58 .arg(cargo.build_dir().join("flutter_assets"))
59 .arg("--depfile")
60 .arg(cargo.build_dir().join("snapshot_blob.bin.d"))
61 .arg("--target")
62 .arg(dart_main)
63 .status()
64 .expect("flutter build bundle");
65 if status.code() != Some(0) {
66 return Err(Error::FlutterError);
67 }
68 Ok(())
69 }
70
71 pub fn attach(&self, cargo: &Cargo, debug_uri: &str) -> Result<(), Error> {
72 let status = Command::new(self.flutter()?)
73 .current_dir(cargo.workspace().root())
74 .arg("attach")
75 .arg("--device-id=flutter-tester")
76 .arg(format!("--debug-uri={}", debug_uri))
77 .status()
78 .expect("Success");
79 if status.code() != Some(0) {
80 return Err(Error::FlutterError);
81 }
82 Ok(())
83 }
84
85 pub fn aot(
86 &self,
87 cargo: &Cargo,
88 host_engine: &Engine,
89 target_engine: &Engine,
90 ) -> Result<(), Error> {
91 let root = cargo.workspace().root();
92 let build_dir = cargo.build_dir();
93 let host_engine_dir = host_engine.engine_dir();
94 let target_engine_dir = target_engine.engine_dir();
95 let snapshot = build_dir.join("kernel_snapshot.dill");
96
97 let status = Command::new(host_engine.dart()?)
98 .current_dir(root)
99 .arg(
100 host_engine_dir
101 .join("gen")
102 .join("frontend_server.dart.snapshot"),
103 )
104 .arg("--sdk-root")
105 .arg(host_engine_dir.join("flutter_patched_sdk"))
106 .arg("--target=flutter")
107 .arg("--aot")
108 .arg("--tfa")
109 .arg("-Ddart.vm.product=true")
110 .arg("--packages")
111 .arg(".packages")
112 .arg("--output-dill")
113 .arg(&snapshot)
114 .arg(root.join("lib").join("main.dart"))
115 .status()
116 .expect("Success");
117
118 if status.code() != Some(0) {
119 return Err(Error::FlutterError);
120 }
121
122 let gen_snapshot = [
123 "gen_snapshot",
124 "gen_snapshot_x64",
125 "gen_snapshot_x86",
126 "gen_snapshot_host_targeting_host",
127 "gen_snapshot.exe",
128 ]
129 .iter()
130 .map(|bin| target_engine_dir.join(bin))
131 .find(|path| path.exists())
132 .ok_or(Error::GenSnapshotNotFound)?;
133
134 let status = Command::new(gen_snapshot)
135 .current_dir(root)
136 .arg("--causal_async_stacks")
137 .arg("--deterministic")
138 .arg("--snapshot_kind=app-aot-elf")
139 .arg("--strip")
140 .arg(format!("--elf={}", build_dir.join("app.so").display()))
141 .arg(&snapshot)
142 .status()
143 .expect("Success");
144
145 if status.code() != Some(0) {
146 return Err(Error::FlutterError);
147 }
148
149 Ok(())
150 }
151
152 pub fn drive(
153 &self,
154 host_engine: &Engine,
155 cargo: &Cargo,
156 debug_uri: &str,
157 dart_main: &Path,
158 ) -> Result<(), Error> {
159 let mut file = dart_main.file_stem().unwrap().to_owned();
160 file.push("_test.dart");
161 let driver = dart_main.parent().unwrap().join(file);
162
163 std::env::set_var("VM_SERVICE_URL", debug_uri);
165 let status = Command::new(host_engine.dart()?)
166 .current_dir(cargo.workspace().root())
167 .arg(driver)
168 .status()
169 .expect("Success");
170 if status.code() != Some(0) {
171 return Err(Error::FlutterError);
172 }
173 Ok(())
174 }
175}