SwiftPM support for Swift scripts

Continuing the discussion from SwiftPM support for Swift scripts:

I similarly don’t know how minor or involved it would be for Swift to ignore the shebang line. It is already interpreting lines beginning with #if #warning, etc. So maybe it can see #! and treat it as another type of comment.

I think making the editor ignore the shebang line would put the burden on every Swift editor to need to special case that first line. Having the compiler just recognize and ignore it would mean any Swift editor should handle script files without issue.

1 Like

It already does :+1:

1 Like

Continuing the discussion from SwiftPM support for Swift scripts:

Do you know when that was added? It didn't seem to work when I had tried about a year ago.

I don't know when, but here is someone talking about it in 2015.

1 Like

Yes, you've been able to do that for a long time. But I think a year ago Xcode could not compile the file if it had a shebang line. Now, it seems like it can.

The ~/Library folder and invisible files in the user's home directory have both been a part of the Mac since the original version of Mac OS X (and earlier in OpenStep).

So there’s a long tradition on the platform for Mac apps to use ~/Library and for command-line tools to use invisible files. In a sense, this lets Mac-centric apps be Mac-like and lets Unix-centric / command-line tools be Unix-like.

One of the goals of Swift is to be available on many platforms, not specifically tied to macOS, with this goal reaffirmed as part of the focus of Swift 6:

Swift scripting seems to be very much in the category of command-line / Unix-style usage which fits better with the established convention of hidden files, with the added benefit that it works in a way familiar to many platforms, including macOS.

I definitely understand wanting to do things in a manner that suits the platform, but in this case it seems like the proposal is following how macOS has done things since its inception.

EDITED to add formatting

4 Likes

I have real apprehension that the question that decides this topic will be "who cares?", but I hope there's still something left from the spirit of Steve Jobs, who allegedly rejected the design of a circuit board just because it looked ugly. ;-)

Sticking with Mac conventions would absolutely be no problem for other platforms - and as I pointed out, even on Linux people shift away from creating invisible files in the users home directory.

I couldn't find a comprehensive history of dotfiles on the Mac yet, but just to clarify:
My standpoint is that proper Mac software should honor Mac standards, no matter wether it has a GUI or not.
Yours (and @Mark_Lilback's?) is that those standards have never had any meaning for command-line tools at all - correct?
Do you have some examples of tools created by Apple that use dotfiles instead of ~/Library/?
Or official guidelines that recommend the use of dotfiles?

The Finder uses ~/.Trash, and there's the ~/.CFUserTextEncoding file, which sets the default C string encoding.

Apple also ship plenty of tools which use dotfiles (SSH, git, bash, zsh, etc). Also, LLDB uses dotfiles.

4 Likes

Yes, shifting toward standardized directory paths for config files, caches, and others. It would absolutely not be ok to just throw stuff in a ~/Library directory on Linux.

Oh yes, of course — the trash!
Thinking of that one, there's also the well-known .DS_Store we all love ;-) (but I think that needs no consideration, as it is only for interaction with alien platforms).
The Finder is made by Apple, but it's also definitely not a shell tool, so those examples weaken the hypothetical rule "GUI -> Library, shell -> dotfile".

I don't know the story of those invisible items, and it's quite hard to find any information — but that is what Where to Put Application Files has to say:

It is important to remember that the user domain ( /Users ) is intended for files created by the user. With the exception of the ~/Library directory, your application should never install files into the user’s home directory.

Unfortunately, this is an old document, and it does not mention shell explicitly — but it doesn't mention "apps" either, so I consider this to be a general rule.

Being a Mac user for quite some time, I remembered an evidence that supports this standpoint:

In order to customize tcsh:

    mkdir -p ~/Library/init/tcsh

For those who are too young ;-): tcsh was the first default shell on OS X, and those lines have been shipped with every installation.
So yes, macOS is delivered with some shell tools which only support configuration via dotfiles, but it was decided that the configuration of the shell itself belongs into Library.

I don't know wether Apple changed their rules in the meantime, if they made exceptions, or if there's yet another explanation — but ultimately, I think the answer isn't that important, and the questions that matter are "What's wrong with putting things in ~/Library, and why should we follow the bad precedent of polluting home directories instead?"

swift-sh puts its files in ~/Library/Developer/swift-sh/cache on Mac and uses the XDG FreeDesktop spec on Linux.

I think this argument is moot anyway, since as soon as this is pitched Apple will return it for revision insisting that SwiftPM (an official Apple tool) be consistent with the rest of Apple produced macOS tooling.

4 Likes

Considering that, I'd say a better definition is that .files are for cross-platform compatibility. DS_Store files preserve Finder information on SMB, NFS, and removable media not formatted as HFS+. Hidden files were in use for this when I started using System 6.

