Skip to content

Add Wasm builds to pull_request.yml#1078

Open
MaxDesiatov wants to merge 2 commits into
swiftlang:mainfrom
MaxDesiatov:patch-1
Open

Add Wasm builds to pull_request.yml#1078
MaxDesiatov wants to merge 2 commits into
swiftlang:mainfrom
MaxDesiatov:patch-1

Conversation

@MaxDesiatov
Copy link
Copy Markdown
Contributor

@MaxDesiatov MaxDesiatov commented Oct 22, 2025

This ensures that builds of swift-format for WASI don't regress.

@MaxDesiatov
Copy link
Copy Markdown
Contributor Author

cc @kkebo

@kkebo
Copy link
Copy Markdown
Contributor

kkebo commented Oct 23, 2025

To build swift-format for WASI, we need some fixes.

The main blockers are:

main...kkebo:swift-format:wasm32-wasi#files_bucket

@allevato
Copy link
Copy Markdown
Member

swift-corelibs-libdispatch

Certainly happy to dust off #675 again if it makes this requirement go away.

@MaxDesiatov
Copy link
Copy Markdown
Contributor Author

Yes, removing the Dispatch dependency would be greatly appreciated! 🙏

@MaxDesiatov
Copy link
Copy Markdown
Contributor Author

  • Atomic file writing

Is this required for in-place formatting? I wonder if a better near-term option is just to disable directly in-place formatting in files, and IIUC in-place formatting in memory would still be possible when SwiftFormat is used as a library?

@kkebo
Copy link
Copy Markdown
Contributor

kkebo commented Oct 23, 2025

@MaxDesiatov

Is this required for in-place formatting?

Yes, it is.

I wonder if a better near-term option is just to disable directly in-place formatting in files

That's one option. Or, how about using non-atomic file writing for WASI?

and IIUC in-place formatting in memory would still be possible when SwiftFormat is used as a library?

What does "in-place formatting in memory" mean? IIUC swift-format with in-place disabled will only print the formatting results.

@MaxDesiatov
Copy link
Copy Markdown
Contributor Author

MaxDesiatov commented Oct 23, 2025

That's one option. Or, how about using non-atomic file writing for WASI?

IMO that's a significant change in behavior with potential for unexpected data loss. I'd prefer that to be a new CLI flag, like --in-place-nonatomic.

What does "in-place formatting in memory" mean? IIUC swift-format with in-place disabled will only print the formatting results.

I'm referring to the SwiftFormat library product, not the executable. I'm not familiar with its API, but isn't there something there would allow in-place in-memory formatting that someone could rely on, say in a browser editor or something like that?

@allevato
Copy link
Copy Markdown
Member

I'm referring to the SwiftFormat library product, not the executable. I'm not familiar with its API, but isn't there something there would allow in-place in-memory formatting that someone could rely on, say in a browser editor or something like that?

SwiftFormat the library API doesn't write files at all, directly. The various APIs can take either a URL to read from or a String with the source as inputs, and they write the output by calling TextOutputStream.write, so the behavior is based on whatever the caller's abstraction does.

Only the swift-format executable product needs atomic file writes.

@MaxDesiatov
Copy link
Copy Markdown
Contributor Author

MaxDesiatov commented Oct 29, 2025

swift-markdown

It's cherry-picked now to release/6.2 and is included in the 0.7.1 release, so this item can now be crossed off the list.

Explicit `void` is required for functions taking no arguments when targeting Wasm.
@MaxDesiatov
Copy link
Copy Markdown
Contributor Author

Now with cmark and getInstructionsExecuted failures fixed, it's only import Dispatch causing problems https://github.com/swiftlang/swift-format/actions/runs/18917047373/job/54003074951?pr=1078

@kkebo
Copy link
Copy Markdown
Contributor

kkebo commented Feb 28, 2026

DirectoryEnumerator

Now, DirectoryEnumerator works on WASI.

Toolchains for which this change is available:

  • main: The next snapshot following main-snapshot-2026-02-24 and later
  • release/6.3: 6.3-snapshot-2026-02-26 and later

@kkebo
Copy link
Copy Markdown
Contributor

kkebo commented Feb 28, 2026

So, the current minimal required change (without tests) is like:

