@@ -136,6 +136,11 @@ public struct BuildParameters: Encodable {
136
136
/// `.swiftmodule`s.
137
137
public let enableParseableModuleInterfaces : Bool
138
138
139
+ /// Emit Swift module separately from object files. This can enable more parallelism
140
+ /// since downstream targets can begin compiling without waiting for the entire
141
+ /// module to finish building.
142
+ public let emitSwiftModuleSeparately : Bool
143
+
139
144
/// Checks if stdout stream is tty.
140
145
fileprivate let isTTY : Bool = {
141
146
guard let stream = stdoutStream. stream as? LocalFileOutputByteStream else {
@@ -165,7 +170,8 @@ public struct BuildParameters: Encodable {
165
170
enableCodeCoverage: Bool = false ,
166
171
indexStoreMode: IndexStoreMode = . auto,
167
172
enableParseableModuleInterfaces: Bool = false ,
168
- enableTestDiscovery: Bool = false
173
+ enableTestDiscovery: Bool = false ,
174
+ emitSwiftModuleSeparately: Bool = false
169
175
) {
170
176
self . dataPath = dataPath
171
177
self . configuration = configuration
@@ -180,6 +186,7 @@ public struct BuildParameters: Encodable {
180
186
self . indexStoreMode = indexStoreMode
181
187
self . enableParseableModuleInterfaces = enableParseableModuleInterfaces
182
188
self . enableTestDiscovery = enableTestDiscovery
189
+ self . emitSwiftModuleSeparately = emitSwiftModuleSeparately
183
190
}
184
191
185
192
/// Returns the compiler arguments for the index store, if enabled.
@@ -701,11 +708,148 @@ public final class SwiftTargetBuildDescription {
701
708
return args
702
709
}
703
710
711
+ /// Command-line for emitting just the Swift module.
712
+ public func emitModuleCommandLine( ) -> [ String ] {
713
+ assert ( buildParameters. emitSwiftModuleSeparately)
714
+
715
+ var result : [ String ] = [ ]
716
+ result. append ( buildParameters. toolchain. swiftCompiler. pathString)
717
+
718
+ result. append ( " -module-name " )
719
+ result. append ( target. c99name)
720
+ result. append ( " -emit-module " )
721
+ result. append ( " -emit-module-path " )
722
+ result. append ( moduleOutputPath. pathString)
723
+ result += buildParameters. toolchain. extraSwiftCFlags
724
+
725
+ result. append ( " -Xfrontend " )
726
+ result. append ( " -experimental-skip-non-inlinable-function-bodies " )
727
+ result. append ( " -force-single-frontend-invocation " )
728
+
729
+ if target. type == . library || target. type == . test {
730
+ result. append ( " -parse-as-library " )
731
+ }
732
+
733
+ // FIXME: Handle WMO
734
+
735
+ for source in target. sources. paths {
736
+ result. append ( source. pathString)
737
+ }
738
+
739
+ result. append ( " -I " )
740
+ result. append ( buildParameters. buildPath. pathString)
741
+
742
+ // FIXME: Maybe refactor these into "common args".
743
+ result += buildParameters. targetTripleArgs ( for: target)
744
+ result += [ " -swift-version " , swiftVersion. rawValue]
745
+ result += optimizationArguments
746
+ result += [ " -g " ]
747
+ result += [ " -j \( ProcessInfo . processInfo. activeProcessorCount) " ]
748
+ result += activeCompilationConditions
749
+ result += additionalFlags
750
+ result += moduleCacheArgs
751
+ result += self . buildSettingsFlags ( )
752
+
753
+ return result
754
+ }
755
+
756
+ /// Command-line for emitting the object files.
757
+ ///
758
+ /// Note: This doesn't emit the module.
759
+ public func emitObjectsCommandLine( ) -> [ String ] {
760
+ assert ( buildParameters. emitSwiftModuleSeparately)
761
+
762
+ var result : [ String ] = [ ]
763
+ result. append ( buildParameters. toolchain. swiftCompiler. pathString)
764
+
765
+ result. append ( " -module-name " )
766
+ result. append ( target. c99name)
767
+ result. append ( " -incremental " )
768
+ result. append ( " -emit-dependencies " )
769
+
770
+ result. append ( " -output-file-map " )
771
+ // FIXME: Eliminate side effect.
772
+ result. append ( try ! writeOutputFileMap ( ) . pathString)
773
+
774
+ if target. type == . library || target. type == . test {
775
+ result. append ( " -parse-as-library " )
776
+ }
777
+ // FIXME: Handle WMO
778
+
779
+ result. append ( " -c " )
780
+ for source in target. sources. paths {
781
+ result. append ( source. pathString)
782
+ }
783
+
784
+ result. append ( " -I " )
785
+ result. append ( buildParameters. buildPath. pathString)
786
+
787
+ result += buildParameters. targetTripleArgs ( for: target)
788
+ result += [ " -swift-version " , swiftVersion. rawValue]
789
+
790
+ result += buildParameters. indexStoreArguments
791
+ result += buildParameters. toolchain. extraSwiftCFlags
792
+ result += optimizationArguments
793
+ result += [ " -g " ]
794
+ result += [ " -j \( ProcessInfo . processInfo. activeProcessorCount) " ]
795
+ result += activeCompilationConditions
796
+ result += additionalFlags
797
+ result += moduleCacheArgs
798
+ result += buildParameters. sanitizers. compileSwiftFlags ( )
799
+ result += [ " -parseable-output " ]
800
+ result += self . buildSettingsFlags ( )
801
+ result += buildParameters. swiftCompilerFlags
802
+ return result
803
+ }
804
+
704
805
/// Returns true if ObjC compatibility header should be emitted.
705
806
private var shouldEmitObjCCompatibilityHeader : Bool {
706
807
return buildParameters. triple. isDarwin ( ) && target. type == . library
707
808
}
708
809
810
+ private func writeOutputFileMap( ) throws -> AbsolutePath {
811
+ let path = tempsPath. appending ( component: " output-file-map.json " )
812
+ let stream = BufferedOutputByteStream ( )
813
+
814
+ stream <<< " { \n "
815
+
816
+ let masterDepsPath = tempsPath. appending ( component: " master.swiftdeps " )
817
+
818
+ stream <<< " \" \" : { \n " ;
819
+ // FIXME: Handle WMO
820
+ stream <<< " \" swift-dependencies \" : \" " <<< masterDepsPath. pathString <<< " \" \n " ;
821
+ stream <<< " }, \n " ;
822
+
823
+ // Write out the entries for each source file.
824
+ let sources = target. sources. paths
825
+ for (idx, source) in sources. enumerated ( ) {
826
+ let object = objects [ idx]
827
+ let objectDir = object. parentDirectory
828
+
829
+ let sourceFileName = source. basenameWithoutExt
830
+ let partialModulePath = objectDir. appending ( component: sourceFileName + " ~partial.swiftmodule " )
831
+
832
+ let swiftDepsPath = objectDir. appending ( component: sourceFileName + " .swiftdeps " )
833
+
834
+ stream <<< " \" " <<< source. pathString <<< " \" : { \n "
835
+ // FIXME: Handle WMO
836
+ let depsPath = objectDir. appending ( component: sourceFileName + " .d " )
837
+ stream <<< " \" dependencies \" : \" " <<< depsPath. pathString <<< " \" , \n "
838
+ // FIXME: Need to record this deps file for processing it later.
839
+
840
+ stream <<< " \" object \" : \" " <<< object. pathString <<< " \" , \n "
841
+ stream <<< " \" swiftmodule \" : \" " <<< partialModulePath. pathString <<< " \" , \n " ;
842
+ stream <<< " \" swift-dependencies \" : \" " <<< swiftDepsPath. pathString <<< " \" \n " ;
843
+ stream <<< " } " <<< ( ( idx + 1 ) < sources. count ? " , " : " " ) <<< " \n "
844
+ }
845
+
846
+ stream <<< " } \n "
847
+
848
+ try localFileSystem. createDirectory ( path. parentDirectory, recursive: true )
849
+ try localFileSystem. writeFileContents ( path, bytes: stream. bytes)
850
+ return path
851
+ }
852
+
709
853
/// Generates the module map for the Swift target and returns its path.
710
854
private func generateModuleMap( ) throws -> AbsolutePath {
711
855
let path = tempsPath. appending ( component: moduleMapFilename)
0 commit comments