From 306a80a00a3e46e1554c13f47f5da448ed483536 Mon Sep 17 00:00:00 2001 From: JP Simard Date: Tue, 17 Nov 2015 18:15:13 -0800 Subject: [PATCH] Fix an issue where logs would be printed asynchronously. Fixes #200. --- CHANGELOG.md | 4 +++ .../Extensions/QueuedPrint.swift | 34 +++++++++++++++++++ .../Models/Configuration.swift | 13 ++++--- Source/swiftlint/LintCommand.swift | 17 +++++----- Source/swiftlint/main.swift | 3 +- SwiftLint.xcodeproj/project.pbxproj | 4 +++ 6 files changed, 58 insertions(+), 17 deletions(-) create mode 100644 Source/SwiftLintFramework/Extensions/QueuedPrint.swift diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b77df1a3..92ac2aded 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,10 @@ [Mickael Morier](https://github.com/mmorier) [#135](https://github.com/realm/SwiftLint/issues/135) +* Fix an issue where logs would be printed asynchronously over each other. + [JP Simard](https://github.com/jpsim) + [#200](https://github.com/realm/SwiftLint/issues/200) + ## 0.3.0: Wrinkly Rules diff --git a/Source/SwiftLintFramework/Extensions/QueuedPrint.swift b/Source/SwiftLintFramework/Extensions/QueuedPrint.swift new file mode 100644 index 000000000..978649375 --- /dev/null +++ b/Source/SwiftLintFramework/Extensions/QueuedPrint.swift @@ -0,0 +1,34 @@ +// +// QueuedPrint.swift +// SwiftLint +// +// Created by JP Simard on 2015-11-17. +// Copyright © 2015 Realm. All rights reserved. +// + +import Foundation + +private let outputQueue: dispatch_queue_t = { + let queue = dispatch_queue_create("io.realm.swiftlint.outputQueue", DISPATCH_QUEUE_SERIAL) + dispatch_set_target_queue(queue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) + + atexit_b { + dispatch_barrier_sync(queue) {} + } + + return queue +}() + +/// A thread-safe version of Swift's standard print(). +public func queuedPrint(object: T) { + dispatch_async(outputQueue) { + print(object, terminator: "") + } +} + +/// A thread-safe, newline-terminated version of fputs(..., stderr). +public func queuedPrintError(string: String) { + dispatch_async(outputQueue) { + fputs(string + "\n", stderr) + } +} diff --git a/Source/SwiftLintFramework/Models/Configuration.swift b/Source/SwiftLintFramework/Models/Configuration.swift index fa2dfe892..75ee38bc3 100644 --- a/Source/SwiftLintFramework/Models/Configuration.swift +++ b/Source/SwiftLintFramework/Models/Configuration.swift @@ -57,10 +57,10 @@ public struct Configuration { let invalidRules = disabledRules.filter({ !validRuleIdentifiers.contains($0) }) if !invalidRules.isEmpty { for invalidRule in invalidRules { - fputs("config error: '\(invalidRule)' is not a valid rule identifier\n", stderr) + queuedPrintError("config error: '\(invalidRule)' is not a valid rule identifier") } let listOfValidRuleIdentifiers = validRuleIdentifiers.joinWithSeparator("\n") - fputs("Valid rule identifiers:\n\(listOfValidRuleIdentifiers)\n", stderr) + queuedPrintError("Valid rule identifiers:\n\(listOfValidRuleIdentifiers)") } // Validate that rule identifiers aren't listed multiple times @@ -71,10 +71,9 @@ public struct Configuration { accu[element] = accu[element]?.successor() ?? 1 return accu }.filter { $0.1 > 1 } - for duplicateRule in duplicateRules { - fputs("config error: '\(duplicateRule.0)' is listed \(duplicateRule.1) times\n", - stderr) - } + queuedPrintError(duplicateRules.map { rule in + "config error: '\(rule.0)' is listed \(rule.1) times" + }.joinWithSeparator("\n")) return nil } self.disabledRules = validDisabledRules @@ -111,7 +110,7 @@ public struct Configuration { let yamlContents = try NSString(contentsOfFile: fullPath, encoding: NSUTF8StringEncoding) as String if let _ = Configuration(yaml: yamlContents) { - fputs("Loading configuration from '\(path)'\n", stderr) + queuedPrintError("Loading configuration from '\(path)'") self.init(yaml: yamlContents)! return } diff --git a/Source/swiftlint/LintCommand.swift b/Source/swiftlint/LintCommand.swift index 5649ace69..064903488 100644 --- a/Source/swiftlint/LintCommand.swift +++ b/Source/swiftlint/LintCommand.swift @@ -51,9 +51,9 @@ struct LintCommand: CommandType { .filter({ !configuration.excluded.flatMap(filesToLintAtPath).contains($0) }) + configuration.included.flatMap(filesToLintAtPath) if path.isEmpty { - fputs("Linting Swift files in current working directory\n", stderr) + queuedPrintError("Linting Swift files in current working directory") } else { - fputs("Linting Swift files at path \(path)\n", stderr) + queuedPrintError("Linting Swift files at path \(path)") } let files = filesToLint.flatMap(File.init) if !files.isEmpty { @@ -70,7 +70,7 @@ struct LintCommand: CommandType { for (index, file) in files.enumerate() { if let path = file.path { let filename = (path as NSString).lastPathComponent - fputs("Linting '\(filename)' (\(index + 1)/\(files.count))\n", stderr) + queuedPrintError("Linting '\(filename)' (\(index + 1)/\(files.count))") } let linter = Linter(file: file, configuration: configuration) let currentViolations = linter.styleViolations @@ -79,22 +79,21 @@ struct LintCommand: CommandType { if reporter.isRealtime { let report = reporter.generateReport(currentViolations) if !report.isEmpty { - print(report) + queuedPrint(report) } } } if !reporter.isRealtime { - print(reporter.generateReport(violations)) + queuedPrint(reporter.generateReport(violations)) } let numberOfSeriousViolations = violations.filter({ $0.severity == .Error }).count let violationSuffix = (violations.count != 1 ? "s" : "") let filesSuffix = (files.count != 1 ? "s." : ".") - fputs( + queuedPrintError( "Done linting!" + " Found \(violations.count) violation\(violationSuffix)," + " \(numberOfSeriousViolations) serious" + - " in \(files.count) file\(filesSuffix)\n", - stderr + " in \(files.count) file\(filesSuffix)" ) if strict && !violations.isEmpty { return .Failure(CommandantError<()>.CommandError()) @@ -152,7 +151,7 @@ struct LintCommand: CommandType { case let .Success(path): return path case let .Failure(error): - fputs("\(error)\n", stderr) + queuedPrintError(String(error)) return nil } } diff --git a/Source/swiftlint/main.swift b/Source/swiftlint/main.swift index c79dec42b..6f35398ba 100644 --- a/Source/swiftlint/main.swift +++ b/Source/swiftlint/main.swift @@ -7,6 +7,7 @@ // import Commandant +import SwiftLintFramework let registry = CommandRegistry<()>() registry.register(LintCommand()) @@ -15,5 +16,5 @@ registry.register(HelpCommand(registry: registry)) registry.register(RulesCommand()) registry.main(defaultVerb: LintCommand().verb) { error in - fputs("\(error)\n", stderr) + queuedPrintError(String(error)) } diff --git a/SwiftLint.xcodeproj/project.pbxproj b/SwiftLint.xcodeproj/project.pbxproj index a3aee614d..26dc8094d 100644 --- a/SwiftLint.xcodeproj/project.pbxproj +++ b/SwiftLint.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ E812249C1B04FADC001783D2 /* Linter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E812249B1B04FADC001783D2 /* Linter.swift */; }; E816194C1BFBF35D00946723 /* SwiftDeclarationKind+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E816194B1BFBF35D00946723 /* SwiftDeclarationKind+SwiftLint.swift */; }; E816194E1BFBFEAB00946723 /* ForceTryRule.swift in Sources */ = {isa = PBXBuildFile; fileRef = E816194D1BFBFEAB00946723 /* ForceTryRule.swift */; }; + E81619531BFC162C00946723 /* QueuedPrint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E81619521BFC162C00946723 /* QueuedPrint.swift */; }; E832F10B1B17E2F5003F265F /* NSFileManager+SwiftLint.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832F10A1B17E2F5003F265F /* NSFileManager+SwiftLint.swift */; }; E832F10D1B17E725003F265F /* IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E832F10C1B17E725003F265F /* IntegrationTests.swift */; }; E83A0B351A5D382B0041A60A /* VersionCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E83A0B341A5D382B0041A60A /* VersionCommand.swift */; }; @@ -173,6 +174,7 @@ E812249B1B04FADC001783D2 /* Linter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Linter.swift; sourceTree = ""; }; E816194B1BFBF35D00946723 /* SwiftDeclarationKind+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftDeclarationKind+SwiftLint.swift"; sourceTree = ""; }; E816194D1BFBFEAB00946723 /* ForceTryRule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForceTryRule.swift; sourceTree = ""; }; + E81619521BFC162C00946723 /* QueuedPrint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueuedPrint.swift; sourceTree = ""; }; E832F10A1B17E2F5003F265F /* NSFileManager+SwiftLint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSFileManager+SwiftLint.swift"; sourceTree = ""; }; E832F10C1B17E725003F265F /* IntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IntegrationTests.swift; sourceTree = ""; }; E83A0B341A5D382B0041A60A /* VersionCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VersionCommand.swift; sourceTree = ""; }; @@ -487,6 +489,7 @@ 24E17F701B1481FF008195BE /* File+Cache.swift */, E88DEA741B09852000A66CB0 /* File+SwiftLint.swift */, E832F10A1B17E2F5003F265F /* NSFileManager+SwiftLint.swift */, + E81619521BFC162C00946723 /* QueuedPrint.swift */, E88DEA721B0984C400A66CB0 /* String+SwiftLint.swift */, E816194B1BFBF35D00946723 /* SwiftDeclarationKind+SwiftLint.swift */, E87E4A081BFB9CAE00FCFE46 /* SyntaxKind+SwiftLint.swift */, @@ -688,6 +691,7 @@ E88DEA6F1B09843F00A66CB0 /* Location.swift in Sources */, E88DEA771B098D0C00A66CB0 /* Rule.swift in Sources */, CAF900141BED8B17006A371D /* VariableNameMaxLengthRule.swift in Sources */, + E81619531BFC162C00946723 /* QueuedPrint.swift in Sources */, E87E4A051BFB927C00FCFE46 /* TrailingSemicolonRule.swift in Sources */, E88198421BEA929F00333A11 /* NestingRule.swift in Sources */, E881985B1BEA974E00333A11 /* StatementPositionRule.swift in Sources */,