How to build a scripting language runtime into a macOS app?

The Xcode 11 release notes (and I think the Catalina release notes) say


"Scripting language runtimes such as Python, Ruby, and Perl are included in macOS for compatibility with legacy software. In future versions of macOS, scripting language runtimes won’t be available by default, and may require you to install an additional package. If your software depends on scripting languages, it’s recommended that you bundle the runtime within the app."


It would be helpful if someone could explain exactly how to do that. (i'm particularly interested in Python.) I'd also need to know how to test that it's really working, when testing on a macOS version that does have scripting languages built in.

Replies

That is an open-ended question. It is a fairly complicated task so it isn't much different than asking "how to program".


One suggestion would be to use a language designed to be embedded, like lua. There might be some similar wrappers around Python.


A more straightforward way is to just build Python from scratch, with the "PREFIX" value being a path inside your project. You are essentially creaing your own "/usr" directory. You may need some dependencies. I think I needed OpenSSL for Python, but then it turned out that I needed OpenSSL for other things too.


Once your tool is built, you can put your custom "/usr/bin" directory at the front of your path and test it. You may need other environment variables set as well, depending on the project. Once you are certain that works, you can bundle your custom /usr directory inside your app. I recommend using the path "Content/Helpers" for executables. You have to be careful about signatures. If you put executable in the wrong directories, they might not be signed correctly and then they will have problems at runtime.


Once it is all bundled up, your app can set any environment variables it needs and then execute the bundled tool with the desired arguments. Since you don't have a terminal, you will have to be clever about standard input, output, and error. Personally, I use real old-school select() statements for these. I've tried all the fancier options but I find the basic approach to be the easiest one to conceptualize. There are fewer things to ***** up.


See what I mean about asking "how to program"?

I've used Lua, but my situation is that I use a Python script that uses the SMTP library of Python, and I wouldn't know how to duplicate that functionality in a different language. An option, I suppose, would be to find some other SMTP library.


I'm not worried about how to run a bundled tool. I use NSTask to run the script, and I could use the same techniques to run a command executable.


What does worry me (maybe it's a silly worry) is that if I build Python with a specified PREFIX, it might think that all its files have full paths beginning with that prefix. If these files are inside my application bundle, which is not at a fixed location, then that wouldn't be true.

Is this SMTP functionality the only reason you need Python?

Share and Enjoy

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

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

Yes, the SMTP functionality is the only reason I need python. I guess I should look into Hedwig. It's in Swift, which I've never used, but I should make time to learn about it one of these years.

If you including Python just for SMTP, then there are lots of better alternatives. First of all, you are on a Mac, so you have libcurl available, which already supports SMTP. cURL can be a bit funky, but it works well. I didn't find very many alternatives. I did find github.com/somnisoft/smtp-client, a couple of GPL ones, and at least one proprietary. Although I haven't used SMTP in libcurl, I've used curl for other things and its fine. Most SMTP client libraries are straight C. Hedwig is more for Swift people.


What are you doing with this anyway? Although SMTP technically "Simple", you are adding some significant practical complications. Wouldn't end users have to configure it? That's a royal mess. Is this just for your own use? I strongly recommend a simple REST server instead. You can do that in Python on AWS and have it e-mail you the results. I do that and it is much more reliable that e-mail from clients.


As far as I can tell, the Python portion of your project should be dead and buried at this point. For if it ever comes up in the future, you do have a valid concern about the PREFIX. If you are incorporating any open-source libraries, you have to be careful about builds and things like PREFIX. Now that 32-bit is gone, things are easier because you only have to worry about 64-bit. But if you are ever doing any cross-platform development for iOS and macOS, open source components can be delicate. They must be both built and "configure"d using the correct host and target platforms. There may be CPU and platform-dependent code in generated headers.

mac

Yes, the SMTP functionality is the only reason I need python.

OK.

I guess I should look into Hedwig.

Yeah, or any other SMTP library. Whichever library you choose, it’s likely to be easier to embed that than to embed a full Python runtime.

Share and Enjoy

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

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

I didn't know libcurl could do SMTP. I looked at the docs a bit, and it looks kind of complicated. For instance their examples have hard-coded inputs for headers like date and message-ID, and I would have expected an SMTP library to take care of that stuff for me. Thanks for the pointer to smtp-client, that looks promising.


I'm using SMTP to send crash reports from user machines to mine, with minimal help from the user. I use my own SMTP server, so it does not have to be configured by end users, they don't even have to have set up an email client.

I think that's just an example. You can provide your own date and a UUID for message ID.


If you release in the App Store, you automatically get a sample of crash reports. I would recommend a web service over SMTP. Many users have funky networks and/or firewalls/AV apps that will block anything other than http. They will often block http too, but an SMTP seems likely to trigger the paranoid set.

Yes, I knew about App Store provided crash reports, but I can't sell in the App Store for various reasons.


I don't quite get what you're saying about networks and firewalls. I could understand that being a problem if I were trying to run an SMTP server on the client's Mac, but that's not the situation. I think I'm basically doing the same as what Mail.app or Thunderbird.app does when it sends an email, except that I'm using a hard-coded server address instead of asking the user to configure one.

I'm thinking of things like corporate firewalls and security apps like "Little Snitch" and others. One popular "antivirus" app installs a man-on-the-middle attack. These regularly cause problems even with web connections. I would expect an SMTP connection to be at an even higher risk of being blocked.

I would expect an SMTP connection to be at an even higher risk of being blocked.

That matches my experience. Even relatively permissive corporate firewalls tend to block outgoing SMTP connections.

Also, sending email via a web service makes it easier to update things to accommodate change; you just have to update your web service, you don’t have to update your client apps.

Share and Enjoy

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

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