Skip to content
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

SPM with custom module map #460

Merged
merged 5 commits into from
May 11, 2021
Merged

SPM with custom module map #460

merged 5 commits into from
May 11, 2021

Conversation

paulb777
Copy link
Contributor

Alternative to #442. Fix #375

Add Swift Package Manager support via a custom module map
Adds tests to verify import syntax
Add a GitHub Action workflow to run the tests on PRs and pushes
Adds headers to the module map to support existing client usage. See SwiftPMTests/objc/objc-header.m.

I couldn't find another way to support client usage other than module syntax without adding the extra headers.
Without them would require source changes for existing clients as well as being problematic for testing libraries (versus frameworks) and Objective C++ that doesn't support module imports.

Any suggestions about a better way?

@paulb777 paulb777 mentioned this pull request Aug 22, 2020
@erikdoe
Copy link
Owner

erikdoe commented Aug 23, 2020

Maybe I misunderstand something, but weren't the modulemaps an approach to avoid the directory full of symlinks?

Also, OCMock uses Travis and not Githib Actions as CI system. In any case, given that the test is adding yet more complexity, I am happy not to have a test for the SPM file. There are no tests for CocoaPods and Carthage either.

@paulb777
Copy link
Contributor Author

One reason I added the tests is to make it clear the advantage of the extra directories. The extra directories make it easier for existing OCMock clients to migrate, since they can switch from CocoaPods to SPM without source changes. If there's only a module map, all clients will need to change their code to use the module import @import OCMock; to migrate.

So three questions:

  • Should file import support be deleted?
  • Should the tests move to travis?
  • Should the tests be deleted?

If the answer to all three is yes, the PR will be reduced to two files - the Package.swift and a module map.

@twitterkb
Copy link

just starting to catch up on the changes here w.r.t. SPM.

would like to see some solution that will allow us to start using OCMock as an SPM package …

i don't know which of the alternatives here is preferred at this point.

looking for a tl;dr on where the discussion on this is at and if there's a plan going forward.

@erikdoe
Copy link
Owner

erikdoe commented Aug 24, 2020

As I have commented previously in various PRs, I'm not happy about adding a directory full of symlinks just for Swift PM. This PR has the additional issue that it introduces a new CI system (Github actions in addition to Travis).

We are making some progress and the questions asked by @paulb777 help. I would say that the benefit of easing migration from CocoaPods or Carthage to Swift PM is not worth the directory full of symlinks. Unless I'm missing something, that's a very niche use case. I assume that most clients that still use OCMock are mature Objective-C codebases grown over the years. What would be the reason for them to switch? Also, if a team makes such a major change then changing from #import to @import doesn't seem too much of a stretch.

I've seen the PR you opened, @twitterkb, and I've seen your comment, @paulb777. If the straight quotes aren't needed why did people seem to tell me that they are? Also, can someone please look at how modulemaps work in detail and come up with a proposal for Swift PM that requires neither straight quotes nor directories with symlinks?

In the meantime I'm going to look at the PRs flagged for 3.8 and some of the other bugfixes. It feels like we're going round in circles on this Swift PM topic. And it seems like it's a lot of work for a niche use case for which a workaround seems to exist, right?

@twitterkb
Copy link

We are making some progress and the questions asked by @paulb777 help. I would say that the benefit of easing migration from CocoaPods or Carthage to Swift PM is not worth the directory full of symlinks. Unless I'm missing something, that's a very niche use case. I assume that most clients that still use OCMock are mature Objective-C codebases grown over the years. What would be the reason for them to switch? Also, if a team makes such a major change then changing from #import to @import doesn't seem too much of a stretch.

in our codebase, we do indeed have a large and mature Objective-C codebase grown over 10 years … and we have committed (internally) to switching to use SPM for all code by WWDC 21 . we feel that in terms of dependency management that stays close to the Apple process, and w.r.t. inter-operability between Objective-C and Swift, Swift Package Manager based solutions are the way to go.

I've seen the PR you opened, @twitterkb, and I've seen your comment, @paulb777. If the straight quotes aren't needed why did people seem to tell me that they are? Also, can someone please look at how modulemaps work in detail and come up with a proposal for Swift PM that requires neither straight quotes nor directories with symlinks?

