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

Windows Support #769

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

Windows Support #769

wants to merge 10 commits into from

Conversation

compnerd
Copy link
Contributor

@compnerd compnerd commented May 11, 2023

This initial patch series beings the work to support Windows. It is incomplete in the sense that there are multiple issues with the tests that need to be ironed out still. However, the core of the changes seem correct to partially work through the test suite.

Of note is that the implementation here significantly diverges from Linux in at least one one sense: it is generally far more approximate to Darwin as the Windows Swift toolchain is a complete distribution and that includes libclang. This toolchain is also (partially) able to host the ObjC paths though the Foundation headers are not available as that is not part of the toolchain distribution.

One of the biggest issues that currently need to be resolved in this core set of changes is the toolchain installation location.

If it is helpful/reasonable, it should be possible to peel off parts of this series into individual PRs to get them merged earlier rather than later to help get more things in place (and make it easier for others to contribute as well).

One final item of note: this is currently not possible to build for most. This is reliant on a toolchain build that is newer than anything available in a prebuilt snapshot. I hope that we can get a new snapshot built in the next few days.

@compnerd compnerd force-pushed the windows branch 2 times, most recently from 50a3d0f to b832600 Compare May 15, 2023 16:40
@compnerd compnerd marked this pull request as ready for review May 22, 2023 15:15
@compnerd
Copy link
Contributor Author

@jpsim I'm happy to break this down into separate PRs. I still do not have a good solution to the HACK commits, and would love suggestions from you. The latest snapshot 5/18 should be able to build this. There is one failure that I am seeing locally due to the need to re-generate the expected results.

@jpsim
Copy link
Owner

jpsim commented Jun 13, 2023

Hi @compnerd, first sorry for the delay (laid off, job search, other priorities, etc).

Second, it's fantastic to see SourceKitten working on Windows, massive kudos 🙌.

My main point of feedback is that some changes break the non-Windows build or functionality. For example, some of the hardcoded paths (e.g. C:\Program Files\Git\cmd\git.exe, .build\checkouts\, C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin\swift.exe) apply unconditionally on all OSes, where they should only be applied to Windows.

If you don't mind, I'll cherry pick some of your commits (keeping you as author) that don't break anything in the currently supported platforms to reduce the scope of the changes you want to land in this PR. As those smaller pieces get merged in, you can rebase this one as you fix the build/functionality regressions on macOS/Linux.

@compnerd
Copy link
Contributor Author

Hi @compnerd, first sorry for the delay (laid off, job search, other priorities, etc).

Thank you for the response! Life often gets in the way, I hope that everything has worked out well now.

Second, it's fantastic to see SourceKitten working on Windows, massive kudos 🙌.

Thank you!

My main point of feedback is that some changes break the non-Windows build or functionality. For example, some of the hardcoded paths (e.g. C:\Program Files\Git\cmd\git.exe, .build\checkouts\, C:\Library\Developer\Toolchains\unknown-Asserts-development.xctoolchain\usr\bin\swift.exe) apply unconditionally on all OSes, where they should only be applied to Windows.

Absolutely on point. In fact, they are being broken even further as we speak :). They are not entirely portable, and I've tried to call them out in the commits with the "HACK" prefix. They are not ready at all for merging and actually need some guidance on how to best approach the problems there.

If you don't mind, I'll cherry pick some of your commits (keeping you as author) that don't break anything in the currently supported platforms to reduce the scope of the changes you want to land in this PR. As those smaller pieces get merged in, you can rebase this one as you fix the build/functionality regressions on macOS/Linux.

That sounds great! The changes are ordered such that the ones marked as HACK are situated on top to help splitting things that are ready from those that are not.

@jpsim
Copy link
Owner

jpsim commented Jun 14, 2023

I've cherry-picked and merged many of the commits from this PR in #779.

Could you please rebase this PR with the remaining changes and I can add feedback inline?

Or you can open new PRs for incremental changes as they're ready if you prefer, up to you.

@@ -20,6 +20,210 @@ private let sourcekitStrings: [String] = {
}
fatalError("Could not find or load libsourcekitdInProc.so")
}()
#elseif os(Windows)
Copy link
Owner

Choose a reason for hiding this comment

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

This is now defined in SourceKitStrings+Windows.swift as of #779.

@compnerd
Copy link
Contributor Author

Rebased the changes. I think that we need to add a helper to locate the executable (basically implement a poor which). The location of swiftc.exe is going to change soon as well. This will break this and will need to be aware of looking up the paths by searching Path.

compnerd and others added 10 commits June 14, 2023 09:59
Xcode is only available on the macOS platform.  Rather than enumerating
all the other OSes, simply limit the test cases to that platform.
This adds Windows specific handling for dynamic symbol resolution into
a library.  The `dlopen` and `dlsym` APIs are not portable as they are
part of POSIX rather than the C standard.  Use the Windows equivalents
of `LoadLibraryW` and `GetProcAddress`.