diff --git a/Sources/_SwiftFormatInstructionCounter/include/InstructionsExecuted.h b/Sources/_SwiftFormatInstructionCounter/include/InstructionsExecuted.h
index af9d9a4..164f441 100644
--- a/Sources/_SwiftFormatInstructionCounter/include/InstructionsExecuted.h
+++ b/Sources/_SwiftFormatInstructionCounter/include/InstructionsExecuted.h
@@ -14,4 +14,4 @@
 
 /// On macOS returns the number of instructions the process has executed since
 /// it was launched, on all other platforms returns 0.
-uint64_t getInstructionsExecuted();
+uint64_t getInstructionsExecuted(void);
diff --git a/Sources/swift-format/Frontend/FormatFrontend.swift b/Sources/swift-format/Frontend/FormatFrontend.swift
index 0e8fca9..d5e1675 100644
--- a/Sources/swift-format/Frontend/FormatFrontend.swift
+++ b/Sources/swift-format/Frontend/FormatFrontend.swift
@@ -63,7 +63,11 @@ class FormatFrontend: Frontend {
 
         if buffer != source {
           let bufferData = buffer.data(using: .utf8)!  // Conversion to UTF-8 cannot fail
+          #if !os(WASI)
           try bufferData.write(to: url, options: .atomic)
+          #else
+          // `.atomic` is not yet supported on WASI.
+          try bufferData.write(to: url)
+          #endif
         }
       } else {
         try formatter.format(
diff --git a/Sources/swift-format/Frontend/Frontend.swift b/Sources/swift-format/Frontend/Frontend.swift
index aaa24ad..587b2a8 100644
--- a/Sources/swift-format/Frontend/Frontend.swift
+++ b/Sources/swift-format/Frontend/Frontend.swift
@@ -287,12 +287,16 @@ class Frontend {
     )
 
     if parallel {
+      #if !os(WASI)
       let filesToProcess =
         FileIterator(urls: urls, followSymlinks: lintFormatOptions.followSymlinks)
         .compactMap(openAndPrepareFile)
       DispatchQueue.concurrentPerform(iterations: filesToProcess.count) { index in
         processFile(filesToProcess[index])
       }
+      #else
+      fatalError("`--parallel` is not yet implemented on WASI.")
+      #endif
     } else {
       FileIterator(urls: urls, followSymlinks: lintFormatOptions.followSymlinks)
         .lazy
diff --git a/Sources/swift-format/Utilities/StderrDiagnosticPrinter.swift b/Sources/swift-format/Utilities/StderrDiagnosticPrinter.swift
index eb4f79c..6c97f10 100644
--- a/Sources/swift-format/Utilities/StderrDiagnosticPrinter.swift
+++ b/Sources/swift-format/Utilities/StderrDiagnosticPrinter.swift
@@ -10,9 +10,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-import Dispatch
 import Foundation
 
+#if !os(WASI)
+import Dispatch
+#endif
+
 /// Manages printing of diagnostics to standard error.
 final class StderrDiagnosticPrinter {
   /// Determines how colors are used in printed diagnostics.
@@ -38,8 +41,10 @@ final class StderrDiagnosticPrinter {
     case reset = "0"
   }
 
+  #if !os(WASI)
   /// The queue used to synchronize printing uninterrupted diagnostic messages.
   private let printQueue = DispatchQueue(label: "com.apple.swift-format.StderrDiagnosticPrinter")
+  #endif
 
   /// Indicates whether colors should be used when printing diagnostics.
   private let useColors: Bool
@@ -58,7 +63,18 @@ final class StderrDiagnosticPrinter {
 
   /// Prints a diagnostic to standard error.
   func printDiagnostic(_ diagnostic: Diagnostic) {
+    #if !os(WASI)
     printQueue.sync {
+      _printDiagnostic(diagnostic)
+    }
+    #else
+    // This is safe because swift-format can't process files in parallel on WASI.
+    _printDiagnostic(diagnostic)
+    #endif
+  }
+
+  /// Prints a diagnostic to standard error.
+  private func _printDiagnostic(_ diagnostic: Diagnostic) {
     let stderr = FileHandleTextOutputStream(FileHandle.standardError)
 
     stderr.write("\(ansiSGR(.reset))\(description(of: diagnostic.location)): ")
@@ -74,7 +90,6 @@ final class StderrDiagnosticPrinter {
     }
     stderr.write("\(ansiSGR(.reset))\(ansiSGR(.bold))\(diagnostic.message)\(ansiSGR(.reset))\n")
   }
-  }
 
   /// Returns a string representation of the given diagnostic location, or a fallback string if the
   /// location was not known.

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.

3 participants