1#![type_length_limit = "2149570"]
2pub mod errors {
3 pub use anyhow::{anyhow, bail, Context, Error, Result};
4}
5
6mod android;
7#[cfg(target_os = "macos")]
8mod apple;
9pub mod config;
10pub mod device;
11mod host;
12pub mod overlay;
13pub mod platform;
14pub mod plugin;
15pub mod project;
16mod script;
17mod ssh;
18mod toolchain;
19pub mod utils;
20
21pub use crate::config::Configuration;
22
23#[cfg(target_os = "macos")]
24use crate::apple::{IosManager, TvosManager, WatchosManager};
25use crate::config::PlatformConfiguration;
26
27use crate::platform::regular_platform::RegularPlatform;
28use crate::project::Project;
29use anyhow::{anyhow, Context};
30use dyn_clone::DynClone;
31use std::fmt::Display;
32use std::{path, sync};
33
34use crate::errors::Result;
35
36pub struct Dinghy {
37 devices: Vec<sync::Arc<Box<dyn Device>>>,
38 platforms: Vec<(String, sync::Arc<Box<dyn Platform>>)>,
39}
40
41impl Dinghy {
42 pub fn probe(conf: &sync::Arc<Configuration>) -> Result<Dinghy> {
43 let mut managers: Vec<Box<dyn PlatformManager>> = vec![];
44 if let Some(man) = host::HostManager::probe(conf) {
45 managers.push(Box::new(man));
46 }
47 if let Some(man) = android::AndroidManager::probe() {
48 managers.push(Box::new(man));
49 }
50 if let Some(man) = script::ScriptDeviceManager::probe(conf.clone()) {
51 managers.push(Box::new(man));
52 }
53 if let Some(man) = ssh::SshDeviceManager::probe(conf.clone()) {
54 managers.push(Box::new(man));
55 }
56 #[cfg(target_os = "macos")]
57 {
58 std::thread::sleep(std::time::Duration::from_millis(100));
59 if let Some(man) = IosManager::new().context("Could not initialize iOS manager")? {
60 managers.push(Box::new(man));
61 }
62 if let Some(man) = TvosManager::new().context("Could not initialize tvOS manager")? {
63 managers.push(Box::new(man));
64 }
65 if let Some(man) = WatchosManager::new().context("Could not initialize tvOS manager")? {
66 managers.push(Box::new(man));
67 }
68 }
69 if let Some(man) = plugin::PluginManager::probe(conf.clone()) {
70 managers.push(Box::new(man));
71 }
72
73 let mut devices = vec![];
74 let mut platforms = vec![];
75 for man in managers.into_iter() {
76 devices.extend(
77 man.devices()
78 .context("Could not list devices")?
79 .into_iter()
80 .map(|it| sync::Arc::new(it)),
81 );
82 platforms.extend(
83 man.platforms()
84 .context("Could not list platforms")?
85 .into_iter()
86 .map(|it| (it.id(), sync::Arc::new(it))),
87 );
88 }
89 for (platform_name, platform_conf) in &conf.platforms {
90 if platform_name == "host" {
91 continue;
92 }
93 let rustc_triple = platform_conf
94 .rustc_triple
95 .as_ref()
96 .ok_or_else(|| anyhow!("Platform {} has no rustc_triple", platform_name))?;
97 let pf = RegularPlatform::new(
98 platform_conf.clone(),
99 platform_name.to_string(),
100 rustc_triple.clone(),
101 platform_conf
102 .toolchain
103 .clone()
104 .map(|it| path::PathBuf::from(it))
105 .or(dirs::home_dir()
106 .map(|it| it.join(".dinghy").join("toolchain").join(platform_name)))
107 .with_context(|| format!("Toolchain missing for platform {}", platform_name))?,
108 )
109 .with_context(|| format!("Could not assemble platform {}", platform_name))?;
110 platforms.push((pf.id(), sync::Arc::new(pf)));
111 }
112 Ok(Dinghy { devices, platforms })
113 }
114
115 pub fn devices(&self) -> Vec<sync::Arc<Box<dyn Device>>> {
116 self.devices.clone()
117 }
118
119 pub fn host_platform(&self) -> sync::Arc<Box<dyn Platform>> {
120 self.platforms[0].1.clone()
121 }
122
123 pub fn platforms(&self) -> Vec<sync::Arc<Box<dyn Platform>>> {
124 self.platforms
125 .iter()
126 .map(|&(_, ref platform)| platform.clone())
127 .collect()
128 }
129
130 pub fn platform_by_name(
131 &self,
132 platform_name_filter: &str,
133 ) -> Option<sync::Arc<Box<dyn Platform>>> {
134 self.platforms
135 .iter()
136 .filter(|&&(ref platform_name, _)| platform_name == platform_name_filter)
137 .map(|&(_, ref platform)| platform.clone())
138 .next()
139 }
140}
141
142pub trait Device: std::fmt::Debug + Display + DeviceCompatibility + DynClone {
143 fn clean_app(&self, build_bundle: &BuildBundle) -> Result<()>;
144
145 fn debug_app(
146 &self,
147 project: &Project,
148 build: &Build,
149 args: &[&str],
150 envs: &[&str],
151 ) -> Result<BuildBundle>;
152
153 fn id(&self) -> &str;
154
155 fn name(&self) -> &str;
156
157 fn run_app(
158 &self,
159 project: &Project,
160 build: &Build,
161 args: &[&str],
162 envs: &[&str],
163 ) -> Result<BuildBundle>;
164}
165
166dyn_clone::clone_trait_object!(Device);
167
168pub trait DeviceCompatibility {
169 fn is_compatible_with_regular_platform(&self, _platform: &RegularPlatform) -> bool {
170 false
171 }
172
173 fn is_compatible_with_host_platform(&self, _platform: &host::HostPlatform) -> bool {
174 false
175 }
176
177 #[cfg(target_os = "macos")]
178 fn is_compatible_with_simulator_platform(
179 &self,
180 _platform: &apple::AppleDevicePlatform,
181 ) -> bool {
182 false
183 }
184}
185
186pub trait Platform: std::fmt::Debug {
187 fn setup_env(&self, project: &Project, setup_args: &SetupArgs) -> Result<()>;
188
189 fn id(&self) -> String;
190
191 fn is_compatible_with(&self, device: &dyn Device) -> bool;
192
193 fn is_host(&self) -> bool;
194 fn rustc_triple(&self) -> &str;
195
196 fn strip(&self, build: &mut Build) -> Result<()>;
197 fn sysroot(&self) -> Result<Option<path::PathBuf>>;
198}
199
200impl Display for dyn Platform {
201 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
202 write!(fmt, "{}", self.id())
203 }
204}
205
206pub trait PlatformManager {
207 fn devices(&self) -> Result<Vec<Box<dyn Device>>>;
208 fn platforms(&self) -> Result<Vec<Box<dyn Platform>>>;
209}
210
211#[derive(Clone, Debug)]
212pub struct Build {
213 pub setup_args: SetupArgs,
214 pub dynamic_libraries: Vec<path::PathBuf>,
215 pub runnable: Runnable,
216 pub target_path: path::PathBuf,
217 pub files_in_run_args: Vec<path::PathBuf>,
218}
219
220#[derive(Clone, Debug)]
221pub struct SetupArgs {
222 pub verbosity: i8,
223 pub forced_overlays: Vec<String>,
224 pub envs: Vec<String>,
225 pub cleanup: bool,
226 pub strip: bool,
227 pub device_id: Option<String>,
228}
229
230impl SetupArgs {
231 pub fn get_runner_command(&self, platform_id: &str) -> String {
232 let mut extra_args = String::new();
233 if self.verbosity > 0 {
234 for _ in 0..self.verbosity {
235 extra_args.push_str("-v ")
236 }
237 }
238 if self.verbosity < 0 {
239 for _ in 0..-self.verbosity {
240 extra_args.push_str("-q ")
241 }
242 }
243 if self.cleanup {
244 extra_args.push_str("--cleanup ")
245 }
246 if self.strip {
247 extra_args.push_str("--strip ")
248 }
249 if let Some(device_id) = &self.device_id {
250 extra_args.push_str("-d ");
251 extra_args.push_str(&device_id);
252 extra_args.push(' ');
253 }
254 for env in &self.envs {
255 extra_args.push_str("-e ");
256 extra_args.push_str(env);
257 extra_args.push(' ');
258 }
259
260 format!(
261 "{} -p {} {}runner --",
262 std::env::current_exe().unwrap().to_str().unwrap(),
263 platform_id,
264 extra_args
265 )
266 }
267}
268
269#[derive(Clone, Debug, Default)]
270pub struct BuildBundle {
271 pub id: String,
272 pub bundle_dir: path::PathBuf,
273 pub bundle_exe: path::PathBuf,
274 pub lib_dir: path::PathBuf,
275 pub root_dir: path::PathBuf,
276 pub app_id: Option<String>,
277}
278
279impl BuildBundle {
280 fn replace_prefix_with<P: AsRef<path::Path>>(&self, path: P) -> Result<Self> {
281 Ok(BuildBundle {
282 bundle_dir: utils::normalize_path(
283 &path
284 .as_ref()
285 .to_path_buf()
286 .join(self.bundle_dir.strip_prefix(&self.root_dir)?),
287 ),
288 bundle_exe: utils::normalize_path(
289 &path
290 .as_ref()
291 .to_path_buf()
292 .join(self.bundle_exe.strip_prefix(&self.root_dir)?),
293 ),
294 lib_dir: utils::normalize_path(
295 &path
296 .as_ref()
297 .to_path_buf()
298 .join(self.lib_dir.strip_prefix(&self.root_dir)?),
299 ),
300 root_dir: path.as_ref().to_path_buf(),
301 ..self.clone()
302 })
303 }
304}
305
306#[derive(Clone, Debug, Default)]
307pub struct Runnable {
308 pub id: String,
309 pub package_name: String,
310 pub exe: path::PathBuf,
311 pub source: path::PathBuf,
312 pub skip_source_copy: bool,
313}