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

FoundationMacros: use cross-compilation to build for host #714

Merged
merged 1 commit into from
Aug 12, 2024

Conversation

compnerd
Copy link
Member

@compnerd compnerd commented Jul 3, 2024

Use ExternalProject to switch FoundationMacros to cross-compilation. This allows us to build the macros for the right OS/architecture when cross-compiling Foundation for other environments.

Copy link
Contributor

@etcwilde etcwilde left a comment

Choose a reason for hiding this comment

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

Few nits, but otherwise LGTM.

@compnerd
Copy link
Member Author

compnerd commented Jul 3, 2024

@swift-ci please test

@shahmishal
Copy link
Member

@swift-ci please test

1 similar comment
@shahmishal
Copy link
Member

@swift-ci please test

@compnerd
Copy link
Member Author

CC: @bnbarham there was a conversation on swiftlang/swift#75206 which seems to have converged around moving ahead with this change.

@compnerd
Copy link
Member Author

@jmschonfeld @bnbarham - any idea if this is something we should be moving forward with? This is going to break the windows builds at least, but I want to ensure that we only are investing in this if we are going to move forward with this.

@jmschonfeld
Copy link
Contributor

@jmschonfeld @bnbarham - any idea if this is something we should be moving forward with? This is going to break the windows builds at least, but I want to ensure that we only are investing in this if we are going to move forward with this.

I think I'm still a little fuzzy on the details for this, so just to lay it out to confirm:

Currently we have:

(builder platform = host building the toolchain, toolchain platform = host where the toolchain will be used, sdk platform = platform where things the toolchain builds will be run)

Linux:

  • Macro builds as a library and links the early swift syntax build
  • Macro builds for builder platform (platform that is building the toolchain)
  • Macro gets used for FoundationEssentials build
  • Macro gets installed into the toolchain since in the supported case, builder platform == toolchain platform

Windows: Macro does not build

And with this PR, can you clarify what changes? In specific

  • Does this still install the macro on linux where builder platform == toolchain platform
  • Do we build the macro on Windows for the builder platform to use to build FoundationEssentials (this is yes right?)
  • Do we build the macro on Windows for the toolchain platform? And it is not built for the SDK platform right?
  • Is it still built as a library?

I think I'm still unclear on those points and I want to make sure that we do not regress what Linux already has working in the process of getting it right on Windows (but I agree we need to make sure we get it right on Windows too)

@etcwilde
Copy link
Contributor

So the goal is to split where the SDK library that actually goes in the runtime environment and the macros that are only useful if you're compiling things against that library.
Right now, the build is set up such that the macro will always build for whatever you're building swift-foundation to run on, but not necessarily where you're running the compiler to build things against that SDK, which isn't particularly helpful if you don't want/need the macro to run there. It also means that we'll need to build and include Swift-Syntax in the built SDK, even though it will just be dead weight in situations where you're never running a compiler.

What this PR does is, when building Swift-Foundation, build the macro for the platform that is doing the build, rather than what swift-foundation is being cross-compiled for. This is just like what we're doing for Swift-Testing (https://github.com/apple/swift-testing/blob/b19c9af0750e219fc972217af3f061b1b0b60897/Sources/CMakeLists.txt#L22-L30).

It's still not perfect, but it's better because it means that we can use these macros in the build of swift-foundation.
In a future iteration, we might be able to teach the build to produce macros for each of the toolchain platforms that we support so that we can have an SDK that works universally with each of the supported toolchains (good for cross-compiling story) instead of only the toolchain that is on the system that you're deploying for (not great if you're compiling for a low-powered phone or something).

@compnerd
Copy link
Member Author

Just to clear up some of the confusion on what is happening today:

Windows:

  • Macros build as executables, link the early swift syntax build
  • Macros build for the platform that foundation are being built for (e.g. Android)
  • Macros get used for the tests

Note that this is identical to Linux where the macros are built for the platform that foundation is being built for (e.g. macOS).

@bnbarham
Copy link
Contributor

Macros build as executables, link the early swift syntax build

In the toolchain build or standalone? If the former, how are they being picked up at runtime? IIRC we only add toolchain search paths for the libraries, not executables.

@compnerd
Copy link
Member Author

Macros build as executables, link the early swift syntax build

