Skip to content

Conversation

crazytonyli
Copy link
Contributor

At the moment, we run make xcframework when we update the rust code and need to use them in the Swift package. The command builds the rust library against all Apple platforms (11 targets in total). But in reality, most of the time, we really only need libraries for one platform or one target—the one we choose in Xcode.

Alternatives to the make xcframework command

This PR introduces a couple of new things:

  1. make xcframework-only-<ios|macos|..> to create a xcframework for a given platform, which will save us from building rust library for other platforms that we don't need during development.
  2. By default, use debug configuration (a.k.a cargo dev profile) during development and use release configuration on CI, which will also save us build time during development because dev build is about 1.2 times faster than release build.

New tools cargo binary package

This PR adds a new tools binary package to Cargo. It's a command line application, which for the moment only has a create-xcframework command. We may need to add more later (for example, release process).

$ cargo run --quiet --bin tools -- --help
Usage: tools <COMMAND>

Commands:
  create-xcframework
  help                Print this message or the help of the given subcommand(s)

Options:
  -h, --help  Print help

$ cargo run --quiet --bin tools -- create-xcframework --help
Usage: tools create-xcframework [OPTIONS] --targets <TARGETS>...

Options:
      --targets <TARGETS>...  List of targets whose static libraries should be included in the xcframework
      --profile <PROFILE>     Cargo profile used to build the targets [default: release]
  -h, --help                  Print help

The create-xcframework command is a more convenient way to call xcodebuild -create-xcframework. It takes a list of cargo targets(which should have been built by this point), combine some of them into fat libraries if needed, and then call the xcodebuild command to create a xcframework. That enables us easily creating xcframework for selected targets/platforms from Makefile.

By using this tool, Makefile now only builds targets and the tools create-xcframework command takes care of creating an xcframework from built targets.

Test Instructions

The existing make xcframework should be covered by CI.

You can run [CARGO_PROFILE=dev|release] make clean xcframework-only-<platform> locally to verify xcframework is built accordingly.

You can also try calling the create-xcframework tool directly, but you'll need to build some targets before passing them to command line.

cargo run --quiet --bin tools -- create-xcframework --targets ...

xcframework-only-macos: $(build-apple-platform-macos)
xcframework-only-ios: $(build-apple-platform-ios)
xcframework-only-tvos: $(build-apple-platform-tvos)
xcframework-only-watchos: $(build-apple-platform-watchos)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to consolidate all of the above into one wildcard target: xcframework-only-%, but couldn't get it to work. Let me know if you have any ideas.

Copy link
Contributor

@oguzkocer oguzkocer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@crazytonyli I went through the Rust implementation and left you some non-blocking notes. In some cases, I stopped duplicating some of the comments as the earlier ones applied elsewhere as well. I can mention them in my follow up review if necessary.


use crate::Action;

static LIBRARY_FILENAME: &str = "libwordpress.a";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
static LIBRARY_FILENAME: &str = "libwordpress.a";
const LIBRARY_FILENAME: &str = "libwordpress.a";

I assume this value is not supposed to be modified with a 'static lifetime? If so, we should use const instead.

Comment on lines 319 to 322
std::fs::create_dir_all(dir)
.with_context(|| format!("Failed to create directory: {:?}", dir))?;

Ok(())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
std::fs::create_dir_all(dir)
.with_context(|| format!("Failed to create directory: {:?}", dir))?;
Ok(())
std::fs::create_dir_all(dir).with_context(|| format!("Failed to create directory: {:?}", dir))

You can return the result directly since create_dir_all returns std.io::Result<()>.


impl XCFramework {
fn new(targets: &Vec<String>, profile: &str) -> Result<Self> {
let headers = PathBuf::from("target/swift-bindings/headers");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should "target/swift-bindings/headers" be a const?

Comment on lines 68 to 71
return Err(Error::msg(format!(
"Headers not found: {}",
headers.display()
)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return Err(Error::msg(format!(
"Headers not found: {}",
headers.display()
)));
anyhow::bail!("Headers not found: {}", headers.display());

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL!

let mut parts = target.split('-');
_ /* arch */= parts.next();
if parts.next() != Some("apple") {
return Err(Error::msg(format!("{} is not an Apple platform", target)));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return Err(Error::msg(format!("{} is not an Apple platform", target)));
anyhow::bail!("{} is not an Apple platform", target);

Comment on lines 248 to 252
let os = match parts.next() {
Some("darwin") => ApplePlatform::MacOS,
Some("ios") => ApplePlatform::IOS,
Some("tvos") => ApplePlatform::TvOS,
Some("watchos") => ApplePlatform::WatchOS,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably worth implementing as ApplePlatform::from_str.

let llvm_target = json
.get("llvm-target")
.and_then(|t| t.as_str())
.ok_or(Error::msg("No llvm-target in command output"))?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use with_context for consistency?

Suggested change
.ok_or(Error::msg("No llvm-target in command output"))?;
.with_context(|| format!("No llvm-target in command output"))?;

Comment on lines 122 to 126
let mut libraries: Vec<PathBuf> = Vec::new();
for lib in &self.libraries {
libraries.push(lib.create(temp_dir)?);
}
Ok(libraries)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let mut libraries: Vec<PathBuf> = Vec::new();
for lib in &self.libraries {
libraries.push(lib.create(temp_dir)?);
}
Ok(libraries)
self.libraries
.iter()
.map(|lib| lib.create(temp_dir))
.collect()


impl Action for CreateXCFramework {
fn run(&self) -> Result<()> {
let temp_dir = std::env::temp_dir().join("wp-rs-xcframework");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should wp-rs-xcframework be a const?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll leave this string local to the function, because it's a temporary directory name which can change without affecting anyone.

recreate_directory(&dest)?;
std::fs::rename(temp_dest, &dest).with_context(|| "Failed to move xcframework")?;

println!("xcframework created at {}", &dest.display());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
println!("xcframework created at {}", &dest.display());
println!("xcframework created at {}", dest.display());

Copy link
Contributor

@jkmassel jkmassel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Chatted about whether we want to use this or a different scripting language for this (ruby, because it's our release tooling language, or Swift because this tool is for folks writing swift code)

We'll land it as-is for now (with Oguz's comments addressed), and we can adjust later if we need to.

Thanks Tony!

@crazytonyli crazytonyli enabled auto-merge (squash) May 28, 2024 23:40
@crazytonyli
Copy link
Contributor Author

@oguzkocer Thanks for your review. I have addressed all your comments. I'll merge this PR after CI jobs pass. Happy to address any further comments in follow up PRs.

@crazytonyli crazytonyli merged commit d320476 into trunk May 28, 2024
@crazytonyli crazytonyli deleted the create-xcframework-in-rust branch May 28, 2024 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants