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
Introduce a xcframework dependency type #769
Introduce a xcframework dependency type #769
Conversation
5a2069f
to
0cdf13f
Compare
0cdf13f
to
9c21445
Compare
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.
Looks good @lakpa 👍
some final bits:
-
The dependencies section in the docs will need a new entry for
xcframework
https://github.com/tuist/tuist/blob/master/docs/usage/dependencies.mdx -
Worth adding a static library xcframework fixture too
import Foundation | ||
import XCTest | ||
|
||
@testable import xcframeworks |
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.
typo: @testable import App
let pbxproj = PBXProj() | ||
let pbxTarget = PBXNativeTarget(name: "Test") | ||
let sourceRootPath = AbsolutePath("/") | ||
|
||
let group = PBXGroup() | ||
pbxproj.add(object: group) | ||
|
||
let from = AbsolutePath("/Frameworks/") | ||
let fileAbsolutePath = AbsolutePath("/Frameworks/Test.xcframework") | ||
let fileRelativePath = RelativePath("./Test.xcframework") | ||
let fileElements = ProjectFileElements() | ||
fileElements.addFileElement(from: from, | ||
fileAbsolutePath: fileAbsolutePath, | ||
fileRelativePath: fileRelativePath, | ||
name: nil, | ||
toGroup: group, | ||
pbxproj: pbxproj) | ||
|
||
let wakaFile = fileElements.file(path: fileAbsolutePath) |
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.
I wonder if we can benefit from a test helper here to make the test more concise.
// Given
let xcframeworkPath = AbsolutePath("/Frameworks/Test.xcframework")
let dependencies: [DependencyReference] = [
.absolute(xcframeworkPath)
]
let fileElements = createFileElements(for: xcframeworkPath)
// ...
Note: "waka" was the name of a framework used in another test, there is no need to use it here as the framework we have here is called "test", the name doesn't really impact the test.
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.
Agreed, we could make code concise by using a helper.
// Given | ||
let library = LibraryNode.test() | ||
let framework = FrameworkNode.test() | ||
let cocoapods = CocoaPodsNode.test() | ||
let xcframework = try XCFrameworkNode.test() |
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.
Does this helper exist? is there a reason it's throwing?
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.
Thanks for spotting, i was running some trials, it got left over after. Will remove it.
let copyBuildPhase: PBXCopyFilesBuildPhase? = pbxTarget.buildPhases.last as? PBXCopyFilesBuildPhase | ||
XCTAssertEqual(copyBuildPhase?.name, "Embed Frameworks") | ||
let wakaBuildFile: PBXBuildFile? = copyBuildPhase?.files?.last | ||
XCTAssertEqual(wakaBuildFile?.file, wakaFile) | ||
let settings: [String: [String]]? = wakaBuildFile?.settings as? [String: [String]] | ||
XCTAssertEqual(settings, ["ATTRIBUTES": ["CodeSignOnCopy"]]) |
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.
Similarly here we could benefit from some array comparisons to make the asserts more readable
(this is somewhat subjective)
// Then
let copyBuildPhase = try XCTUnwrap(pbxTarget.embedFrameworksBuildPhases().first)
let buildFiles = try XCTUnwrap(copyBuildPhase.files)
XCTAssertEqual(buildFiles.map { $0.file?.path }, [
"Test.xcframework"
])
XCTAssertEqual(buildFiles.map { $0.settings as? [String: [String]] }, [
["ATTRIBUTES": ["CodeSignOnCopy"]]
])
0cc6b9d
to
5f6241a
Compare
@@ -169,7 +169,19 @@ final class LinkGenerator: LinkGenerating { | |||
|
|||
try dependencies.forEach { dependency in | |||
if case let GraphDependencyReference.absolute(path) = dependency { | |||
precompiledFrameworkPaths.append(path) | |||
if path.extension == "xcframework" { |
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.
👏
@@ -88,6 +88,39 @@ final class LinkGeneratorErrorTests: XCTestCase { | |||
XCTAssertEqual($0 as? LinkGeneratorError, LinkGeneratorError.missingProduct(name: "Test")) | |||
} | |||
} | |||
|
|||
func test_generateEmbedPhase_setupEmbedFrameworksBuildPhase_whenXCFrameworkIsPresent() throws { |
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.
Beautifully tested 👏
features/generate.feature
Outdated
@@ -230,3 +230,11 @@ Scenario: The project is an iOS application with watch app (ios_app_with_watchap | |||
Then I should be able to build for watchOS the scheme App | |||
Then the product 'App.app' with destination 'Debug-iphoneos' contains resource 'Watch/WatchApp.app' | |||
Then the product 'WatchApp.app' with destination 'Debug-watchos' contains extension 'WatchAppExtension' | |||
|
|||
Scenario: The project is an iOS application with Xcframeworks (ios_app_with_xcframeworks) |
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.
Nitpick: Xcframeworks -> xcframeworks
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.
Hey @lakpa! Just wanted to say you did an amazing job here 👏. I'm very excited about seeing xcframeworks' support landing in the project.
@kwridan , wondering if I could include the static lib fixture in next PR as follow up work is needed to support static libs, which hasn't accounted in this PR in regards to |
5f6241a
to
8d5344a
Compare
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.
awesome, thanks for all the updates @lakpa
@@ -482,4 +515,19 @@ final class LinkGeneratorErrorTests: XCTestCase { | |||
|
|||
return projectFileElements | |||
} | |||
|
|||
private func createFileElements(fileAbsolutePath: AbsolutePath, |
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.
awesome! thanks for making this helper 👍
One last suggestion to simplify it even further, we can prime the ProjectFileElements
directly with the fileElement.
private func createFileElements(fileAbsolutePath: AbsolutePath) -> ProjectFileElements {
let fileElements = ProjectFileElements()
fileElements.elements[fileAbsolutePath] = PBXFileReference(path: fileAbsolutePath.basename)
return fileElements
}
Something like this makes it an even more re-usable helper here as it not specific to the Test.xcframework
XCTAssertEqual(buildFiles.map { $0.settings as? [String: [String]] }, [ | ||
["ATTRIBUTES": ["CodeSignOnCopy"]] | ||
]) | ||
} |
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.
👍
5778923
to
44dca72
Compare
Short description 📝
This PR introduces a new dependency type to support
xcframework
.Solution 📦
A graph node to represent xcframework dependency called
XCFrameworkNode
is introduced which inherits fromPrecompiledNode
, theXCframeworkNode
can leverage existing lookup however the only exception we need to consider is duringembedding phase
when we need to specifically look forxcframework
file type to skip getting the xcframework being picked up asprecompiled
to skipembed script build phase
and embed the respective file reference with requiredCodeSignOnCopy
attribute instead.