Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass compiler flags to alllow use of @main in single-source-file executable modules #3410

Conversation

abertelrud
Copy link
Contributor

@abertelrud abertelrud commented Apr 16, 2021

Pass -parse-as-library when compiling an executable module that has a single source file that isn't named main.swift. This avoids a compiler error when using @main in single-file executable modules.

Motivation

It should be possible to use @main in executables, even if they are implemented as a single source file.

Background

The Swift compiler has certain special behaviors regarding main source files:

  • if a module has just a single source file of any name, then that file is treated as the main source file
  • if a module has a source file named main.swift, then that file is treated as the main source file

If a source file is considered the main source file, it can have top level code. But a source file that has top level code can't also have @main.

This means that a single source file executable module can't use @main, regardless of the name of that source file. A second empty source file can be added as a workaround, but we can employ some countermeasures in SwiftPM.

The issue of wanting more control over the compiler's semantics continues to be tracked by https://bugs.swift.org/browse/SR-14488. What would really be needed here would be for SwiftPM to be able to tell the compiler that it knows it wants to build and executable, and for the compiler to allow either top-level code or @main in any one of the source files (regardless of how many source files there are or what they are named). Until we have that, adding this workaround (and matching the same heuristics as Xcode has) seems desirable.

Changes

  • pass -parse-as-library if the executable module consists of a single source file and it is not named main.swift, and if the tools version of the package is 5.5
  • update the change log

Results

Packages that specify 5.5 as their required tools version can now used @main in single-source-file executables as long as those source files aren't named "main.swift" (in which case top-level code is expected).

This still does not allow use of @main in source files named main.swift, but that will require compiler support to address (in the absence of scanning source files to see which ones use @main).

It is worth noting that, according to experiments, such package targets already don't build in Xcode (which is of course not something that SwiftPM can fix, but there is a certain advantage to having matching semantics, and it's arguable better to require renaming a single source file rather than to completely block use of @main in single-source file executable modules).

Notes

We should consider updating the documentation for executable targets. The special casing going on here is tricky to explain but stems from the applying of the compiler's heuristics around source files (which are motivated by direct invocation of the compiler without additional information) to package targets (where the intent is already known).

rdar://76746150

@abertelrud abertelrud marked this pull request as draft April 16, 2021 18:12
Copy link

@CertifiedRice CertifiedRice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmm, looks good... I guess

@abertelrud abertelrud self-assigned this Apr 17, 2021
@abertelrud abertelrud force-pushed the pass-parse-as-library-for-single-source-exec branch from 219dd49 to 8344de2 Compare April 19, 2021 17:35
@abertelrud abertelrud changed the title WIP: Pass -parse-as-library when compiling an executable module that has a single source file that isn't named main.swift Allow use of @main in single-source-file executable modules Apr 19, 2021
@abertelrud abertelrud changed the title Allow use of @main in single-source-file executable modules Pass compiler flags to alllow use of @main in single-source-file executable modules Apr 19, 2021
@abertelrud
Copy link
Contributor Author

@swift-ci please smoke test

@abertelrud abertelrud added the ready Author believes the PR is ready to be merged & any feedback has been addressed label Apr 19, 2021
@abertelrud abertelrud marked this pull request as ready for review April 19, 2021 17:47
@tomerd
Copy link
Contributor

tomerd commented Apr 19, 2021

@abertelrud thanks for working to fix this unfortunate state of things. is there a better fix that we can do in the compiler instead of working around the limitation in SwiftPM? @nate-chandler @airspeedswift ?

@abertelrud
Copy link
Contributor Author

abertelrud commented Apr 19, 2021

@abertelrud thanks for working to fix this unfortunate state of things. is there a better fix that we can do in the compiler instead of working around the limitation in SwiftPM? @nate-chandler @airspeedswift ?

Absolutely. Had a good conversation with @nate-chandler about this, and added notes to https://bugs.swift.org/browse/SR-14488. I think that a long-term solution here is to take advantage of the fact that SwiftPM knows that it is building an executable (from the manifest), so there is no need for heuristics. I elaborate on this in the SR, but conveying the intent to the compiler is the basic idea, and then there would not be any need for heuristics based on the number and names of source files.

But @nate-chandler indicated that this would not be possible in the 5.5 timeframe.

@tomerd
Copy link
Contributor

tomerd commented Apr 19, 2021

But @nate-chandler indicated that this would not be possible in the 5.5 timeframe.

adopting a short term solution is fine if we are tracking fixing it the right way for the version that comes after 5.5. We should also add code comments + radar number to that end as part of this short term workaround.

@abertelrud
Copy link
Contributor Author

But @nate-chandler indicated that this would not be possible in the 5.5 timeframe.

adopting a short term solution is fine if we are tracking fixing it the right way for the version that comes after 5.5. We should also add code comments + radar number to that end as part of this short term workaround.

Adding comments and a reference to the SR is a great point. I'll update the PR.

… a single source file that isn't named `main.swift`

The Swift compiler has certain special behaviors regarding main source files:

- if a module has just a single source file of any name, it's treated as the main source file
- if a module has a source file named `main.swift`, it's treated as the main source file

If a source file is considered the main source file, it can have top level code.  But a source file that has top level code can't also have `@main`.

This means that a single source file executable module can't use `@main`, regardless of the name of that source file.  A second empty source file can be added as a workaround, but we can employ some countermeasures in SwiftPM.

Specifically, if the executable module consists of a single source file and it is not named `main.swift`, we pass `-parse-as-library` so that a single-source file module will work.  This matches what can be seen in the build logs in Xcode, meaning that packages will build the same in SwiftPM and in Xcode.

Note that this still does not allow use of `@main` in source files named `main.swift`, but that will require compiler support to address.

Since this has the potential to break existing packages that use top-level code in a single source file that isn't named `main.swift`, this behavior is gated by a 5.5 tools version.

 See https://bugs.swift.org/browse/SR-14488 for discussion about improvements so that SwiftPM can convey the intent to build an executable module to the compiler regardless of the number of files in the module or their names.

rdar://76746150
@abertelrud abertelrud force-pushed the pass-parse-as-library-for-single-source-exec branch from 8344de2 to e333dbe Compare April 19, 2021 22:37
@abertelrud
Copy link
Contributor Author

@swift-ci please smoke test

@abertelrud
Copy link
Contributor Author

@swift-ci please smoke test macos

@abertelrud
Copy link
Contributor Author

@swift-ci please smoke test macos

@abertelrud abertelrud merged commit 41b97c8 into swiftlang:main Apr 20, 2021
@abertelrud abertelrud deleted the pass-parse-as-library-for-single-source-exec branch April 20, 2021 20:31
abertelrud added a commit to abertelrud/swift-package-manager that referenced this pull request Apr 21, 2021
… a single source file that isn't named `main.swift` (swiftlang#3410)

The Swift compiler has certain special behaviors regarding main source files:

- if a module has just a single source file of any name, it's treated as the main source file
- if a module has a source file named `main.swift`, it's treated as the main source file

If a source file is considered the main source file, it can have top level code.  But a source file that has top level code can't also have `@main`.

This means that a single source file executable module can't use `@main`, regardless of the name of that source file.  A second empty source file can be added as a workaround, but we can employ some countermeasures in SwiftPM.

Specifically, if the executable module consists of a single source file and it is not named `main.swift`, we pass `-parse-as-library` so that a single-source file module will work.  This matches what can be seen in the build logs in Xcode, meaning that packages will build the same in SwiftPM and in Xcode.

Note that this still does not allow use of `@main` in source files named `main.swift`, but that will require compiler support to address.

Since this has the potential to break existing packages that use top-level code in a single source file that isn't named `main.swift`, this behavior is gated by a 5.5 tools version.

 See https://bugs.swift.org/browse/SR-14488 for discussion about improvements so that SwiftPM can convey the intent to build an executable module to the compiler regardless of the number of files in the module or their names.

rdar://76746150
(cherry picked from commit 41b97c8)
abertelrud added a commit that referenced this pull request Apr 21, 2021
… a single source file that isn't named `main.swift` (#3410) (#3421)

The Swift compiler has certain special behaviors regarding main source files:

- if a module has just a single source file of any name, it's treated as the main source file
- if a module has a source file named `main.swift`, it's treated as the main source file

If a source file is considered the main source file, it can have top level code.  But a source file that has top level code can't also have `@main`.

This means that a single source file executable module can't use `@main`, regardless of the name of that source file.  A second empty source file can be added as a workaround, but we can employ some countermeasures in SwiftPM.

Specifically, if the executable module consists of a single source file and it is not named `main.swift`, we pass `-parse-as-library` so that a single-source file module will work.  This matches what can be seen in the build logs in Xcode, meaning that packages will build the same in SwiftPM and in Xcode.

Note that this still does not allow use of `@main` in source files named `main.swift`, but that will require compiler support to address.

Since this has the potential to break existing packages that use top-level code in a single source file that isn't named `main.swift`, this behavior is gated by a 5.5 tools version.

 See https://bugs.swift.org/browse/SR-14488 for discussion about improvements so that SwiftPM can convey the intent to build an executable module to the compiler regardless of the number of files in the module or their names.

rdar://76746150
(cherry picked from commit 41b97c8)
keith added a commit to bazelbuild/rules_swift that referenced this pull request Mar 17, 2022
This matches the behavior implemented in
swiftlang/swift-package-manager#3410 for single
file modules.
keith added a commit to bazelbuild/rules_swift that referenced this pull request Oct 3, 2022
This matches the behavior implemented in
swiftlang/swift-package-manager#3410 for single
file modules.
keith added a commit to bazelbuild/rules_swift that referenced this pull request Oct 3, 2022
This matches the behavior implemented in
swiftlang/swift-package-manager#3410 for single
file modules.

Fixes #913
tymurmustafaiev pushed a commit to tymurmustafaiev/rules_swift that referenced this pull request Jul 19, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ready Author believes the PR is ready to be merged & any feedback has been addressed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants