Notarization successful but spctl verification fails on Qt frameworks

We are currently trying to have our app verified in order to distribute it outside of the app store. We are including OpenSceneGraph libraries as well as Qt frameworks in the app bundle.


This is how we did it so far:


- Signed executable in Contents/MacOS folder

- Signed libraries and Qt frameworks

- Signed App.app folder

- zipped .app and submitted for Notarization


The executable, libraries and frameworks signing is done manually with the codesign command, and to sign the whole .app we do the following:

codesign --force --verify --verbose=3 --options runtime --timestamp --entitlements App.entitlements -s "Developer ID Application: Our Dev Id" App.app


When we send the zipped .app to be notarized we usually get a quick reply informing us that the notarization was successful, but if we try to run "spctl --verbose --assess --type execute -v App.app" we get the following error:

App.app: rejected (unsealed contents present in the root directory of an embedded framework)


Also inspecting the json file with the notarization output we notice the same error, but it is marked as a warning and checking it with codesign no error is returned.


After a bit of digging we realized that the issue is related to the Qt frameworks: as a counterproof, we tried to submit the same app without the Qt frameworks and this time when the bundle was successfully notarized spctl accepted it too, so we eliminated the all symlinks in the root directory, moved the .prl files into the Resources/ folder, and created an alias to A/ in the Versions/ subfolder as suggested in several forum posts, but we have not been able to have spctl accept our bundle with the Qt frameworks. Now at the root of each framework there is just the Versions folder and nothing else (we checked with ls-lha to be sure)


What are we missing in this? Is there a way to at least get some hint on where is the unsealed content which is upsetting the verification tool?


Thank you in advance

Replies

So what is it about the Qt framework that is different than other frameworks? This is a problem with open source projects. You are the one responsible for support. If someone else comes along who has successfully notarized Qt, then they might be able to help. But otherwise, you are on your own.


I can tell you that, in general, it is possible to cobble together a framework and have it successfully notarized. I have an app I'm working on that includes the GDAL framework. This is an open-source package like Qt, but far larger and more complex I'm sure. While GDAL has a Mac framework target, I don't trust open source developers for such things. I cobble together my own framework. I just tested it with one of my proof-of-concept apps and it notarizies fine. I tested it both within Xcode and from the command line using your command above. It worked fine.


What does this mean? Well, for one thing, my app lives in Xcode. That means I can notarize it from Xcode. I don't need to notarize from the command line. I'm not sure if I'm even able to exactly reproduce what you are doing because of this. I'm not even sure how I would get an unsigned app out of Xcode. I did use your command to resign it, but it was already properly signed, just not notarized.


I do have a crazy, Perl XML build system for GDAL that runs on the command line. I rent a souped-up, multi-core Mac just so I can bulid it in a reasonable time. But the output of this process is a valid, signed framework.


My guess is that your Qt framework is simply invalid. Many open-source developers are clueless about Macs. In many cases, they don't even have Macs and are building this thing on Linux or Windows. Maybe it worked in 2009, but you might be the first person to try to notarize a Qt-based app. Everyone else using Qt might be posting questions on these forums asking about specific edge cases for the last date they can get notarized under Apple's special extension and whether or not that download will still be notarized later. Hopefully you aren't doing that too.


All you need to do is look at a valid framework. Any one will do. Build your own Qt framework that looks just like that. Make sure your Info.plist file is valid. Be very careful with the Qt dynamic library itself. It may reference other dynamic libraries. That can be very messy if you try to have multi-layer dynamic frameworks. You are essentially building an umbrella framework in that case, and doing it wrong. Apple doesn't even recommend attempting to build a correct umbrella framework. This can be done, but it is very delicate. You may have manually fix up the paths via install_name_tool. One of your minor dependencies may have been build with CMake or something and injects an @rpath into the mix. Good luck with that.


I am concerned about how you are building this thing to begin with. You said you are signing the "executable", and then libraries and Qt frameworks, and then the .app folder. The only thing you need to sign is the .app folder. That is the "executable" on a Mac. Don't try to get clever inside the Contents/MacOS folder. That never ends well.


I am also concerned about how you describe the Qt framework. You said you eliminated all the symlinks, which would be bad. And then you moved the .prl files into the Resources folder. Well, you just said you deleted the Resources folder. And what are .prl files? Are they something that Qt needs? If so, it won't be able to find them after you move them. You will have to do something inside of Qt to specify where they are. They aren't a Mac thing. I'm not sure what you mean about creating an "alias" to A. You specifically said symlink before. Are you really creating an alias? That would be bad too. Frameworks are full of true symlinks, no Finder aliases. Look for any other app that has embedded frameworks. Find a framework and copy that. If Qt has ancillary files in unusual locations, you may be able to keep them there. You can have "extras" in a framework without too much problem. But this is where you could get into signing problems. But the problems would be more likely to be at runtime, not at Notarization time.

Thank you for your reply. We could not go with the "within XCode" option and this is why we are trying to figure out how to do without it.


