Skip to content

Commit 35f0e18

Browse files
committed
UPDATE - search for xcrun in the searchPaths to prevent having host-specific behavior.
FIX - some typos and parameter names. Merge branch 'master' into where-are-the-tools
2 parents e53c79b + 74980ca commit 35f0e18

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+639
-235
lines changed

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ Similarly, one can use the new Swift driver within Xcode by adding a custom buil
3636

3737
The new Swift driver is a work in progress, and there are numerous places for anyone with an interest to contribute! This section covers testing, miscellaneous development tips and tricks, and a rough development plan showing what work still needs to be done.
3838

39+
### Driver Documentation
40+
41+
For a conceptual overview of the driver, see [The Swift Driver, Compilation Model, and Command-Line Experience](https://github.com/apple/swift/blob/master/docs/Driver.md). To learn more about the internals, see [Driver Design & Internals](https://github.com/apple/swift/blob/master/docs/DriverInternals.rst) and [Parseable Driver Output](https://github.com/apple/swift/blob/master/docs/DriverParseableOutput.rst).
42+
3943
### Testing
4044

4145
Test using command-line SwiftPM or Xcode.
@@ -64,6 +68,28 @@ $ SWIFT_DRIVER_ENABLE_INTEGRATION_TESTS=1 \
6468
swift test --parallel
6569
```
6670

71+
#### Preparing a Linux docker for debug
72+
73+
When developing on macOS without quick access to a Linux machine, using a Linux Docker is often helpful when debugging.
74+
75+
To get a docker up and running to the following:
76+
- Install Docker for Mac.
77+
- Get the newest swift docker image `docker pull swift`.
78+
- Run the following command to start a docker
79+
```
80+
$ docker run -v /path/to/swift-driver:/home/swift-driver \
81+
--cap-add=SYS_PTRACE --security-opt seccomp=unconfined \
82+
--security-opt apparmor=unconfined -it swift:latest bash
83+
```
84+
- Install dependencies by running
85+
```
86+
$ apt-get update
87+
$ apt-get install libsqlite3-dev
88+
$ apt-get install libncurses-dev
89+
```
90+
- You can now go to `/home/swift-driver` and run `swift test --parallel` to run your tests.
91+
92+
6793
### Rebuilding `Options.swift`
6894

6995
`Options.swift`, which contains the complete set of options that can be parsed by the driver, is automatically generated from the [option tables in the Swift compiler](https://github.com/apple/swift/tree/master/include/swift/Option). If you need to regenerate `Options.swift`, you will need to [build the Swift compiler](https://github.com/apple/swift#building-swift) and then build `makeOptions` program with a `-I` that allows the generated `Options.inc` to
@@ -100,7 +126,7 @@ The goal of the new Swift driver is to provide a drop-in replacement for the exi
100126
* [x] Implement proper tokenization for response files
101127
* Compilation modes
102128
* [x] Batch mode
103-
* [ ] Whole-module-optimization mode
129+
* [x] Whole-module-optimization mode
104130
* [ ] REPL mode
105131
* [ ] Immediate mode
106132
* Features
@@ -110,6 +136,7 @@ The goal of the new Swift driver is to provide a drop-in replacement for the exi
110136
* [x] Parseable output, as used by SwiftPM
111137
* [x] Response files
112138
* [ ] Input and primary input file lists
139+
* [ ] Complete `OutputFileMap` implementation to handle all file types uniformly
113140
* Testing
114141
* [ ] Build stuff with SwiftPM or Xcode or your favorite build system, using `swift-driver`. Were the results identical? What changed?
115142
* [x] Shim in `swift-driver` so it can run the Swift repository's [driver test suite](https://github.com/apple/swift/tree/master/test/Driver).

Sources/SwiftDriver/Driver/CompilerMode.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ extension CompilerMode {
4747
return true
4848
}
4949
}
50+
51+
/// Whether this compilation mode compiles the whole target in one job.
52+
public var isSingleCompilation: Bool {
53+
switch self {
54+
case .immediate, .repl, .standardCompile, .batchCompile:
55+
return false
56+
57+
case .singleCompile:
58+
return true
59+
}
60+
}
5061
}
5162

5263
extension CompilerMode: CustomStringConvertible {

Sources/SwiftDriver/Driver/Driver.swift

Lines changed: 138 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ public struct Driver {
4343
enum Error: Swift.Error {
4444
case invalidDriverName(String)
4545
case invalidInput(String)
46+
case subcommandPassedToDriver
4647
}
4748

4849
/// The set of environment variables that are visible to the driver and
@@ -193,11 +194,15 @@ public struct Driver {
193194
diagnosticsHandler: @escaping DiagnosticsEngine.DiagnosticsHandler = Driver.stderrDiagnosticsHandler
194195
) throws {
195196
self.env = env
196-
197-
// FIXME: Determine if we should run as subcommand.
198197

199198
self.diagnosticEngine = DiagnosticsEngine(handlers: [diagnosticsHandler])
199+
200+
if case .subcommand = try Self.invocationRunMode(forArgs: args).mode {
201+
throw Error.subcommandPassedToDriver
202+
}
203+
200204
var args = try Self.expandResponseFiles(args, diagnosticsEngine: self.diagnosticEngine)[...]
205+
201206
self.driverKind = try Self.determineDriverKind(args: &args)
202207
self.optionTable = OptionTable()
203208
self.parsedOptions = try optionTable.parse(Array(args))
@@ -284,16 +289,118 @@ public struct Driver {
284289
self.enabledSanitizers = try Self.parseSanitizerArgValues(&parsedOptions, diagnosticEngine: diagnosticEngine, toolchain: toolchain, targetTriple: targetTriple)
285290

286291
// Supplemental outputs.
287-
self.dependenciesFilePath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .dependencies, isOutput: .emitDependencies, outputPath: .emitDependenciesPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
288-
self.referenceDependenciesFilePath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .swiftDeps, isOutput: .emitReferenceDependencies, outputPath: .emitReferenceDependenciesPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
289-
self.serializedDiagnosticsFilePath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .diagnostics, isOutput: .serializeDiagnostics, outputPath: .serializeDiagnosticsPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
292+
self.dependenciesFilePath = try Self.computeSupplementaryOutputPath(
293+
&parsedOptions, type: .dependencies, isOutput: .emitDependencies,
294+
outputPath: .emitDependenciesPath,
295+
compilerOutputType: compilerOutputType,
296+
compilerMode: compilerMode,
297+
outputFileMap: self.outputFileMap,
298+
moduleName: moduleName)
299+
self.referenceDependenciesFilePath = try Self.computeSupplementaryOutputPath(
300+
&parsedOptions, type: .swiftDeps, isOutput: .emitReferenceDependencies,
301+
outputPath: .emitReferenceDependenciesPath,
302+
compilerOutputType: compilerOutputType,
303+
compilerMode: compilerMode,
304+
outputFileMap: self.outputFileMap,
305+
moduleName: moduleName)
306+
self.serializedDiagnosticsFilePath = try Self.computeSupplementaryOutputPath(
307+
&parsedOptions, type: .diagnostics, isOutput: .serializeDiagnostics,
308+
outputPath: .serializeDiagnosticsPath,
309+
compilerOutputType: compilerOutputType,
310+
compilerMode: compilerMode,
311+
outputFileMap: self.outputFileMap,
312+
moduleName: moduleName)
290313
// FIXME: -fixits-output-path
291-
self.objcGeneratedHeaderPath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .objcHeader, isOutput: .emitObjcHeader, outputPath: .emitObjcHeaderPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
292-
self.loadedModuleTracePath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .moduleTrace, isOutput: .emitLoadedModuleTrace, outputPath: .emitLoadedModuleTracePath, compilerOutputType: compilerOutputType, moduleName: moduleName)
293-
self.tbdPath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .tbd, isOutput: .emitTbd, outputPath: .emitTbdPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
294-
self.moduleDocOutputPath = try Self.computeModuleDocOutputPath(&parsedOptions, moduleOutputPath: self.moduleOutput?.outputPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
295-
self.swiftInterfacePath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .swiftInterface, isOutput: .emitModuleInterface, outputPath: .emitModuleInterfacePath, compilerOutputType: compilerOutputType, moduleName: moduleName)
296-
self.optimizationRecordPath = try Self.computeSupplementaryOutputPath(&parsedOptions, type: .optimizationRecord, isOutput: .saveOptimizationRecord, outputPath: .saveOptimizationRecordPath, compilerOutputType: compilerOutputType, moduleName: moduleName)
314+
self.objcGeneratedHeaderPath = try Self.computeSupplementaryOutputPath(
315+
&parsedOptions, type: .objcHeader, isOutput: .emitObjcHeader,
316+
outputPath: .emitObjcHeaderPath,
317+
compilerOutputType: compilerOutputType,
318+
compilerMode: compilerMode,
319+
outputFileMap: self.outputFileMap,
320+
moduleName: moduleName)
321+
self.loadedModuleTracePath = try Self.computeSupplementaryOutputPath(
322+
&parsedOptions, type: .moduleTrace, isOutput: .emitLoadedModuleTrace,
323+
outputPath: .emitLoadedModuleTracePath,
324+
compilerOutputType: compilerOutputType,
325+
compilerMode: compilerMode,
326+
outputFileMap: self.outputFileMap,
327+
moduleName: moduleName)
328+
self.tbdPath = try Self.computeSupplementaryOutputPath(
329+
&parsedOptions, type: .tbd, isOutput: .emitTbd,
330+
outputPath: .emitTbdPath,
331+
compilerOutputType: compilerOutputType,
332+
compilerMode: compilerMode,
333+
outputFileMap: self.outputFileMap,
334+
moduleName: moduleName)
335+
self.moduleDocOutputPath = try Self.computeModuleDocOutputPath(
336+
&parsedOptions, moduleOutputPath: self.moduleOutput?.outputPath,
337+
compilerOutputType: compilerOutputType,
338+
compilerMode: compilerMode,
339+
outputFileMap: self.outputFileMap,
340+
moduleName: moduleName)
341+
self.swiftInterfacePath = try Self.computeSupplementaryOutputPath(
342+
&parsedOptions, type: .swiftInterface, isOutput: .emitModuleInterface,
343+
outputPath: .emitModuleInterfacePath,
344+
compilerOutputType: compilerOutputType,
345+
compilerMode: compilerMode,
346+
outputFileMap: self.outputFileMap,
347+
moduleName: moduleName)
348+
self.optimizationRecordPath = try Self.computeSupplementaryOutputPath(
349+
&parsedOptions, type: .optimizationRecord,
350+
isOutput: .saveOptimizationRecord,
351+
outputPath: .saveOptimizationRecordPath,
352+
compilerOutputType: compilerOutputType,
353+
compilerMode: compilerMode,
354+
outputFileMap: self.outputFileMap,
355+
moduleName: moduleName)
356+
}
357+
}
358+
359+
extension Driver {
360+
361+
public enum InvocationRunMode: Equatable {
362+
case normal(isRepl: Bool)
363+
case subcommand(String)
364+
}
365+
366+
/// Determines whether the given arguments constitute a normal invocation,
367+
/// or whether they invoke a subcommand.
368+
///
369+
/// Returns the invocation mode along with the arguments modified for that mode.
370+
public static func invocationRunMode(
371+
forArgs args: [String]
372+
) throws -> (mode: InvocationRunMode, args: [String]) {
373+
374+
assert(!args.isEmpty)
375+
376+
let execName = try VirtualPath(path: args[0]).basenameWithoutExt
377+
378+
// If we are not run as 'swift' or there are no program arguments, always invoke as normal.
379+
guard execName == "swift", args.count > 1 else { return (.normal(isRepl: false), args) }
380+
381+
// Otherwise, we have a program argument.
382+
let firstArg = args[1]
383+
384+
// If it looks like an option or a path, then invoke in interactive mode with the arguments as given.
385+
if firstArg.hasPrefix("-") || firstArg.hasPrefix("/") || firstArg.contains(".") {
386+
return (.normal(isRepl: false), args)
387+
}
388+
389+
// Otherwise, we should have some sort of subcommand.
390+
391+
var updatedArgs = args
392+
393+
// If it is the "built-in" 'repl', then use the normal driver.
394+
if firstArg == "repl" {
395+
updatedArgs.remove(at: 1)
396+
return (.normal(isRepl: true), updatedArgs)
397+
}
398+
399+
let subcommand = "swift-\(firstArg)"
400+
401+
updatedArgs.replaceSubrange(0...1, with: [subcommand])
402+
403+
return (.subcommand(subcommand), updatedArgs)
297404
}
298405
}
299406

@@ -1249,11 +1356,11 @@ extension Driver {
12491356
isOutput: Option?,
12501357
outputPath: Option,
12511358
compilerOutputType: FileType?,
1359+
compilerMode: CompilerMode,
1360+
outputFileMap: OutputFileMap?,
12521361
moduleName: String,
12531362
patternOutputFile: VirtualPath? = nil
12541363
) throws -> VirtualPath? {
1255-
// FIXME: Do we need to check the output file map?
1256-
12571364
// If there is an explicit argument for the output path, use that
12581365
if let outputPathArg = parsedOptions.getLastArgument(outputPath) {
12591366
// Consume the isOutput argument
@@ -1268,6 +1375,14 @@ extension Driver {
12681375
return nil
12691376
}
12701377

1378+
// If this is a single-file compile and there is an entry in the
1379+
// output file map, use that.
1380+
if compilerMode.isSingleCompilation,
1381+
let singleOutputPath = outputFileMap?.existingOutputForSingleInput(
1382+
outputType: type) {
1383+
return singleOutputPath
1384+
}
1385+
12711386
// If there is an output argument, derive the name from there.
12721387
if let outputPathArg = parsedOptions.getLastArgument(.o) {
12731388
let path = try VirtualPath(path: outputPathArg.asSingle)
@@ -1288,10 +1403,10 @@ extension Driver {
12881403
_ parsedOptions: inout ParsedOptions,
12891404
moduleOutputPath: VirtualPath?,
12901405
compilerOutputType: FileType?,
1406+
compilerMode: CompilerMode,
1407+
outputFileMap: OutputFileMap?,
12911408
moduleName: String
12921409
) throws -> VirtualPath? {
1293-
// FIXME: Do we need to check the output file map?
1294-
12951410
// If there is an explicit argument for the output path, use that
12961411
if let outputPathArg = parsedOptions.getLastArgument(.emitModuleDocPath) {
12971412
// Consume -emit-module-doc if it's there.
@@ -1300,6 +1415,14 @@ extension Driver {
13001415
return try VirtualPath(path: outputPathArg.asSingle)
13011416
}
13021417

1418+
// If this is a single-file compile and there is an entry in the
1419+
// output file map, use that.
1420+
if compilerMode.isSingleCompilation,
1421+
let singleOutputPath = outputFileMap?.existingOutputForSingleInput(
1422+
outputType: .swiftDocumentation) {
1423+
return singleOutputPath
1424+
}
1425+
13031426
// If there's a known module output path, put the .swiftdoc file next
13041427
// to it.
13051428
if let moduleOutputPath = moduleOutputPath {

Sources/SwiftDriver/Driver/DriverKind.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
12-
/// Describes which mode the driver is in, which dictates
12+
13+
/// Describes which mode the driver is in.
1314
public enum DriverKind {
1415
case interactive
1516
case batch

Sources/SwiftDriver/Driver/OutputFileMap.swift

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,21 +31,12 @@ public struct OutputFileMap: Equatable {
3131
return output
3232
}
3333

34-
// Create a temporary file
35-
let baseName: String
36-
switch inputFile {
37-
case .absolute(let path):
38-
baseName = path.basenameWithoutExt
39-
case .relative(let path), .temporary(let path):
40-
baseName = path.basenameWithoutExt
41-
case .standardInput:
42-
baseName = ""
43-
case .standardOutput:
34+
if inputFile == .standardOutput {
4435
fatalError("Standard output cannot be an input file")
4536
}
4637

4738
// Form the virtual path.
48-
return .temporary(RelativePath(baseName.appendingFileTypeExtension(outputType)))
39+
return .temporary(RelativePath(inputFile.basenameWithoutExt.appendingFileTypeExtension(outputType)))
4940
}
5041

5142
public func existingOutput(inputFile: VirtualPath, outputType: FileType) -> VirtualPath? {
@@ -113,13 +104,17 @@ fileprivate struct OutputFileMapJSON: Codable {
113104
case dependencies
114105
case object
115106
case swiftmodule
107+
case swiftinterface
116108
case swiftDependencies = "swift-dependencies"
109+
case diagnostics
117110
}
118111

119112
let dependencies: String?
120113
let object: String?
121114
let swiftmodule: String?
115+
let swiftinterface: String?
122116
let swiftDependencies: String?
117+
let diagnostics: String?
123118
}
124119

125120
/// The parsed entires
@@ -146,7 +141,9 @@ fileprivate struct OutputFileMapJSON: Codable {
146141
map[.dependencies] = entry.dependencies
147142
map[.object] = entry.object
148143
map[.swiftModule] = entry.swiftmodule
144+
map[.swiftInterface] = entry.swiftinterface
149145
map[.swiftDeps] = entry.swiftDependencies
146+
map[.diagnostics] = entry.diagnostics
150147

151148
result[input] = try map.mapValues(VirtualPath.init(path:))
152149
}
@@ -168,7 +165,9 @@ fileprivate struct OutputFileMapJSON: Codable {
168165
dependencies: outputs[.dependencies]?.name,
169166
object: outputs[.object]?.name,
170167
swiftmodule: outputs[.swiftModule]?.name,
171-
swiftDependencies: outputs[.swiftDeps]?.name)
168+
swiftinterface: outputs[.swiftInterface]?.name,
169+
swiftDependencies: outputs[.swiftDeps]?.name,
170+
diagnostics: outputs[.diagnostics]?.name)
172171
}
173172
return Self(entries: Dictionary(uniqueKeysWithValues: entries.map(convert(entry:))))
174173
}

0 commit comments

Comments
 (0)