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/. */
6 bindings::{RunScriptOptions, TargetLanguage},
7 library_mode::generate_bindings,
9 use anyhow::{bail, Context, Result};
10 use camino::{Utf8Path, Utf8PathBuf};
11 use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
13 use std::fs::{read_to_string, File};
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<()> {
25 &RunScriptOptions::default(),
29 /// Run a Swift script
31 /// This function will set things up so that the script can import the UniFFI bindings for a crate
37 options: &RunScriptOptions,
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
48 &generated_sources.main_module,
49 &generated_sources.generated_swift_files,
50 &generated_sources.module_map,
54 // Run the test script against compiled bindings
55 let mut command = create_command("swift", options);
57 .current_dir(&out_dir)
62 .args(calc_library_args(&out_dir)?)
65 "-fmodule-map-file={}",
66 generated_sources.module_map
72 .context("Failed to spawn `swiftc` when running test script")?
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)
81 fn compile_swift_module<T: AsRef<OsStr>>(
84 sources: impl IntoIterator<Item = T>,
85 module_map: &Utf8Path,
86 options: &RunScriptOptions,
88 let output_filename = format!("{DLL_PREFIX}testmod_{module_name}{DLL_SUFFIX}");
89 let mut command = create_command("swiftc", options);
99 .arg(format!("-fmodule-map-file={module_map}"))
104 .args(calc_library_args(out_dir)?)
108 .context("Failed to spawn `swiftc` when compiling bindings")?
110 .context("Failed to wait for `swiftc` when compiling bindings")?;
111 if !status.success() {
113 "running `swiftc` to compile bindings failed ({:?})",
120 // Stores sources generated by `uniffi-bindgen-swift`
121 struct GeneratedSources {
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> {
130 generate_bindings(cdylib_path, None, &[TargetLanguage::Swift], out_dir, false)?;
131 let main_source = sources
133 .find(|s| s.package.name == crate_name)
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
144 let path = out_dir.join("combined.modulemap");
145 let mut f = File::create(&path)?;
151 .map(|path| Ok(read_to_string(path)?))
152 .collect::<Result<Vec<String>>>()?
159 Ok(GeneratedSources {
161 generated_swift_files: glob(&out_dir.join("*.swift"))?,
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());
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?)?))
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())?;
190 let path = Utf8PathBuf::try_from(globresult.unwrap())?;
195 .strip_prefix(DLL_PREFIX)
197 .strip_suffix(DLL_SUFFIX)