RE: Everyone else using Qt might be posting questions on these forums asking about specific edge cases for the last date they can get notarized under Apple's special extension and whether or not that download will still be notarized later. Hopefully you aren't doing that too.

Of course I am not, that would have saved me 2 days of banging my head against a wall. But no, I might have to release a new version in the future so I want to understand how to do it the proper way. And yes, I have a Mac and I am building it from there: the reason why we need Qt is that the application we're working on is a cross-platform one and before all this notarization stuff happened it looked like the best option for us.


Yes, I have looked into a "valid" framework, and the only difference I saw was the structure of the Versions/ folder: in the "valid" case, it had the A/ folder and a Current/ symlink pointing to it, while in my case instead of Versions/A there is Versions/5, and this is a known issue with Qt Frameworks. I haven't very clear the difference between symlink and alias, what I can tell you is that I created them with the "ln -s" command. Anyway, the files I said I have removed are not really removed, but I moved them to the Versions/A/Resources/ folder


RE: The only thing you need to sign is the .app folder

And what about the plugins, dylibs and framework? My understanding was that they needed to be signed as well, and that it was not too much of a good idea to use the --deep option for codesign


Thanks

We could not go with the "within XCode" option and this is why we are trying to figure out how to do without it.


Are you sure about that? I'm not saying you need to rewrite your app in SwiftUI or anything. But you can have a mostly empty app project with a "main" function that does nothing but call out to whatever cross-platform frameworks you are using. Xcode will make a proper application bundle and give you lots of additional features, like notarization, for free. You can even do your building on the command line with xcodebuild. Some of my dependencies are in CMake and crazy Google-written build systems. It is relatively easy to re-do those in an Xcode project, which makes lots of problems just go away.


the reason why we need Qt is that the application we're working on is a cross-platform one and before all this notarization stuff happened it looked like the best option for us.


Appearances can be deceiving, but I'm not going to belabour that point. But I will tell you that the Mac platform is changing. If you want to avoid problems in the future, ask yourself one question - "what do I need to do to port this to iOS?" If Qt solves that problem, go for it. If not, then you will have problems in the future. [Note: apparently Qt does have an iOS version. I'm just joking here. Anyone building a new Mac app in 2020 needs to build a native iOS app instead and port that to the Mac - end of discussion.]


Yes, I have looked into a "valid" framework, and the only difference I saw was the structure of the Versions/ folder: in the "valid" case, it had the A/ folder and a Current/ symlink pointing to it, while in my case instead of Versions/A there is Versions/5, and this is a known issue with Qt Frameworks. I haven't very clear the difference between symlink and alias, what I can tell you is that I created them with the "ln -s" command. Anyway, the files I said I have removed are not really removed, but I moved them to the Versions/A/Resources/ folder


In theory, you can have multiple versions of framework. In practice, nobody does that. Apple isn't using versions for frameworks on iOS (hint, hint). What "valid" case are you looking at?


I didn't think that you were using a Finder alias, but these things have to be absolutely precise. Yeah, I'm being annoyingly pedantic. But I'm being far less annoyingly pedantic than Apple's servers or Gatekeeper framework is going to be. The structure of a framework is documented here. One of the first things it mentions is versioning. Maybe, if you do it perfectly, you could get that to work. But I suggest starting with just version "A" and getting that to work first. You can build a framework entirely from scratch with a shell script.


I am attempting to download Qt to look at that. That is proving to be a challenge. Not a good sign.


And what about the plugins, dylibs and framework? My understanding was that they needed to be signed as well, and that it was not too much of a good idea to use the --deep option for codesign


There is no "as well". The app bundle is "the executable". You sign the app bundle and that's it. If it doesn't work (through the entire notarization flow), then your app bundle is invalid. If you build things using Apple's tools and with Apple's technologies, then there is nothing to worry about. Everything is pretty straightforward and will be identical to every other app. But if you try to do something funky, then you have to be careful. Open source projects frequently do funky things and frequently have problems. Qt seems to be unique. Is it open source or proprietary?


OK. I did get some of Qt installed, not the full 200 GB worth, but enough to see the frameworks. Aside from the "5", they look OK. But there are 59 of them. And Qt doesn't seem to actually build stand-alone bundles. It is relatively easy to embed a framework from within Xcode, and Xcode will do all of the work necessary to make it work. If you don't use Xcode, you'll have to do all of that yourself. While I'm certainly no fan of Qt, and even less so after having seen it, it doesn't appear to be doing anything wrong. It definitely isn't doing enough to make a deployable app bundle for you. It seems like they are leaving all the hard work up to you. Those .prl files look like some kind of Qt version of pkg_config or something. I doubt you need them at runtime.


It sounds like all you have done is corrupt the frameworks. I'm not sure what you are doing, but I think that your overall build procedure is not valid.

First up, I’m going to recommend that you read through my Signing a Mac Product For Distribution post. It’s chock full of advice on situations like this.