In the toolchain build or standalone? If the former, how are they being picked up at runtime? IIRC we only add toolchain search paths for the libraries, not executables.

Ugh, wait, I mixed up two scenarios. The reason that it is building with the toolchain early swift-syntax is because I'm building locally with a reference to the toolchain build. That change is dependent on the other change that I have pending.

The CI builds currently use SPM based builds in a docker container, so they are building their own copy of swift-syntax.

For the toolchain distribution, we should be using the toolchain build of swift-syntax though to avoid bloat (the toolchain is already large enough that it prevents pre-installation on GHA agents).

@bnbarham
Copy link
Contributor

For the toolchain distribution, we should be using the toolchain build of swift-syntax though to avoid bloat

Sure, that should be the case here - we should be passing through SwiftSyntax_DIR and thus going down the FindPackage path.

@compnerd
Copy link
Member Author

For the toolchain distribution, we should be using the toolchain build of swift-syntax though to avoid bloat

Sure, that should be the case here - we should be passing through SwiftSyntax_DIR and thus going down the FindPackage path.

I don't think that was wired up in the swift-corelibs-foundation build which is now re-cored to swift-foundation. We don't build swift-foundation on its own yet, so this particular path is more future direction and I don't think that we are setup to test it in CI.

@jmschonfeld
Copy link
Contributor

For the toolchain distribution, we should be using the toolchain build of swift-syntax though to avoid bloat

Sure, that should be the case here - we should be passing through SwiftSyntax_DIR and thus going down the FindPackage path.

I don't think that was wired up in the swift-corelibs-foundation build which is now re-cored to swift-foundation. We don't build swift-foundation on its own yet, so this particular path is more future direction and I don't think that we are setup to test it in CI.

