How do you tell the compiler to treat another file as if it were `main.swift`?

What flags to you have to pass to the compiler to tell it to generate an executable from a file that isn’t called main.swift? How does it know to treat top‐level code as a function body? How does it decide what to name the main function? How does it set up the entry point? Is Windows any different?

Such a thing must be possible:

  • SwiftPM generates an executable from LinuxMain.swift.
  • CMake doesn’t care what the file name is; @compnerd’s example uses hello.swift and works across all platforms.

My main motivation for asking is the hope of finding a workaround for this, but as I hunt through the source of both SwiftPM and CMake, I’ve mostly just realized I have no idea how the compiler works in this area in the first place. I’m finding nothing mostly because I don’t even know what I’m looking for.

Do you mean something like swift-evolution/0281-main-attribute.md at master · apple/swift-evolution · GitHub?

No. I mean if I have a file titled Hello World.swift, and it contains exactly this...

print("Hello, world!")

...what flags do I have to pass to swiftc to get it to compile properly?

I got away with

swiftc -o a.out hello.swift

where hello.swift had your print statement in it. I tried it without the -o parameter, but nothing was generated. Unlike gcc and clang, swiftc doesn't seem to create a default executable if you don't give an output parameter.

If you actually built a file called Hello World.swift, did you either put the filename in double quotes, or escape the space? Otherwise, swiftc would try to compile a file named Hello (presumably, Hello.swift), and World.swift

1 Like

Well that’s so delightfully simple it didn’t help with what I’m actually trying to solve. But that’s my fault for reducing it so much. So here’s take two:

If I have two files:

// HelloWorld.swift

go()
// HelloWorld2.swift

func go() {
  print("Hello, world!")
}

Now what do I pass? The naïve derivative of your invocation above would be this:

% swiftc -o hello HelloWorld.swift HelloWorld2.swift
HelloWorld.swift:3:1: error: expressions are not allowed at the top level
go()
^

I don't know if it's changed, but when I left Apple there were three ways for a file to be "main":

  1. It is the only file in the compilation and you have not passed -parse-as-library
  2. It is named main.swift
  3. It contains a type with one of the main-ish attributes

I don't actually know how LinuxMain.swift works, but it wouldn't surprise me if the package manager is simply copying it to a temporary directory and renaming it to main.swift. You could toss a print(#file) in there to see if that's the case.

On the one hand, there's no reason why the main file needs to be named main.swift. On the other, there's no reason not to name it main.swift except if you're using the name of the file to decide how to process it for some reason (like SwiftPM). That's why no one's added that capability in all the years Swift's been public.

(Xcode actually always passes -parse-as-library, so Xcode targets have to use main.swift if they want top-level code.)

1 Like

Putting SwiftPM into verbose mode (and using --enable-test-discovery) yields this:

/usr/bin/swiftc [...] -o [...]/.build/x86_64-unknown-windows-msvc/release/WorkspacePackageTests.xctest [...]

So I guess that falls into the first category as far as swiftc is concerned.

When I look closer at the actual error, it isn’t swiftc complaining, but the linker inside clang:

The only thing that still isn’t working is the test executable:

/usr/bin/lld-link: error: entry point must be defined
clang-7: error: linker command failed with exit code 1 (use -v to see invocation)
[5/6] Linking MyPackagePackageTests.xctest

So maybe I’m looking in completely the wrong spot to begin with.

Are you sure? My experience is that it does, and it is named the same as the test file (e.g. hello)