Having said that, I’m not sure what’s causing your unsealed contents present in the root directory of an embedded framework error. Well, I know what the error means, but it’s not clear what’s triggering it. I’ve worked with a lot of Qt developers with notarisation problems, and I’ve not see this one before.

If you get completely stuck you should open a DTS tech support incident and I can look at your case in more depth. DTS doesn’t support third-party tools or libraries, but I should be able to determine what specifically is triggering this problem.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

I am sure there is something wrong with my Qt Frameworks structure, and I am attempting to explain you why I am convinced of that. After I replied to you I tried to structure the Qt frameworks I am including in the bundle as any other working framework:

QtFramework.framework

Versions

A

Current (symlink pointing to A)


I signed the app folder, zipped it and sent it for notarization, and when it came back I inspected the json result and there were no issues in it (neither errors nor warnings).


Unfortunately, I still can't execute it because removing the Versions/5 folder inside the framework invalidates some entry in the frameworks @rpath path. I verified that if I run

otool -L QtFramework.framework/Versions/A/QtFramework

the first entry I get is

@rpath/QtFramework.framework/Versions/5/QtFramework

and I have not been able to correct it with install_name_tool. If there are other Qt frameworks found with the otool command instead, they are correctly referenced with "@rpath/QtOtherFramework/Versions/A/QtOtherFramework".


So yes, I think I get what you say about building a framework from scratch and I was going for it but I just am missing something in the process because I haven't been able to correct all the needed runpaths so far. And I am quite convinced that this is more on the Qt people than it is on you (after all, frameworks are used only on Apple, why Qt people structured them differently from standard goes far beyond my understanding), but you were the ones who replied me first.


Once again, I don't think this app is ever going to be ported to iOS: it is a (desktop) cross platform tool used for research purposes by people who don't need to have any specific computer backgound, they ideally just need to double-click and run it (whether they are on Mac, Windows on Linux), and before this Gatekeeper and Notarization enforcement we were able to successfully build and execute the bundle.

And I am not saying this enforcement is wrong, I can see how safe it is from a user point of view.


RE: There is no "as well". The app bundle is "the executable".

Fine, I trust you on that, but there are (too) many other posts saying otherwise (including https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html#//apple_ref/doc/uid/TP40005929-CH4-SW5), and since as you can see I am not an expert it can be misleading.


And once again, thanks because at least you replied

Thank you for the link, I somehow managed to miss it while looking for answers.


As I told your colleague, I tried to structure the Qt frameworks "the Mac way" and this time there were no notarization issues, but I could not open the executable because in some frameworks runpaths there was some reference to /Versions/5 folder which I was not able to replace with install_name_tool. Curiously enough, as soon as I tried to add a Versions/5 symlink I was not able to sign the bundle (unsealed content error), and I hope the link you provided will shed some light on this issue.


I get that what we're trying to do is not really standard, and there might be something else going on, and this is why I would have hoped/expected to receive more hints about the error and what is causing it

As I said above, I think the Qt frameworks are fine. That 5 is unusual, but I don't think it is causing any problem. The problem stems from the fact that the Qt environment does not properly embed those frameworks and bundle the app for you.


(Speaking of which, do you have a license for Qt? If you are using the GPL license, then your app may be GPL now too. I didn't look into this. Like Apple, I generally just avoid all versions of GPL.)


I really think you should take another look at Xcode. Even if you don't want to use it, you can see how it creates a demo app bundle. Add a minimal number of Qt frameworks and embed them. Make sure your demo app runs on a pristine system. Then you can review everything Xcode did in the build log.
I guess it is all about what you want to accomplish. Do you want to become an expert on Apple executable bundles and code signing? Or would you rather have some Apple tool do for you what Apple needs to be done, the way Apple wants it done?

As I told your colleague

Just to be clear, despite their prolific contributions to DevForums, john daniel is not my colleague. You can identify Apple employees here on DevForums by the Apple Staff below their avatar.

I could not open the executable because in some frameworks runpaths there was some reference to

Versions/5
folder

The system should be able to handle a wacky framework version without the benefit of a runpath entry [1]. And even if you do use a runpath entry, that should be fine as long as Gatekeeper can resolve it to a location within your app’s bundle.

What’s weird here is that none of the other Qt-based app developers I’ve helped have hit this specific issue. Unfortunately, I don’t have a lot of experience with Qt, so I can’t offer any insight into what’s triggering this in your specific case.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] Not that I want to encourage this. Framework versions are, IMO, vestigial cruft that has no place in a modern app. I’ve also seen cases where wacky framework versions actively cause problems.

Ok, we managed to solve the issue. I describe briefly the solution here in case it might help other people which have to go "the hard way" in saving a week of trials and investigation.


There was actually something in how the Qt Frameworks were copied inside the .app folder and we did not figure out exactly why it failed doing it by hand (we tried both with finder and calling 'cp -R'), but the issue was solved when we upgraded Qt to 5.14.1 and copied the frameworks to the bundle using 'macdeployqt' command.


We submitted the app on Monday and notarization succeeded with no issues.


Thanks for your help