How to make my code discover an embedded utility? - xcode

Continuing with this question.
I have a command line utility embedded in a framework. That utility is used by third parties to handle some processing. There is no way I can not use that utility.
My problem is, I don't know how to locate my utility on disk. It's bundled in a framework within my main app, but I am not sure how to get the right path.
Halp?

Provided you've set a proper bundle identifier (CFBundleIdentifier) entry for the Info.plist file of the .framework, you can use NSBundle's bundleWithIdentifier: method to obtain the bundle representing the framework, then use pathForResource:ofType: to get the path to the executable. Just make sure you give the framework a unique bundle identifier (different from your main app bundle's bundle identifier).
For example, say I have an app bundle CoolApp.app that has a CFBundleIdentifier of com.blah.CoolApp. It has a TextureKit.framework framework which is at CoolApp.app/Contents/Frameworks/TextureKit.framework, and has a CFBundleIdentifier of com.blah.TextureKit.framework. Inside the Resources folder of the TextureKit.framework is an executable named textureUtility. I could use the following code to get the full path to textureUtility:
NSString *path = [[NSBundle bundleWithIdentifier:#"com.blah.TextureKit.framework"]
pathForResource:#"textureUtility"
ofType:#""];

Related

Creating symlinks in OSX frameworks inside app bundle

I need to create symlinks in frameworks inside an app on OSX (vers 10.10.1). I am outside of XCode as the app is developed in Qt. It provides the frameworks, but they need some retouches to work on the new codesigning rules.
I call "ln -s path_origin path_target_link" and apparently it works, the links are created and they resolve to the right place.
But when I codesign the app, I get an error "unsealed contents present in the root directory of an embedded framework"
(In order to verify my procedure, I copied a framework from another package downloaded that validates codesigning and I could sign it successfully, when I create a symlink my way codesign starts to give the error so I'm assuming the problem is my symlink creation)
I tried other methods with the same result so I must be missing something. Any ideas?
For the case it's useful to anyone. In my case the problem was that I was creating the links with absoulte paths. Obviously they should be relative: You move to the directory where the link should go and create the link relative to the current location.
Version 1 code sign recorded only files in the Resources directory and ignored the rest. But version 2 records substantially all files by default. So we have to sign each and every frameworks bundle's versions and app binaries.
Ensure your framework bundle structure is meet the requirement of apple.
According to Apple's documentation, framework bundles don't have a 'Contents' folder. Instead, each version folder gets a 'Resources' folder which contains the Info.plist file, and which is also symbolic linked at the top-level framework folder. Qt5 frameworks have incorrect layout after SDK build, so this isn't just a problem with macdeployqt, but whole framework assembly part.
so confirm, there is no faulty layout in your app bundle.
If you have a *.prl in your Qt framework bundle (i.e.QtCore.framework/QtCore.prl). *.prl file may cause trouble for you as codesign just bails out with "unsealed content presents in the root directory of an embedded framework" without telling exactly what's going on. Strip if you have any prl files.
It is necessary for the Info.plist to have the correct CFBundleExecutable field. At the moment it doesn't. The debug version of Info.plist would overwrite the release version, and it also happens to contain invalid data. In particular, CFBundleExecutable would contain the _debug suffixed library name, which it shouldn't. If you have this problem then modify the incorrect info.plist.
The last step is you have to codesign all the frameworks before you sign the entire app bundle.
For anyone confused about this issue, you can refer to Apple's documentation https://developer.apple.com/documentation/bundleresources/placing_content_in_a_bundle
It contains all the details for macOS/iOS/MacCatalyst/watchOS/tvOS...

Can I put info about multiple executables inside the same bundle in Info.plist?

I have a Qt app that uses Assistant to display help.
On Mac, I am packaging he Assistant inside the bundle. The only way I can include all its libraries is by placing the Assistant executable inside the same MacOS folder as the app executable, and properly link all the library dependencies.
Is there a way to place information about both executables in the Info.plist ?
No, you can't put info about multiple executables inside the same Info.plist. (Well, you an always puts custom keys into the Info.plist and store whatever property list data you like there, but the system won't pay any attention to those keys.)
Why not bundle the Assistant into its own bundle and put that bundle inside the main app's bundle? The Assistant bundle would have its own Info.plist file. Also, if you create a question about whatever linking or dynamic loading problems made you think you had to put it all into the main bundle, you might find there's a better solution.
placing the Assistant executable inside the same MacOS folder
I recommend not to do this. The Assistant is a resource to the main application and so it should reside in the resources folder. If you want to launch the Assistant app from the main app, you can then locate it by name.
You can only define one application in the Info.plist. If you were to add more, there would be a conflict in keys.
For example, CFBundleIdentifier is a unique URI that names the bundle (e.g. com.apple.calculator). The OS uses the URI to register the application with the OS when an application is, for example, copied to the /Applications folder. The OS expects the key to be a child of the root dictionary and its value must be unique. If there were multiple keys named CFBundleIdentifier, it would not know which is valid.
Although you can throw almost any junk into a Mac application bundle, much good will not come to you.
If I understand right, you have both a Mac Application (bundled normally) and a side-application you call the "Assistant" you want embedded in the same application bundle.
You also mention libraries (.dylib's I guess) that must reside in the same directory as the assistant.
Now - if these libraries are only used by the Assistant side-application, I would recommend that you bundle the assistant as a Code-bundle (Apple provides lots of information about these, and you have easy to use templates from Xcode). You can then use Xcode to copy it into the right place within the main application's bundle (I'd choose "Plugins") and use NSBundle APIs to launch it.
However, if those .dylibs are shared between the main app and the assistant - then I'd say go ahead, stick your assistant, .dylibs and main app's binary files in the same "MacOS-X" directory, and use posix APIs, or shell command to launch the assistant. Of course it will share (if possible) every resource of the main application, because they are located at the same place. However, the main app's bundle can only have ONE CFBundleExecutable entry, and that should point to your main application's binary.

Installing two bundles with the same file

I am using OSGI declarative Services.
In my java application, I launch Apache Felix, and install and start some bundles.
There are two bundles installed using the same file path. Once the bundle starts, it is supposed to display a 'Hello' message.
When I install and start the first bundle:
Bundle bundle1 = context.installBundle("file:C://Users//bundles//myBundle.jar");
bundle1.start();
This shows 'Hello' in the console.
However, when I install the second bundle (with the same file path as the first bundle)
Bundle bundle2= context.installBundle("file:C://Users//bundles//myBundle.jar");
bundle2.start();
I don't see any output. It means that the installation and/or the starting of the second bundle was ignored.
I need a way to install and start two different bundles with the same file path, and when I stop one of these bundles, the other should stay ACTIVE. How can I possibly achieve that? Thanks.
The use case I am thinking of: Let's say I have two users, and both would like to use the same feature (bundle). What if one of them decided to stop the bundle feature, and the other one would like to keep it?
Is there a better way to achieve that? Thanks.
Does the file content change in between installing two times? If not, then you cannot do this. Essentially you're trying to instantiate the bundle twice, and OSGi only allows each bundle to be installed once.
Actually you can have multiple versions of a bundle at the same time, but the pair of Bundle-SymbolicName and Bundle-Version must be unique within the framework.
Mapping user functionality to installed bundles is really not a good idea. Why should users care about the modules installed in your application?? Instead you want to make the functionality in the bundle support multiple users.
You can call the function
context.installBundle(location, inputStream)
and pass an InputStream to that function. In that case, you can specify two different locations (e.g. one that has some meaning but not a real location).
Your next problem will be if two bundles with the same SymbolicName and same version can cannot exist twice.
I cannot imagine a use-case where the same bundle should be installed twice. It might be the result of a bad concept.
There are two things you need to consider: The bundle location must be unique and the framework must be set to accept multiple bundles with the same bundle symbolic name. You should use bundleContext.install(location, inputStream)
to install the bundle. Maybe like this:
byte[] byteArray byteArray = IOUtils.toByteArray(new FileInputStream(new File(filePath);
Bundle bundle = bc.installBundle(jobID, new ByteArrayInputStream(byteArray));
And to enable the framework to accept multiple bundles with the same bundle symbolic name, you need to start the framework with the following option:
-Dorg.osgi.framework.bsnversion=multiple
Notice that the update command will still try to update the bundle from the given location, which in my case (jobID) was not a real file path. This was irrelevant for my use case, so I never bothered to solve it.

copy a folder in resource

I am developing an application in cocoa which needs to copy a folder added to resource to other location in System/library .How can i specify the source and destination path.Looking for a solution
Thanks in advance
I am developing an application in cocoa which needs to copy a folder added to resource to other location in System/library .
It sounds like you're writing an installer. Use PackageMaker; don't write your own custom installer. There are a thousand edge cases that Installer already handles and your custom installer will not.
Do you really need to install this file in /System? The only kind of thing that absolutely needs to be there is a driver. If what you're installing is anything else, the answer is no, and you should not install it to /System.
You'd use NSFileManager which has really nice convenient methods. There is method like
- (BOOL)copyItemAtPath:(NSString *)srcPath toPath:(NSString *)dstPath error:(NSError **)error
And here's the description of it from apple docs:
Copies the directory or file specified in a given path to a different location in the file system identified by another path.

Two executables in one bundle on MAC

Is it possible to have two executables each with its own plist to share the same bundle. Then depends on the way app is executed (parameters) to load the appropriate executable.
Imagine the case where we have a main application (executable with UI) and the mini application (shorter version of the main app also with its own UI) and then depend on the parameters user used for starting the application execute the appropriate executable in the same bundle.
Cheers
Not exactly, but you could achieve something similar.
You could have a master Application bundle, which figures out which version of the code to run, and then have multiple plug-in bundles (as resources of the application) which actually implement the different versions. Each plug-in bundle would have its own Info.plist / nib files / etc.
See the documentation for NSBundle for details of how to load bundles and run their code.
I think I understand you. You want to share a plist between two executables. Just refer to the same plist in each case: tutorial for single executable plist.
Don't know if it's possible. Honestly I doubt it because the plist information is also used to define the icon and so on, so you would confuse the Finder if this would be possible (which icon should it display?). However, I am not an expert in Bundles, at all.
I give you a workaround. Create a demultiplexing script that runs the proper executable according to your parameters, and then associate the script with the plist information.

Resources