AFAIK it should be - when invoking the swift-corelibs-foundation build from both build.ps1 and the build-script, we provide SwiftSyntax_DIR (and swift-corelibs-foundation pulls swift-foundation in via FetchContent and the goal is to go down the find_package path, if that's not happening it's likely a bug we need to fix in the build script or CMake file

@jmschonfeld
Copy link
Contributor

@etcwilde @compnerd ah thanks for clearing up a lot of those details, I think I understand the problem space better now. I think overall this approach sounds good, the only two points that I think we should address before merging this are:

  1. Can we make sure to install the macro into the just-built toolchain for cases where the current host platform matches the platform that the toolchain will be used on (i.e. whenever we're not cross compiling like when we build the linux toolchain)? Not sure if there's a way we can dynamically detect this
  2. Based on @bnbarham's comment in the thread above, it sounds like we should ensure we still build the macro as a library like we currently do on Linux rather than as an executable. Is that feasible or do we have an explicit need to use an executable instead?

@compnerd
Copy link
Member Author

For the installation, I don't think that we have a good way to detect when it is being built for a toolchain host. There is CMAKE_CROSSCOMPILING that tells us whether it is cross-compiling, but I could be cross-compiling for a toolchain host (which is, in fact, what we do for ARM64). So, really, the best way to handle this is to explicitly require that you build the install target if you want to install the macros.

The plugin (DLL) approach is not really viable. The entire compiler codebase needs to be annotated with __declspec(dllimport) and __declspec(dllexport) to get that to work (including the LLVM one). Otherwise we run out of space for exporting everything (not to mention, there is a load time and runtime memory penalty for doing that). Executables are the way forward for the foreseeable future (we could try to invasively fork LLVM for Swift and annotate LLVM and split it up of course).

@bnbarham
Copy link
Contributor

The plugin (DLL) approach is not really viable. The entire compiler codebase needs to be annotated with __declspec(dllimport) and __declspec(dllexport) to get that to work (including the LLVM one). Otherwise we run out of space for exporting everything (not to mention, there is a load time and runtime memory penalty for doing that). Executables are the way forward for the foreseeable future (we could try to invasively fork LLVM for Swift and annotate LLVM and split it up of course).

We currently use libraries for the stdlib plugins. The driver isn't setup to load executables from the toolchain at all, so if we did end up going down that route we'd need updates there. But I'm not sure I understand why we have to. Why does LLVM matter at all here?

@compnerd
Copy link
Member Author

We currently use libraries for the stdlib plugins. The driver isn't setup to load executables from the toolchain at all, so if we did end up going down that route we'd need updates there. But I'm not sure I understand why we have to. Why does LLVM matter at all here?

The plugin host needs to expose interfaces to the plugin (i.e. -rdynamic). If the plugins have no assumed library interface, then this should be possible.

@bnbarham
Copy link
Contributor

The plugin host needs to expose interfaces to the plugin (i.e. -rdynamic). If the plugins have no assumed library interface, then this should be possible.

There's an interface, but it uses swift-syntax APIs and they are in SwiftSyntax.dll.

Just to re-iterate, the stdlib macro plugins are both libraries today and as far as I'm aware they don't have any issues. This seems like an identical case to me.

@hyp
Copy link
Contributor

hyp commented Jul 29, 2024

Added additional changes to make this also work for the android swift-foundation building from build.ps1 windows host:
compnerd#1
swiftlang/swift#75549

set(_SwiftFoundation_ExpressionMacro "${BINARY_DIR}/FoundationMacros.dll#ExpressionMacro")
else()
set(_SwiftFoundation_PredicateMacro "${BINARY_DIR}/FoundationMacros#PredicateMacro")
set(_SwiftFoundation_ExpressionMacro "${BINARY_DIR}/FoundationMacros#ExpressionMacro")
Copy link
Contributor

Choose a reason for hiding this comment

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

I think I'm still confused on what the intention of these settings are? Are they supposed to be paths that we provide as -plugin-path to the FoundationEssentials build? If so, I think I'd expect them to be in the library directory on non-windows and also look like libFoundationMacros.so rather than FoundationMacros#ExpressionMacro.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, this was the expected parameter for the macro. Although, after the discussion with @rintaro yesterday, I don't think it matters. Once we have the custom option for the macro, this will no longer be needed.

if(CMAKE_HOST_WIN32)
set(_FoundationMacrosSwiftFlags "-use-ld=lld")
endif()
ExternalProject_Add(FoundationMacros
Copy link
Contributor

Choose a reason for hiding this comment

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

Just to clarify with this approach, I know that the build script will need to invoke the macro build separately. Is the goal that it needs to invoke the macro build once for the current host (and pass that path to the swift-foundation build to use when building itself) and once for each target host (and that it will actually install instead of just building)? In FoundationEssentials/CMakeLists.txt we have a FoundationEssentials --> FoundationMacros dependency added on non-Windows, I think we'll need to adjust that accordingly if there's no longer a target built in this same project

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, the idea is that we would need at least two builds:

  1. build: for use by the compiler building swift-foundation (not installed)
  2. host: for distribution only

The path to the build build of the macros would be passed in as a parameter for the build as we discussed.

Use `ExternalProject` to switch FoundationMacros to cross-compilation.
This allows us to build the macros for the right OS/architecture when
cross-compiling Foundation for other environments.

Co-authored-by: Alex Lorenz <[email protected]>
@jmschonfeld
Copy link
Contributor

@swift-ci please test

Copy link
Contributor

@jmschonfeld jmschonfeld left a comment

Choose a reason for hiding this comment

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

This LGTM now - approved to merge, but let's try to merge it with both of our swiftlang/swift changes and your swift-installer-script change just so that everything switches to the new build style at once

@compnerd compnerd merged commit aee18e4 into swiftlang:main Aug 12, 2024
3 checks passed
@compnerd compnerd deleted the crossed-macros branch August 12, 2024 16:41
compnerd added a commit to compnerd/swift-foundation that referenced this pull request Aug 14, 2024
)

Use `ExternalProject` to switch FoundationMacros to cross-compilation.
This allows us to build the macros for the right OS/architecture when
cross-compiling Foundation for other environments.

Co-authored-by: Alex Lorenz <[email protected]>
compnerd added a commit that referenced this pull request Aug 15, 2024
Use `ExternalProject` to switch FoundationMacros to cross-compilation.
This allows us to build the macros for the right OS/architecture when
cross-compiling Foundation for other environments.

Co-authored-by: Alex Lorenz <[email protected]>
cthielen pushed a commit to cthielen/swift-foundation that referenced this pull request Nov 8, 2024
)

Use `ExternalProject` to switch FoundationMacros to cross-compilation.
This allows us to build the macros for the right OS/architecture when
cross-compiling Foundation for other environments.

Co-authored-by: Alex Lorenz <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants