Backed out 5 changesets (bug 1890092, bug 1888683) for causing build bustages & crash...
[gecko.git] / third_party / rust / uniffi_bindgen / src / bindings / swift / test.rs
blobc3b2f152771d242c3b05a528e7c36fc8c4297dc8
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 use crate::{
6     bindings::{RunScriptOptions, TargetLanguage},
7     library_mode::generate_bindings,
8 };
9 use anyhow::{bail, Context, Result};
10 use camino::{Utf8Path, Utf8PathBuf};
11 use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
12 use std::ffi::OsStr;
13 use std::fs::{read_to_string, File};
14 use std::io::Write;
15 use std::process::{Command, Stdio};
16 use uniffi_testing::UniFFITestHelper;
18 /// Run Swift tests for a UniFFI test fixture
19 pub fn run_test(tmp_dir: &str, fixture_name: &str, script_file: &str) -> Result<()> {
20     run_script(
21         tmp_dir,
22         fixture_name,
23         script_file,
24         vec![],
25         &RunScriptOptions::default(),
26     )
29 /// Run a Swift script
30 ///
31 /// This function will set things up so that the script can import the UniFFI bindings for a crate
32 pub fn run_script(
33     tmp_dir: &str,
34     crate_name: &str,
35     script_file: &str,
36     args: Vec<String>,
37     options: &RunScriptOptions,
38 ) -> Result<()> {
39     let script_path = Utf8Path::new(".").join(script_file).canonicalize_utf8()?;
40     let test_helper = UniFFITestHelper::new(crate_name)?;
41     let out_dir = test_helper.create_out_dir(tmp_dir, &script_path)?;
42     let cdylib_path = test_helper.copy_cdylib_to_out_dir(&out_dir)?;
43     let generated_sources = GeneratedSources::new(crate_name, &cdylib_path, &out_dir)?;
45     // Compile the generated sources together to create a single swift module
46     compile_swift_module(
47         &out_dir,
48         &generated_sources.main_module,
49         &generated_sources.generated_swift_files,
50         &generated_sources.module_map,
51         options,
52     )?;
54     // Run the test script against compiled bindings
55     let mut command = create_command("swift", options);
56     command
57         .current_dir(&out_dir)
58         .arg("-I")
59         .arg(&out_dir)
60         .arg("-L")
61         .arg(&out_dir)
62         .args(calc_library_args(&out_dir)?)
63         .arg("-Xcc")
64         .arg(format!(
65             "-fmodule-map-file={}",
66             generated_sources.module_map
67         ))
68         .arg(&script_path)
69         .args(args);
70     let status = command
71         .spawn()
72         .context("Failed to spawn `swiftc` when running test script")?
73         .wait()
74         .context("Failed to wait for `swiftc` when running test script")?;
75     if !status.success() {
76         bail!("running `swift` to run test script failed ({:?})", command)
77     }
78     Ok(())
81 fn compile_swift_module<T: AsRef<OsStr>>(
82     out_dir: &Utf8Path,
83     module_name: &str,
84     sources: impl IntoIterator<Item = T>,
85     module_map: &Utf8Path,
86     options: &RunScriptOptions,
87 ) -> Result<()> {
88     let output_filename = format!("{DLL_PREFIX}testmod_{module_name}{DLL_SUFFIX}");
89     let mut command = create_command("swiftc", options);
90     command
91         .current_dir(out_dir)
92         .arg("-emit-module")
93         .arg("-module-name")
94         .arg(module_name)
95         .arg("-o")
96         .arg(output_filename)
97         .arg("-emit-library")
98         .arg("-Xcc")
99         .arg(format!("-fmodule-map-file={module_map}"))
100         .arg("-I")
101         .arg(out_dir)
102         .arg("-L")
103         .arg(out_dir)
104         .args(calc_library_args(out_dir)?)
105         .args(sources);
106     let status = command
107         .spawn()
108         .context("Failed to spawn `swiftc` when compiling bindings")?
109         .wait()
110         .context("Failed to wait for `swiftc` when compiling bindings")?;
111     if !status.success() {
112         bail!(
113             "running `swiftc` to compile bindings failed ({:?})",
114             command
115         )
116     };
117     Ok(())
120 // Stores sources generated by `uniffi-bindgen-swift`
121 struct GeneratedSources {
122     main_module: String,
123     generated_swift_files: Vec<Utf8PathBuf>,
124     module_map: Utf8PathBuf,
127 impl GeneratedSources {
128     fn new(crate_name: &str, cdylib_path: &Utf8Path, out_dir: &Utf8Path) -> Result<Self> {
129         let sources =
130             generate_bindings(cdylib_path, None, &[TargetLanguage::Swift], out_dir, false)?;
131         let main_source = sources
132             .iter()
133             .find(|s| s.package.name == crate_name)
134             .unwrap();
135         let main_module = main_source.config.bindings.swift.module_name();
136         let modulemap_glob = glob(&out_dir.join("*.modulemap"))?;
137         let module_map = match modulemap_glob.len() {
138             0 => bail!("No modulemap files found in {out_dir}"),
139             // Normally we only generate 1 module map and can return it directly
140             1 => modulemap_glob.into_iter().next().unwrap(),
141             // When we use multiple UDL files in a test, for example the ext-types fixture,
142             // then we get multiple module maps and need to combine them
143             _ => {
144                 let path = out_dir.join("combined.modulemap");
145                 let mut f = File::create(&path)?;
146                 write!(
147                     f,
148                     "{}",
149                     modulemap_glob
150                         .into_iter()
151                         .map(|path| Ok(read_to_string(path)?))
152                         .collect::<Result<Vec<String>>>()?
153                         .join("\n")
154                 )?;
155                 path
156             }
157         };
159         Ok(GeneratedSources {
160             main_module,
161             generated_swift_files: glob(&out_dir.join("*.swift"))?,
162             module_map,
163         })
164     }
167 fn create_command(program: &str, options: &RunScriptOptions) -> Command {
168     let mut command = Command::new(program);
169     if !options.show_compiler_messages {
170         // This prevents most compiler messages, but not remarks
171         command.arg("-suppress-warnings");
172         // This gets the remarks.  Note: swift will eventually get a `-supress-remarks` argument,
173         // maybe we can eventually move to that
174         command.stderr(Stdio::null());
175     }
176     command
179 // Wraps glob to use Utf8Paths and flattens errors
180 fn glob(globspec: &Utf8Path) -> Result<Vec<Utf8PathBuf>> {
181     glob::glob(globspec.as_str())?
182         .map(|globresult| Ok(Utf8PathBuf::try_from(globresult?)?))
183         .collect()
186 fn calc_library_args(out_dir: &Utf8Path) -> Result<Vec<String>> {
187     let results = glob::glob(out_dir.join(format!("{DLL_PREFIX}*{DLL_SUFFIX}")).as_str())?;
188     results
189         .map(|globresult| {
190             let path = Utf8PathBuf::try_from(globresult.unwrap())?;
191             Ok(format!(
192                 "-l{}",
193                 path.file_name()
194                     .unwrap()
195                     .strip_prefix(DLL_PREFIX)
196                     .unwrap()
197                     .strip_suffix(DLL_SUFFIX)
198                     .unwrap()
199             ))
200         })
201         .collect()