So despite my best attempts, I’m back in macOS-land, and confused as always!
The actual problem is very clear. I’m attempting to use std::filesystem::rename to move a file from my userBase() to Users/[me]/Library/Audio/Plug-Ins/Components/, but am seeing the filesystem::error Operation not permitted. This isn’t too shocking, because I know that I don’t have permission to write to this folder on macOS. Would be cool if I did.
So if anybody has any suggestions, that would be awesome. I’m on JUCE 7.0.10, targeting macOS 14.0, C++17, so hopefully there’s a clean way of doing this! Thank you.
Small update: I spent some time trying various things, but to no avail. The methods I tried were:
std::filesystem::permissions(newPath, std::filesystem::perms::owner_all | std::filesystem::perms::group_all), which returned Operation not permitted.
File::getSpecialLocation(File::SpecialLocationType::userApplicationDataDirectory).getChildFile("Audio").getChildFile("Plug-Ins").getChildFile("Components").create() to try to get an empty file, but nothing happened
STPrivilegedTask, which just resulted in a lot of errors, so I never successfully built, and even if I did, I’m not sure it’s safe to be calling external scripts to do anything.
This did get me thinking, how does the JUCE Copy Plugin step work? I see that it makes an alias in the Plug-Ins/Component folder, so I’m curious how the post-build step is able to do that, maybe that’ll be useful knowledge.
Regardless, in a bit of a pickle, so could use some help! Thank you.
Is there any reason you can’t use juce::File:moveto() instead of std::filesystem?
There are additional hints on this method about some of the other issues you may run into… be aware that you cannot move a directory, only files - so you may need to re-think the method by which you are creating the destination folder in ~/Library/Audio/Plug-Ins/Components/YourComponentFolder also … such as using juce::File:copyDirectoryTo() and then deleting the source directory, eventually, etc.
Note also that juce::File also has tons of solutions for making symlinks work, Shortcuts, etc. You may find it a bit more versatile for your needs. Also, I think juce::File brokers the permissions issues for you, for example by asking the user for permission to write to folders such as the above …
I forgot to mention that I tried all of the juce::File techniques, which resulted in different things:
juce::File::moveto(): I couldn’t find a function called this, but there is:
juce::File::moveFileTo(): This one breaks down at if (isNonEmptyDirectory()) return false;, but I’m unsure why it thinks a .component file is a directory.
juce::File::copyFileTo(): Returns out of juce_File_mac with nothing (ln. 32).
juce::File::createSymbolicLink(): Probably the most favorable, but it returns -1 to fail at ln. 982 of juce_File (the file definitely does not exist):
// one common reason for getting an error here is that the file already exists
**if** (symlink (nativePathOfTarget.toRawUTF8(), linkFileToCreate.getFullPathName().toRawUTF8()) == -1)
juce::File::copyDirectoryTo(): Just to check that .component is a directory (for some reason). Can’t supply it the .component name anymore in the newPath, so now I just have to cut it off at Plug-Ins/Components/. Fails during the for-loop to find a child file (ln. 334 of juce_File):
for (auto& f : findChildFiles (File::findDirectories, false))
if (! f.copyDirectoryTo (newDirectory.getChildFile (f.getFileName())))
return false;
I really like the idea of the symbolic link (which, if I understand correctly, is effectively a Shortcut?), since that’s what JUCE does, but the question is how? Good to know that juce::File brokers permissions for me. What I would expect is a little prompt showing up to ask if I want to copy to Plug-Ins/Components, but I never got a prompt like that, so I’m unsure what to say.
Just for sanity’s sake, the paths I wrote out to check all of the juce::File functions were:
Current File: /Users/[me]/Library/Audio/Presets/XYZ/Artivox.component
New File: /Users/[me]/Library/Audio/Plug-Ins/Components/Artivox.component
Also, when checking the permissions of my Components folder, it returns unknown. I also checked the Components folder to see what it says:
To be clear .componentis (or at least should be) a directory. Yes technically it’s a bundle, but a bundle is really just a directory/folder with a particular structure. You can right click the bundle and select “See Package Contents” to see for yourself. See here for more details on bundle structures (although you don’t need to know all of this information).
If you’re seeing an alias then you’re probably on a slightly out of date version of JUCE, it should make a copy again now. Don’t try and create a symbolic link, Logic won’t play well with it.
There isn’t much magic in copying a .component. Xcode has permission to access anything the user has access to. However, it’s completely possible it’s a little more involved when trying to do it from an application. If your application is sandboxed then you might need to request access to the location(s) you’re trying to access.
One other issue would be if the permissions are messed up at all on the Components directory, any of its parent directories, or the plugin itself or any of its parent directories.
Is there a reason you want an application to do this? if it’s during development I would suggest using a script. If it’s for an installer I would recommend either a DMG with an alias to the Componets directory so users can drag the plugin(s) in, or create a pkg installer.
Where you can download, update, uninstall the program from this GUI, and it will manage putting the .app/.components in the right spot.
It’s definitely a sandbox issue, just took that off to be sure and it works fine without it. Now that I think about it, there really is no need for this application to be sandboxed, just the products themselves. But just in case I do need this application sandboxed, would you happen to know how to request access to the Plug-In/Components folder? Was looking for a function like that to prompt the user, but never found one.
I don’t know off the top of my head, you probably want to look here for more details.
Generally if you have multiple things that need installing a pkg is by the far the most standard route, a couple of reasons to consider doing this.
Imagine a university or media broadcaster needs to install 100’s of copies, you can supply them with the pkg and they will have the tools to easily install it on their machines.
Your custom installer is almost definitely not as accessible as the built in alternatives, both in that you’ll need to make your installer accessible, but also familiarity, the vast majority of plugins are installed via a DMG or PKG installer.
IME Apple really doesn’t want manufacturers making custom installers. Although I respect there are nice features that can be delivered that way.
That being said if there is an Application involved and you only want to install an AU then AUv3 could be an option as Installing your app and running it would install the .appex, this way users just install your app (by dragging it into the Applications directory, probably via a DMG) then run your app - that’s it.
The downside is that only works for AUv3.
You could also look a hosting, downloading, installing the PKG from your custom installer so you can still deliver updates via your application?