-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add resources to package #138
Changes from all commits
d3858a2
9102ced
5cea533
11f4d3c
699b348
0393ff3
55219a2
92bdf07
18476bd
506f7ed
7aa5cb9
5ba2250
73ae1a6
8ba43db
29d54eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -18,6 +18,8 @@ pub struct CreatePackageConfig { | |||||||||||||||||||||||||||||||||||||||||||||||
pub out_dir: PathBuf, | ||||||||||||||||||||||||||||||||||||||||||||||||
/// The name for the Swift package | ||||||||||||||||||||||||||||||||||||||||||||||||
pub package_name: String, | ||||||||||||||||||||||||||||||||||||||||||||||||
/// Additional resources to copy into the package, first PathBuf is source and second is destination | ||||||||||||||||||||||||||||||||||||||||||||||||
pub resources: Vec<CopyBundleResourceDesc>, | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
impl CreatePackageConfig { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -27,12 +29,14 @@ impl CreatePackageConfig { | |||||||||||||||||||||||||||||||||||||||||||||||
paths: HashMap<ApplePlatform, PathBuf>, | ||||||||||||||||||||||||||||||||||||||||||||||||
out_dir: PathBuf, | ||||||||||||||||||||||||||||||||||||||||||||||||
package_name: String, | ||||||||||||||||||||||||||||||||||||||||||||||||
resources: Vec<CopyBundleResourceDesc>, | ||||||||||||||||||||||||||||||||||||||||||||||||
) -> Self { | ||||||||||||||||||||||||||||||||||||||||||||||||
Self { | ||||||||||||||||||||||||||||||||||||||||||||||||
bridge_dir, | ||||||||||||||||||||||||||||||||||||||||||||||||
paths, | ||||||||||||||||||||||||||||||||||||||||||||||||
out_dir, | ||||||||||||||||||||||||||||||||||||||||||||||||
package_name, | ||||||||||||||||||||||||||||||||||||||||||||||||
resources, | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -142,7 +146,7 @@ fn gen_xcframework(output_dir: &Path, config: &CreatePackageConfig) { | |||||||||||||||||||||||||||||||||||||||||||||||
bridge_dir.join("SwiftBridgeCore.h"), | ||||||||||||||||||||||||||||||||||||||||||||||||
&include_dir.join("SwiftBridgeCore.h"), | ||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||
.expect("Couldn't copy SwiftBirdgeCore header file"); | ||||||||||||||||||||||||||||||||||||||||||||||||
.expect("Couldn't copy SwiftBridgeCore header file"); | ||||||||||||||||||||||||||||||||||||||||||||||||
let bridge_project_dir = fs::read_dir(&bridge_dir) | ||||||||||||||||||||||||||||||||||||||||||||||||
.expect("Couldn't read generated directory") | ||||||||||||||||||||||||||||||||||||||||||||||||
.find_map(|file| { | ||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -299,15 +303,43 @@ fn gen_package(output_dir: &Path, config: &CreatePackageConfig) { | |||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||
.expect("Couldn't find project's bridging swift file"); | ||||||||||||||||||||||||||||||||||||||||||||||||
fs::write( | ||||||||||||||||||||||||||||||||||||||||||||||||
sources_dir.join(&bridge_project_swift_dir.file_name().unwrap()), | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
let bridge_swift = fs::read_to_string(&bridge_project_swift_dir) | ||||||||||||||||||||||||||||||||||||||||||||||||
.expect("Couldn't read project's bridging swift file"); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
let bridge_content = if config.resources.is_empty() { | ||||||||||||||||||||||||||||||||||||||||||||||||
format!("import RustXcframework\n{bridge_swift}") | ||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||
// add a Bundle extension with a convenience accessor for the resources making | ||||||||||||||||||||||||||||||||||||||||||||||||
// them accessible from Swift with `Bundle.<package_name>` | ||||||||||||||||||||||||||||||||||||||||||||||||
format!( | ||||||||||||||||||||||||||||||||||||||||||||||||
"import RustXcframework\n{}", | ||||||||||||||||||||||||||||||||||||||||||||||||
fs::read_to_string(&bridge_project_swift_dir) | ||||||||||||||||||||||||||||||||||||||||||||||||
.expect("Couldn't read project's bridging swift file") | ||||||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||
.expect("Couldn't copy project's bridging swift file to the package"); | ||||||||||||||||||||||||||||||||||||||||||||||||
r#"import RustXcframework | ||||||||||||||||||||||||||||||||||||||||||||||||
import Foundation | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
{bridge_swift} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
extension Bundle {{ | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What does this do? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added comment in code |
||||||||||||||||||||||||||||||||||||||||||||||||
public static var {}: Bundle = .module | ||||||||||||||||||||||||||||||||||||||||||||||||
}} | ||||||||||||||||||||||||||||||||||||||||||||||||
"#, | ||||||||||||||||||||||||||||||||||||||||||||||||
first_lowercased(&config.package_name) | ||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
let bridge_path = sources_dir.join(&bridge_project_swift_dir.file_name().unwrap()); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
fs::write(bridge_path, bridge_content) | ||||||||||||||||||||||||||||||||||||||||||||||||
.expect("Couldn't copy project's bridging swift file to the package"); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
let resource_entries = config | ||||||||||||||||||||||||||||||||||||||||||||||||
.resources | ||||||||||||||||||||||||||||||||||||||||||||||||
.iter() | ||||||||||||||||||||||||||||||||||||||||||||||||
.map(|r| { | ||||||||||||||||||||||||||||||||||||||||||||||||
let name = r.copy(&sources_dir); | ||||||||||||||||||||||||||||||||||||||||||||||||
format!(" .copy(\"{name}\")") | ||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||
.collect::<Vec<String>>() | ||||||||||||||||||||||||||||||||||||||||||||||||
.join("\n"); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
// Generate Package.swift | ||||||||||||||||||||||||||||||||||||||||||||||||
let package_name = &config.package_name; | ||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -329,7 +361,11 @@ let package = Package( | |||||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||||||
.target( | ||||||||||||||||||||||||||||||||||||||||||||||||
name: "{package_name}", | ||||||||||||||||||||||||||||||||||||||||||||||||
dependencies: ["RustXcframework"]) | ||||||||||||||||||||||||||||||||||||||||||||||||
dependencies: ["RustXcframework"], | ||||||||||||||||||||||||||||||||||||||||||||||||
resources: [ | ||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we move the package string generation out into its own function swift-bridge/crates/swift-bridge-build/src/package.rs Lines 313 to 335 in 03de7ea
Then add a test to the bottom of this file that generates the package description for the My main concern here is that right now the writing to disk / process spawning stuff is tightly coupled to things that it doesn't need to be (I know this was here before you, so not saying that you did that!). So I'm asking to add a test for this resources thing so that we at least begin take a small step in the right direction and make it easier to make our Swift package creation more powerful over time. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did make an effort to follow your (current) style of programming, imitating how things are done. I think it is better if you, as the owner of this project, set the new precedence instead of me as I will probably not do it as you like it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually all of the package stuff was contributed by a contributor. However, makes sense to me! |
||||||||||||||||||||||||||||||||||||||||||||||||
{resource_entries} | ||||||||||||||||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||
] | ||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||
"# | ||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -338,3 +374,92 @@ let package = Package( | |||||||||||||||||||||||||||||||||||||||||||||||
fs::write(output_dir.join("Package.swift"), package_swift) | ||||||||||||||||||||||||||||||||||||||||||||||||
.expect("Couldn't write Package.swift file"); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
/// make first char lowercase | ||||||||||||||||||||||||||||||||||||||||||||||||
fn first_lowercased(name: &str) -> String { | ||||||||||||||||||||||||||||||||||||||||||||||||
let mut c = name.chars(); | ||||||||||||||||||||||||||||||||||||||||||||||||
match c.next() { | ||||||||||||||||||||||||||||||||||||||||||||||||
None => String::new(), | ||||||||||||||||||||||||||||||||||||||||||||||||
Some(f) => f.to_lowercase().collect::<String>() + c.as_str(), | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
/// Describes a bundle resource to copy from some source path to some destination. | ||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||
/// The source is a path to an existing file or directory that you wish to bundle. | ||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||
/// The destination is a relative path that the resource will get copied to within the | ||||||||||||||||||||||||||||||||||||||||||||||||
/// generated Swift package's `Sources/YourLibraryName` directory. | ||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||
/// # Examples | ||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||
/// ``` | ||||||||||||||||||||||||||||||||||||||||||||||||
/// # use swift_bridge_build::CopyBundleResourceDesc; | ||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||
/// // If you are creating a Swift Package called "MyLibrary", this descriptor will | ||||||||||||||||||||||||||||||||||||||||||||||||
/// // lead to the package generator copying the `/path/to/images` directory to the | ||||||||||||||||||||||||||||||||||||||||||||||||
/// // the Swift package's `Sources/MyLibrary/assets/icons` directory. | ||||||||||||||||||||||||||||||||||||||||||||||||
/// let resource = CopyBundleResourceDesc::new("/path/to/images", "assets/icons"); | ||||||||||||||||||||||||||||||||||||||||||||||||
/// ``` | ||||||||||||||||||||||||||||||||||||||||||||||||
pub struct CopyBundleResourceDesc { | ||||||||||||||||||||||||||||||||||||||||||||||||
source: PathBuf, | ||||||||||||||||||||||||||||||||||||||||||||||||
destination: PathBuf, | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
impl CopyBundleResourceDesc { | ||||||||||||||||||||||||||||||||||||||||||||||||
/// Parse a colon-separated pair of paths into a [`CopyBundleResourceDesc`]. | ||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||
/// Examples: `source:destination`, `some_folder/some_file.txt:to_folder/some_file.txt` | ||||||||||||||||||||||||||||||||||||||||||||||||
/// or `source_folder:destination_folder`. | ||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||
/// # Panics | ||||||||||||||||||||||||||||||||||||||||||||||||
/// Panics if there is no colon in the pair. | ||||||||||||||||||||||||||||||||||||||||||||||||
pub fn from(pair: String) -> Self { | ||||||||||||||||||||||||||||||||||||||||||||||||
let mut split = pair.split(':'); | ||||||||||||||||||||||||||||||||||||||||||||||||
let from = PathBuf::from(split.next().unwrap()); | ||||||||||||||||||||||||||||||||||||||||||||||||
let to = PathBuf::from( | ||||||||||||||||||||||||||||||||||||||||||||||||
split | ||||||||||||||||||||||||||||||||||||||||||||||||
.next() | ||||||||||||||||||||||||||||||||||||||||||||||||
.expect(&format!("Invalid resource pair: {pair}")), | ||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||
if !from.exists() { | ||||||||||||||||||||||||||||||||||||||||||||||||
panic!("Resource file does not exist: {from:?}"); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
if to.is_absolute() { | ||||||||||||||||||||||||||||||||||||||||||||||||
panic!("Resource destination must be relative: {to:?}"); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
Self { | ||||||||||||||||||||||||||||||||||||||||||||||||
source: from, | ||||||||||||||||||||||||||||||||||||||||||||||||
destination: to, | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
/// See [`CopyBundleResourceDesc`] for documentation. | ||||||||||||||||||||||||||||||||||||||||||||||||
/// | ||||||||||||||||||||||||||||||||||||||||||||||||
/// # Panics | ||||||||||||||||||||||||||||||||||||||||||||||||
/// Panics if the destination is an absolute path. | ||||||||||||||||||||||||||||||||||||||||||||||||
pub fn new(source: impl Into<PathBuf>, destination: impl Into<PathBuf>) -> Self { | ||||||||||||||||||||||||||||||||||||||||||||||||
let destination = destination.into(); | ||||||||||||||||||||||||||||||||||||||||||||||||
assert!(destination.is_relative()); | ||||||||||||||||||||||||||||||||||||||||||||||||
Self { | ||||||||||||||||||||||||||||||||||||||||||||||||
source: source.into(), | ||||||||||||||||||||||||||||||||||||||||||||||||
destination, | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
fn copy(&self, sources_dir: &Path) -> String { | ||||||||||||||||||||||||||||||||||||||||||||||||
let to = sources_dir.join(&self.destination); | ||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||
if self.source.is_dir() { | ||||||||||||||||||||||||||||||||||||||||||||||||
copy_dir::copy_dir(&self.source, &to).expect(&format!( | ||||||||||||||||||||||||||||||||||||||||||||||||
"Could not copy resource directory {:?} to {to:?}", | ||||||||||||||||||||||||||||||||||||||||||||||||
self.source | ||||||||||||||||||||||||||||||||||||||||||||||||
)); | ||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||
if let Some(parent) = to.parent() { | ||||||||||||||||||||||||||||||||||||||||||||||||
fs::create_dir_all(parent).expect("Couldn't create directory for resource"); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
fs::copy(&self.source, to).expect("Couldn't copy resource"); | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
self.destination.display().to_string() | ||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -100,4 +100,18 @@ fn create_package_command() -> Command<'static> { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.required(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.help("The name for the Swift Package"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.arg( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Arg::new("resource") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How did you land on the Also, can we leave a TODO comment that whenever clap supports array arguments we should switch to switch to using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. TBH, it's not a great solution to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the TODO |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.long("resource") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.takes_value(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// TODO whenever clap supports array arguments we should switch to switch to using [PathBuf; 2] instead. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// See: https://github.com/clap-rs/clap/issues/1682 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.value_name("SRC_PATH:DEST_PATH") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.multiple_values(true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.help( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we update the book example to include bundling a resource? swift-bridge/book/src/building/swift-packages/README.md Lines 132 to 171 in 2783798
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added it to the end as it is not mandatory to add resources. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"A resource to copy to the package. \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Use colon-separated source and destination paths. \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Ex: --resource=./folder/myresource.txt:packagefolder/myresource.txt", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add some resources?
resource-test.txt
nested/directory/test.txt
Then right after this
create_package
call we can call afn assert_resources_bundled
Which checks the "swift-package-rust-library-fixture/MySwiftPackage" Sources dir to make sure that the resources are in there.
Also, if there's a way to check that the resources are accessible on the Swift side, that would be great. Could check in here
swift-bridge/SwiftRustIntegrationTestRunner/swift-package-test-package/Tests/swift-package-test-packageTests/swift_package_test_packageTests.swift
Lines 5 to 17 in a207c24