99+% of developer documentation is written for app developers. Unix/BSD developer conventions are in man pages.Try to find documentation of AppleScript sdef files. Unless something has changed lately, they are only accessible via `man -s5 sdef.

Look in /etc. Those aren't in /Library or /System/Library. Yet there are the settings for apache, printing (cups), screen sharing, ldap, sshd, and the kernel. Since the first Mac OS X preview, BSD-level stuff is meant to be hidden for the average user. The assumption 20 years ago was that if you used the command line, you already knew the conventions. As I recall, there was talk of not even including Terminal.

Supporting any Foundation API Bundle/File APIs (such as locating the correct Library folder) requires an info.plist. Technically there is a.way to add one to a single binary, but I'v'e never heard of anyone doing it since classic MacOS support went away.

What shell configurations are stored in ~/Library? They are all in /etc and your home directory. One major reason is that Apple was not going to rewrite every command line tool included with Darwin.

Also, for my computer, the convention is by far to use .files. I have 101 items in /Applications and 441 in /usr/local/bin.

Because we are writing command line tools that are likely cross-platform. Because ~/Library is for bundles (that have a plist), not single binary command line tools. Because Apple has always said that OS X includes a BSD compatibility layer, and BSD tools know nothing about ~/Library. And your Library isn't necessarily in your home directory. For Foundation apps, Apple says not to use ~ for any file path.

1 Like

Imho that is exactly how it should be :+1:

Can you elaborate? I hope you consider swift-sh to be consistent with Apple produced macOS tooling, so that SwiftPM will use the same locations to store its files; but obviously, there's no full agreement (where the files belong) in this thread... and the concept is co-authored by an Apple-employee, isn't it?

Big plus one on the concepts behind this. But I think the fact that features in this space are sorely needed should not produce a solution that doesn’t integrate with the rest of the language and application model. There should not be a script only syntax for special script files which cannot be compiled and invoked inside of applications. To that effect I would strongly vote for a solution that

  1. Pursues general purpose expansion of import syntax to meet the needs of scripting while also being useable and providing utility in application code.

Being able to specify the source for imports in source code is not a terrible idea within the context of application code. The Swift compiler should have a way to essentially scan the source of a module and generate a list of swiftpm artifacts that can be referenced from Package.swift. It should be possible to build a swift application that only has swift swiftpm module dependencies and basically not have to write or independently maintain package.swift. The capability to infer the set of packages needed in advance from source code would also enable precompilation of script files in future. There is absolutely no reason that package resolution support for scripting should be a runtime only feature. It should be possible for package resolution to run at runtime — but also possible (eventually) to compile command line applications into fastest possible standalone binaries with pre-resolved dependencies.

  1. The code in swift scripts should be invokable from a swift application

I think there needs to be a richer compilation model for these type of “script like entrypoints” — the same need exists to be able to compile and invoke code from notebooks written in this same “imperative code at module scope” style.

The compilation model for these kind of files should do something like synthesize a method from the module scope imperative code, which is then adaptable to invocation from different entrypoints. For command line entry there’s an adaptor that calls the synthesized function from main. For application code entry — you could call the function with a string argument or something — or even better the api exposed to application could be significantly richer and provide ergonomic features for different use cases — like choosing whether to call the function in-process or to instead spin up a subprocess capable of calling the function and invoking it there.

A quality application level programming model for your command line entry points that lets you invoke the exact same code paths from app and test code would do a lot to help ensure that command line entrypoints are not low quality forgotten pieces of glue trash as is so common with most application ecosystems. Instead these entrypoints could be easily composable components of application architecture.

2 Likes

I love this pitch, and I am really thrilled to see you working to improve this with first class SwiftPM support. I haven't read the detailed comments from others above, but I have one particular question about this part of the pitch:

Why the limitation? Have you explored allowing this for all cases? Such a change is a step towards making the trivial statically compiled app case (which, in the example of small CLTools is likely to be very very simple) work without a Package.swift file, would make the 'hello world' cases much smaller (better "progressive disclosure of complexity") and would make scripts less special.

This sort of thing matters quite a bit before it impacts tutorials. It would be nice for SwiftPM to make the "set up a simple command line tool tutorial" to be a single file, which can then grow a manifest when and if things get more complex.

I suppose the downside of this is that the manifest isn't self contained (you'd have to scan the source files to find dependencies) but it is worth considering the tradeoff there.

-Chris

13 Likes

This is kinda unrelated to the thread, but this is done by tools which can show UI. Checkout swift-progress as an example (it's the CREATE_INFOPLIST_SECTION_IN_BINARY Xcode setting).

Actually it would be quite cool to support this (i.e. write scripts which can show AppKit UI). Or does this just work? No idea :slight_smile:

2 Likes

With SwiftUI making it so easy and compact to create a UI, it's actually quite feasible to make a small GUI for your tool. It can be really useful for prototyping UI components.

You don't need an Info.plist section for UI, though - here's the script template I use for SwiftUI. You do need it for other things like entitlements (it would be really nice if we added support for those... at all!).

3 Likes

I think I would much rather have my build tools be liberal with respect this method of declaring dependencies. Having the option of turning on a linting rule to catch instances of this seems like a better way of policing than imposing this limitation on every project only because it might not be ideal for the average case.

1 Like

I'm really excited about this proposal! I love Swift "scripting" but honestly I always endup just making CLI binaries because they have a way better experience. This makes me wonder how all of you work since I'm seeing a lot of discussion about short vs. long lived scripts. Seems like the tooling should make an effort to support both cases and, most importantly, the transition from one to the other, like creating a distributable binary from a script. For me the most important thing is that Xcode supports the scripts directly. (and things like the difference between and Xcode CLI app and a SPM executable are weird)

I also don't think we should be changing the syntax from the one in SPM. If I don't care about the version I can just specify branch master. Is not a big deal IMO. But if we endup maknig a different syntax I would like to see same changes accepted on the SPM Package. I feel like both system should follow the same rules for easy of adoption. (Unless we go with a totally new and custom import)
Another thing I'm not sure I want to see is to favourite GitHub in any way, I feel like that's something that can be done in a third party solution but it would feel weird from Swift itself.

1 Like

1+ on this

I know Xcode is out of the scope of Swift Evolution but I would just like to put it out there that it would be convenient if you could execute scripts with given params directly from the Xcode UI similarly to how you can run individual tests today. This would be very helpful for development tasks such as seeding a database, running database migrations etc.

1 Like