TODO: this requires further refinement to detect the Swift toolchain
installation.
Ensure that we normalise the path when we substitute the path into the
results to properly remove the basename of the fixture.
`\` must be escaped for valid JSON.  Ensure that we properly escape the
character and form the reference string with the proper representation
of the path.
Use the proper spelling for the filepath so that we can remove the
basename programmatically subsequently.
This ensures that we are able to remove the basename on the included
files when matching the golden references.
Add the content to get the reference data for Windows.  This is required
to run the tests on Windows.
Windows does not have `/usr/bin/env` nor an approximate.  We cannot
invoke `git` without an absolute path.
We match a string by a subpath.  Because we cannot formulate the string
using composition, add the expected spelling.
We should figure out the proper way to identify the toolchain
installation rather than assuming the install location.
Comment on lines +25 to +26
let cloneArguments = ["clone", "https://github.com/Carthage/Commandant.git"]
let cloneResult = Exec.run("C:\\Program Files\\Git\\cmd\\git.exe", cloneArguments, currentDirectory: temporaryURL.path)
Copy link
Owner

Choose a reason for hiding this comment

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

Can you introduce new functions in Exec.swift that abstracts platform-specific execution of the commands we use in SourceKitten? For example:

extension Exec {
    static func runGit(_ arguments: String...,
                       currentDirectory: String = FileManager.default.currentDirectoryPath,
                       stderr: Stderr = .inherit) -> Results {
#if os(Windows)
        let command = #"C:\Program Files\Git\cmd\git.exe"#
#else
        let command = "/usr/bin/env"
        let arguments = ["git"] + arguments
#endif
        return run(command, arguments, currentDirectory: currentDirectory, stderr: stderr)
    }
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I like the idea of the helper. However, I do wonder if we should replicate /usr/bin/env? I think that we want that for Windows as well, but does not have that. We could explicitly use CreateProcess but that is a lot of complexity.

Copy link
Owner

Choose a reason for hiding this comment

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

The equivalent of /usr/bin/env on windows would certainly be helpful.

Maybe some abstraction should live in https://github.com/apple/swift-system? In any case I’m happy to consider it out of scope for initial Windows support in SourceKitten.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I put together https://github.com/compnerd/Finder and we can simply do which("git") and which("swiftc") - it respects PATHEXT which means that we don't even need to worry about the suffix.

@@ -50,7 +50,7 @@ public struct Module {
return nodeModuleName == spmName
}
let inputs = node["inputs"]?.array(of: String.self) ?? []
return inputs.allSatisfy({ !$0.contains(".build/checkouts/") }) && !nodeModuleName.hasSuffix("Tests")
return inputs.allSatisfy({ !$0.contains(".build\\checkouts\\") }) && !nodeModuleName.hasSuffix("Tests")
Copy link
Owner

Choose a reason for hiding this comment

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

Suggested change
return inputs.allSatisfy({ !$0.contains(".build\\checkouts\\") }) && !nodeModuleName.hasSuffix("Tests")
#if os(Windows)
let checkoutsPath = #".build\checkouts\"#
#else
let checkoutsPath = ".build/checkouts/"
#endif
return inputs.allSatisfy({ !$0.contains(checkoutsPath) }) && !nodeModuleName.hasSuffix("Tests")

@@ -30,8 +30,14 @@ class ClangTranslationUnitTests: XCTestCase {
}

private func compare(clangFixture fixture: String) {
let unit = ClangTranslationUnit(headerFiles: [fixturesDirectory + fixture + ".h"],
compilerArguments: ["-x", "objective-c", "-isysroot", sdkPath(), "-I", fixturesDirectory])
let path = URL(fileURLWithPath: fixturesDirectory + fixture + ".h")
Copy link
Owner

Choose a reason for hiding this comment

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

This pattern is used a lot now: URL(fileURLWithPath: ...).standardizedFileURL.withUnsafeFileSystemRepresentation { String(cString: $0!) }

Can you add some sort of shared helper for this? Open to API design ideas, but as a strawman: FileUtils.unsafeSystemPath(for path: String) -> String

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't think that the unsafeSystemPath makes sense, perhaps FileUtils.nativePathRepresentation? The withUnsafeFileSystemRepresentation is because the closure receives a UnsafePointer<CChar>, but we are transacting in String at this layer.

let jsonString = String(describing: jsonString).replacingOccurrences(of: escapedFixturesDirectory, with: "")
let escapedFixturesDirectory = URL(fileURLWithPath: rootDirectory).standardizedFileURL.withUnsafeFileSystemRepresentation {
String(cString: $0!) + FileManager.pathSeparator
}.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "/", with: "\\/")
Copy link
Owner

Choose a reason for hiding this comment

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

Same here, could you please use raw string literals to avoid having to escape \ which adds a lot of noise?

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.

None yet

2 participants