i don't know why you were told "straight quotes are the way to go". the pull-request i submitted is based on Apple's recommendation in the Issue Navigator when you open an existing project in Xcode12 for the first time … and this will allow us to replace a ruby script that we have used to enforce this behavior in all of our code (with OCMock being in an exclude-list from the ruby script). our solution for exposing the headers via SPM is a module.modulemap . i would have to investigate exactly what that might be for OCMock .

(the other pull-request i submitted is really somewhat independent of Swift Package Manager … it is related to being able to import OCMock in any kind of downstream module in a way that fulfills the modular header contract.)

In the meantime I'm going to look at the PRs flagged for 3.8 and some of the other bugfixes. It feels like we're going round in circles on this Swift PM topic. And it seems like it's a lot of work for a niche use case for which a workaround seems to exist, right?

use of Swift PM is definitely not niche in our case. it will be the focus of work by one of our teams for the 9 months or so following Apple's fall events.

@paulb777
Copy link
Contributor Author

I just push an update that removes the extra headers. It might be a good compromise.

With this module map, it's still possible for clients to do non-module imports with #import <OCMock/{header.h}, but #import <{header.h} no longer works. See the diffs in the tests from the previous commit for details.

I still want to do more testing on our source base.

@@ -0,0 +1,29 @@
module OCMock {

Choose a reason for hiding this comment

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

in my experience, in many cases making this framework module OCMock is the canonical way of describing this to allow framework header or modular import. (though that is not what we did in the example i describe below of a legacy bit of code that we added module.modulemap support to …)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With framework module OCMock, I see:

$ swift test
/Users/paulbeusterien/gh/ocmock/Source/module.modulemap:2:10: error: header 'OCMock/OCMock.h' not found
  header "OCMock/OCMock.h"
         ^
/Users/paulbeusterien/gh/ocmock/SwiftPMTests/objc/objc-module.m:1:9: fatal error: could not build module 'OCMock'
@import OCMock;
 ~~~~~~~^~~~~~
2 errors generated.

Choose a reason for hiding this comment

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

in that case, the header should possibly simply be

header "OCMock.h"

… however, it may also be the case that if you follow the guide of the other example i provided, framework may not be necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changing to header "OCMock.h" leads to fatal error: 'OCMock.h' file not found

It seems functional to add the wrapper scope like in the example, but I don't see what value it provides. I don't see any different functionality.

Choose a reason for hiding this comment

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

the wrapper scope in the example allows for submodule import, should that be desired.

we do perform submodule import of items in the example. when performing @import OCMock in our current code, we cannot perform submodule import, so have never done so. i don't think it's necessary, since nothing is really relying on it, and since for most use cases, it is test code, and worrying about eyeball complexity based on which submodules you are importing probably isn't as important. i can live without the submodule wrapper separation.

i am surprised that the header "OCMock/OCMock.h" syntax is fully required. that has not been something we have encountered in creating module.modulemap files.

if this works for the OCMock tests and works in modules you ingesting OCMock in, this feels like an appropriate start.

Copy link
Owner

Choose a reason for hiding this comment

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

As I mentioned before I don't know Swift Package Manager. This exchange leaves me puzzled. Should it be framework, module, and/or header? Have you reached consensus?

Also, please bear in mind that I do not want to change the framework header. So, anything that requires double quotes or similar in OCMock's public header files is a non-starter.

@@ -0,0 +1,29 @@
module OCMock {
umbrella header "OCMock/OCMock.h"
exclude header "OCMock/NSInvocation+OCMAdditions.h"

Choose a reason for hiding this comment

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

rather than having to include every existing .h as well as having to change this modulemap (and thus, technically, a small aspect of the API) every time the internal structure of OCMock changes in a way that causes the addition or deletion of headers, i would make this module.modulemap simply refer specifically to each header to be included.

that way, only when public headers get added or removed will this file also change … and the compiler will pretty quickly enforce such changes in the first thing that attempts to import the module.

possibly reference:

https://github.com/twitter/ios-twitter-logging-service/blob/master/Resources/module.modulemap

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Commit coming that replaces the umbrella and excludes with a list of the public headers. Thanks!

@@ -0,0 +1,29 @@
module OCMock {

Choose a reason for hiding this comment

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

the wrapper scope in the example allows for submodule import, should that be desired.

we do perform submodule import of items in the example. when performing @import OCMock in our current code, we cannot perform submodule import, so have never done so. i don't think it's necessary, since nothing is really relying on it, and since for most use cases, it is test code, and worrying about eyeball complexity based on which submodules you are importing probably isn't as important. i can live without the submodule wrapper separation.

i am surprised that the header "OCMock/OCMock.h" syntax is fully required. that has not been something we have encountered in creating module.modulemap files.

if this works for the OCMock tests and works in modules you ingesting OCMock in, this feels like an appropriate start.

@paulb777
Copy link
Contributor Author

Successful test on the Firebase repo at firebase/firebase-ios-sdk#6326

@3a4oT
Copy link

3a4oT commented Oct 7, 2020

Can we merge this please?

@paulb777
Copy link
Contributor Author

paulb777 commented Nov 2, 2020

Here's an update on the unsafeOptions issue:

SwiftPM allows unsafeOptions for dependencies specified by a commit hash. They're not allowed for versioned dependencies.

Thus, apps and libraries can still use OCMock for their internal testing, while usage of the libraries without the tests can still be versioned. Example at https://github.com/firebase/firebase-ios-sdk/pull/6860/files

@mattsellars
Copy link

Nice work here!

Is there anything else left that is preventing this from merging this? We would like to consume OCMock via SPM as soon as possible.

@paulb777
Copy link
Contributor Author

Firebase is using a fork https://github.com/firebase/firebase-ios-sdk/blob/master/Package.swift#L142. We won't promise to support this in the long term, but no plans to stop in the near term.

steps:
- uses: actions/checkout@v2
- name: Xcode 12
run: sudo xcode-select -s /Applications/Xcode_12_beta.app/Contents/Developer

Choose a reason for hiding this comment

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

this no longer seems necessary …

Choose a reason for hiding this comment

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

@paulb777 , i don't know if adding spm.yml testing really is a complete non-starter. the only part that i thought was "unnecessary" was changing xcode-select to use the -beta .

fwiw, we are at the place where we need to move to SPM for OCMock for some changes we are making this week, and may be interested in going with your fork as a starting point, especially if it just works out of the box for us.

Copy link
Owner

Choose a reason for hiding this comment

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

As I mentioned early on in this discussion (and repeated earlier today), OCMock uses Travis CI for continuous integration. Please do not add another CI system.

Copy link

@twitterkb twitterkb left a comment

Choose a reason for hiding this comment

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

ping … would like to see if the community can agree on a canonical Package.swift file and move foward with this on master.

Comment on lines 1 to 15
name: spm

on:
push:
pull_request:

jobs:
swift-build-run:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: swift build
- name: Run
run: swift test
Copy link
Owner

Choose a reason for hiding this comment

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

As I mentioned before OCMock uses Travis CI. Please do not add another CI system.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Does this PR become mergeable by moving the CI to Travis or are additional changes needed?

Copy link
Owner

Choose a reason for hiding this comment

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

As I wrote last year, I'm okay with not having any tests for this. So, there isn't even a need to move this to Travis.

Regarding the status of the PR, there's still the open question around what should go into the .modulemap file. The discussion seemed to have petered out, which is why I asked for clarification earlier today.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If it can be merged, my preference would be to add the tests, to make it easier to maintain going forward. I'm fine to do that migration if you're ok with SPM tests in .travis.yml. The GHA tests run in a minute or two, so it should be minimal impact.

On the .modulemap discussion, since the current state has worked for us and others using the fork, my recommendation would be to keep it as is and address any shortfalls in another PR.

Copy link
Owner

Choose a reason for hiding this comment

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

Okay, that means the .modulemap is good to go. If you want to add a test on CI, please don't add it directly to the Travis file. That only invokes make ci. So, the actual test should go into the makefile.

@paulb777
Copy link
Contributor Author

paulb777 commented May 6, 2021

I rebased, did a small update, migrated CI to travis, and tested with Firebase in firebase/firebase-ios-sdk#8042

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.

Add Swift Package Manager support
5 participants