swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.2k stars 10.32k forks source link

[SR-12683] `@main` is no longer usable due to misdetection of top level code #55127

Open compnerd opened 4 years ago

compnerd commented 4 years ago
Previous ID SR-12683
Radar rdar://problem/62475602
Original Reporter @compnerd
Type Bug

Attachment: Download

Additional Detail from JIRA | | | |------------------|-----------------| |Votes | 16 | |Component/s | | |Labels | Bug | |Assignee | None | |Priority | Medium | md5: d99a1a03d2003d32b1bfd6a2ae65a2fa

is duplicated by:

Issue Description:

There seems to be a recent regression with comment blocks and functions being treated as top level code which prevents the use of `@main`.

natecook1000 commented 4 years ago

@swift-ci create

natecook1000 commented 4 years ago

It looks like compilation is failing whenever the compiler's in single-file mode. Using the two attached source files, I get this result from trying to compile just MyMain.swift:

$ swiftc MyMain.swift
MyMain.swift:3:1: error: 'main' attribute cannot be used in a module that contains top-level code
@main
^
MyMain.swift:1:1: note: top-level code defined in this source file
// @main attribute test
^

When I add Empty.swift, the issue goes away:

$ swiftc MyMain.swift Empty.swift
(compiles without issue)
natecook1000 commented 4 years ago

To make this work, you can tell the compiler to parse the file as a library instead of in single-file mode with the -parse-as-library flag:

$ swiftc -parse-as-library MyMain.swift
swift-ci commented 3 years ago

Comment by Josh Wright (JIRA)

Any updates to this? Hitting this issue as well when writing a single file script.

Nate's workaround works, but would be great to have this work out of the box (was using it indirectly through https://github.com/mxcl/swift-sh so it wasn't apparent how to update the compiler flags).

swift-ci commented 2 years ago

Comment by Eneko Alonso (JIRA)

This seems to still be an issue for new Swift 5.5 packages containing a main.swift file.

Steps to reproduce:

  1. mkdir myapp && cd myapp
  2. swift package init --type executable
  3. Replace `main.swift` content with the following

{{
@main
struct Program {
static func main() {
print("Hello, world!")
}
}
}}

4. swift run

  1. Observe error: main.swift:2:1: error: 'main' attribute cannot be used in a module that contains top-level code

Multiple files

Adding a second file does not seem to fix the issue:

  1. create a second file Hello.swift with this content

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

2. Update `main.swift` as follows:

{{
@main
struct Program {
static func main() {
hello()
}
}
}}

3. swift run

  1. Observe error: main.swift:2:1: error: 'main' attribute cannot be used in a module that contains top-level code

Solution

Renaming `main.swift` to something else (eg. `Program.swift`) solves the issue, whether there are only one file or more.

  1. Delete Hello.swift
  2. Update main.swift as follows:

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

@main
struct Program {
static func main() {
hello()
}
}
}}

3. swift run

  1. Observer the output "Hello, world!"
ubunatic commented 2 years ago

I can confirm the issue for this example where I import a cooperating module:

// Swift version 5.6 (swift-5.6-RELEASE)
// Target: arm64-apple-macosx12.0
import Foundation
import SwiftUI
import HelloCore

@main
struct HelloApp: App {
    var body: some Scene {
        WindowGroup {
            HelloView().onAppear {
                print("loaded")
                signal(SIGINT) { _ in
                    print("exit")
                    exit(EXIT_SUCCESS)
                }
            }
        }
    }
}

The renaming solution works. However, you may then see swift build errors such as:

'Hello': error: executable product 'Hello' expects target 'Hello' to be executable;
an executable target requires a 'main.swift' file

You can fix that by changing the .target in your Package.swift to .executableTarget.

jhoughjr commented 1 year ago

Trying to use this example for using raw nio:https://theswiftdev.com/swiftnio-tutorial-the-echo-server/ its only from 3 months ago, but I the newest info I've found. seems the renaming worked for me was well.

DeCarabas commented 9 months ago

This issue seems very old but I'd like to chime in that it doesn't seem fixed at all in Swift 5.9. See https://github.com/apple/swift-getting-started-cli/issues/3 - I hit the bug midway through the "getting started" tutorial for a command line program.

MaxMacleod commented 8 months ago

Studying Apple's tutorial Getting Started with ArgumentParser, perhaps this is expected behaviour. See:

Note The Swift compiler uses either the type marked with @main or a main.swift file as the entry point for an executable program. You can use either one, but not both — rename your main.swift file to the name of the command when you add @main. In this case, rename the file to Count.swift.

ghost commented 8 months ago

Studying Apple's tutorial Getting Started with ArgumentParser, perhaps this is expected behaviour.

That was my conclusion as well, but then you get a different error if you don't have any main.swift files:

executable product 'currgen' expects target 'Generator' to be executable; an executable target requires a 'main.swift' file

@ubunatic had the actual fix in his comment above: executable target. The problem is that Getting Started with ArgumentParser has target, so that example code actually fails to compile. But the example in ArgumentParser's README does use executableTarget.

That seems like a different issue. First, the tutorial should be updated. Second, the error could probably say something that would help direct the person to using executableTarget. Which repo would be the right place to log an issue about the error message?

shepazon commented 6 months ago

This is frustrating for someone writing tutorials about Swift development using an asynchronous API for both Apple platforms and Linux, because:

  1. This issue doesn't seem to happen on Linux.
  2. When you write that people can create a single-file project using swift package init, they get a source file named "main.swift".
  3. This means the reader needs to rename the file before they do their first build that uses the SDK. This is a hack, and a hack that's only necessary under certain conditions.
  4. That makes the "how to start this sample project" explanation either more complex than necessary (you go through all the details, when to rename the file and when not to, why to do so, etc), prone to being obsoleted (you just tell everyone to always rename the main source file), or not working at all for some readers (you ignore the problem and leave the default source file name alone).

It would be nice if whoever needs to fix this would just do it. I can't imagine it being that hard to fix...

ayaen commented 2 months ago

This still is an issue with release 5.10.1. I tried following this tutorial, and it failed. I tried following the same by never doing swift run with the main.swift, and only ever building with the MyCLI.swift file and still get the error:

...\mycli\Sources\MyCLI.swift:3:1: error: 'main' attribute cannot be used in a module that contains top-level code
@main
^
...\mycli\Sources\MyCLI.swift:1:1: note: top-level code defined in this source file
import Figlet
^
...\mycli\Sources\MyCLI.swift:1:1: note: pass '-parse-as-library' to compiler invocation if this is intentional
import Figlet
^
...\mycli\Sources\MyCLI.swift:3:1: error: 'main' attribute cannot be used in a module that contains top-level code
@main
^
...\mycli\Sources\MyCLI.swift:1:1: note: top-level code defined in this source file
import Figlet
^
...\mycli\Sources\MyCLI.swift:1:1: note: pass '-parse-as-library' to compiler invocation if this is intentional
import Figlet
^
error: fatalError
ayaen commented 2 months ago

OK so it looks like I need to use -XSwiftc -parse-as-library in order to be able to do swift run.

marcstober commented 1 week ago

OK so it looks like I need to use -XSwiftc -parse-as-library in order to be able to do swift run.

Ran into the same issue in the same tutorial as @ayaen , Swift 5.10.1 on WIndows. This worked but it was -Xswiftc with a lowercase s.