From 274c200fd784439bfb99e01de150bb2bb6707443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihat=20Gu=CC=88ndu=CC=88z?= Date: Tue, 23 Feb 2016 15:44:45 +0100 Subject: [PATCH 1/5] Add CommandLine via Carthage --- BartyCrouch.xcodeproj/project.pbxproj | 31 + Cartfile | 2 + Cartfile.resolved | 1 + Carthage/Checkouts/CommandLine/.gitignore | 19 + Carthage/Checkouts/CommandLine/.travis.yml | 5 + Carthage/Checkouts/CommandLine/Carthage/Build | 1 + .../CommandLine.xcodeproj/project.pbxproj | 537 +++++++++++++ .../contents.xcworkspacedata | 7 + .../xcschemes/CommandLine.xcscheme | 114 +++ .../CommandLine/CommandLine/CommandLine.swift | 300 ++++++++ .../CommandLine/CommandLine/Info.plist | 28 + .../CommandLine/CommandLine/Option.swift | 271 +++++++ .../CommandLine/StringExtensions.swift | 162 ++++ .../CommandLineTests/CommandLineTests.swift | 723 ++++++++++++++++++ .../CommandLine/CommandLineTests/Info.plist | 26 + .../StringExtensionTests.swift | 135 ++++ Carthage/Checkouts/CommandLine/LICENSE | 201 +++++ Carthage/Checkouts/CommandLine/README.md | 146 ++++ 18 files changed, 2709 insertions(+) create mode 100644 Cartfile create mode 100644 Cartfile.resolved create mode 100644 Carthage/Checkouts/CommandLine/.gitignore create mode 100644 Carthage/Checkouts/CommandLine/.travis.yml create mode 120000 Carthage/Checkouts/CommandLine/Carthage/Build create mode 100644 Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/project.pbxproj create mode 100644 Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/xcshareddata/xcschemes/CommandLine.xcscheme create mode 100644 Carthage/Checkouts/CommandLine/CommandLine/CommandLine.swift create mode 100644 Carthage/Checkouts/CommandLine/CommandLine/Info.plist create mode 100644 Carthage/Checkouts/CommandLine/CommandLine/Option.swift create mode 100644 Carthage/Checkouts/CommandLine/CommandLine/StringExtensions.swift create mode 100644 Carthage/Checkouts/CommandLine/CommandLineTests/CommandLineTests.swift create mode 100644 Carthage/Checkouts/CommandLine/CommandLineTests/Info.plist create mode 100644 Carthage/Checkouts/CommandLine/CommandLineTests/StringExtensionTests.swift create mode 100644 Carthage/Checkouts/CommandLine/LICENSE create mode 100644 Carthage/Checkouts/CommandLine/README.md diff --git a/BartyCrouch.xcodeproj/project.pbxproj b/BartyCrouch.xcodeproj/project.pbxproj index 9b7987d..c8f455b 100644 --- a/BartyCrouch.xcodeproj/project.pbxproj +++ b/BartyCrouch.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 820649D31C7CA71E009E501E /* CommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 820649D01C7CA71E009E501E /* CommandLine.swift */; }; + 820649D41C7CA71E009E501E /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = 820649D11C7CA71E009E501E /* Option.swift */; }; + 820649D51C7CA71E009E501E /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 820649D21C7CA71E009E501E /* StringExtensions.swift */; }; 821DED3A1C70CFBB00B8353B /* StringsFilesSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821DED391C70CFBB00B8353B /* StringsFilesSearch.swift */; }; 821DED3C1C70D04B00B8353B /* StringsFilesSearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 821DED3B1C70D04B00B8353B /* StringsFilesSearchTests.swift */; }; 828ED5351C710C2B00E0E947 /* StringsFileUpdater.swift in Sources */ = {isa = PBXBuildFile; fileRef = 82CDE2DF1C6BF35500055FE6 /* StringsFileUpdater.swift */; }; @@ -46,6 +49,10 @@ /* Begin PBXFileReference section */ 8203A2971C6D159D00BCE479 /* NewExample.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = NewExample.strings; sourceTree = ""; }; 8203A2981C6D159D00BCE479 /* OldExample.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = OldExample.strings; sourceTree = ""; }; + 820649CD1C7CA636009E501E /* Cartfile */ = {isa = PBXFileReference; lastKnownFileType = text; path = Cartfile; sourceTree = ""; }; + 820649D01C7CA71E009E501E /* CommandLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandLine.swift; sourceTree = ""; }; + 820649D11C7CA71E009E501E /* Option.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Option.swift; sourceTree = ""; }; + 820649D21C7CA71E009E501E /* StringExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = ""; }; 821DED391C70CFBB00B8353B /* StringsFilesSearch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringsFilesSearch.swift; sourceTree = ""; }; 821DED3B1C70D04B00B8353B /* StringsFilesSearchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringsFilesSearchTests.swift; sourceTree = ""; }; 821DED3F1C70D11F00B8353B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Example.storyboard; sourceTree = ""; }; @@ -100,13 +107,34 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 820649CE1C7CA6FA009E501E /* Carthage */ = { + isa = PBXGroup; + children = ( + 820649CF1C7CA705009E501E /* CommandLine */, + ); + path = Carthage; + sourceTree = ""; + }; + 820649CF1C7CA705009E501E /* CommandLine */ = { + isa = PBXGroup; + children = ( + 820649D01C7CA71E009E501E /* CommandLine.swift */, + 820649D11C7CA71E009E501E /* Option.swift */, + 820649D21C7CA71E009E501E /* StringExtensions.swift */, + ); + name = CommandLine; + path = Checkouts/CommandLine/CommandLine; + sourceTree = ""; + }; 82CDE23C1C6ABB3300055FE6 = { isa = PBXGroup; children = ( + 820649CD1C7CA636009E501E /* Cartfile */, 82CDE2611C6ABBF500055FE6 /* Sources */, 82CDE2681C6ABC0200055FE6 /* Tests */, 82CDE2741C6ABE5D00055FE6 /* BartyCrouch CLI */, 82CDE2471C6ABB3300055FE6 /* Products */, + 820649CE1C7CA6FA009E501E /* Carthage */, ); sourceTree = ""; }; @@ -369,9 +397,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 820649D31C7CA71E009E501E /* CommandLine.swift in Sources */, 828ED5361C710C2F00E0E947 /* IBToolCommander.swift in Sources */, 828ED5351C710C2B00E0E947 /* StringsFileUpdater.swift in Sources */, + 820649D51C7CA71E009E501E /* StringExtensions.swift in Sources */, 828ED5371C710C3100E0E947 /* StringsFilesSearch.swift in Sources */, + 820649D41C7CA71E009E501E /* Option.swift in Sources */, 82CDE2761C6ABE5D00055FE6 /* main.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Cartfile b/Cartfile new file mode 100644 index 0000000..37bcced --- /dev/null +++ b/Cartfile @@ -0,0 +1,2 @@ +# A pure Swift library for creating command-line interfaces +github "jatoben/CommandLine" \ No newline at end of file diff --git a/Cartfile.resolved b/Cartfile.resolved new file mode 100644 index 0000000..b826525 --- /dev/null +++ b/Cartfile.resolved @@ -0,0 +1 @@ +github "jatoben/CommandLine" "v2.0.0" diff --git a/Carthage/Checkouts/CommandLine/.gitignore b/Carthage/Checkouts/CommandLine/.gitignore new file mode 100644 index 0000000..f862246 --- /dev/null +++ b/Carthage/Checkouts/CommandLine/.gitignore @@ -0,0 +1,19 @@ +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate + diff --git a/Carthage/Checkouts/CommandLine/.travis.yml b/Carthage/Checkouts/CommandLine/.travis.yml new file mode 100644 index 0000000..3ff7524 --- /dev/null +++ b/Carthage/Checkouts/CommandLine/.travis.yml @@ -0,0 +1,5 @@ +language: objective-c # this is a lie +xcode_project: CommandLine.xcodeproj +xcode_scheme: CommandLine +script: xcodebuild -scheme CommandLine test +osx_image: xcode7 diff --git a/Carthage/Checkouts/CommandLine/Carthage/Build b/Carthage/Checkouts/CommandLine/Carthage/Build new file mode 120000 index 0000000..76f9ba2 --- /dev/null +++ b/Carthage/Checkouts/CommandLine/Carthage/Build @@ -0,0 +1 @@ +../../../../Carthage/Build \ No newline at end of file diff --git a/Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/project.pbxproj b/Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/project.pbxproj new file mode 100644 index 0000000..bff7140 --- /dev/null +++ b/Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/project.pbxproj @@ -0,0 +1,537 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 5D06884F19674D7D00BBFAAC /* CommandLine.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5D06884319674D7D00BBFAAC /* CommandLine.framework */; }; + 5D06885619674D7D00BBFAAC /* CommandLineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D06885519674D7D00BBFAAC /* CommandLineTests.swift */; }; + 5D06886019674DA600BBFAAC /* CommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D06885F19674DA600BBFAAC /* CommandLine.swift */; }; + 5D06886119674DA600BBFAAC /* CommandLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D06885F19674DA600BBFAAC /* CommandLine.swift */; }; + 5D068867196A212A00BBFAAC /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D068866196A212A00BBFAAC /* Option.swift */; }; + 5D068868196A212A00BBFAAC /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D068866196A212A00BBFAAC /* Option.swift */; }; + 5D449A18198B015E00F9A8A6 /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D449A17198B015E00F9A8A6 /* StringExtensions.swift */; }; + 5D449B5C198B40B600F9A8A6 /* StringExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D449A17198B015E00F9A8A6 /* StringExtensions.swift */; }; + 5D8DC809198D8BA0007CD61B /* StringExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D8DC808198D8BA0007CD61B /* StringExtensionTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 5D06885019674D7D00BBFAAC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5D06883A19674D7D00BBFAAC /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5D06884219674D7D00BBFAAC; + remoteInfo = CommandLine; + }; + 5D0688621969D22E00BBFAAC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5D06883A19674D7D00BBFAAC /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5D06884219674D7D00BBFAAC; + remoteInfo = CommandLine; + }; + 5D0688641969D22E00BBFAAC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5D06883A19674D7D00BBFAAC /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5D06884219674D7D00BBFAAC; + remoteInfo = CommandLine; + }; + 5D449B5D198B40F100F9A8A6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5D06883A19674D7D00BBFAAC /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5D06884219674D7D00BBFAAC; + remoteInfo = CommandLine; + }; + 5D449B5F198B40F100F9A8A6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5D06883A19674D7D00BBFAAC /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5D06884219674D7D00BBFAAC; + remoteInfo = CommandLine; + }; + 5D449B61198B410C00F9A8A6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5D06883A19674D7D00BBFAAC /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5D06884219674D7D00BBFAAC; + remoteInfo = CommandLine; + }; + 5D449BA7198B490500F9A8A6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5D06883A19674D7D00BBFAAC /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5D06884219674D7D00BBFAAC; + remoteInfo = CommandLine; + }; + 5D449BC3198B4ACD00F9A8A6 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5D06883A19674D7D00BBFAAC /* Project object */; + proxyType = 1; + remoteGlobalIDString = 5D06884219674D7D00BBFAAC; + remoteInfo = CommandLine; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 5D06884319674D7D00BBFAAC /* CommandLine.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CommandLine.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5D06884719674D7D00BBFAAC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5D06884E19674D7D00BBFAAC /* CommandLineTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CommandLineTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 5D06885419674D7D00BBFAAC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5D06885519674D7D00BBFAAC /* CommandLineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandLineTests.swift; sourceTree = ""; }; + 5D06885F19674DA600BBFAAC /* CommandLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandLine.swift; sourceTree = ""; }; + 5D068866196A212A00BBFAAC /* Option.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Option.swift; sourceTree = ""; }; + 5D449A17198B015E00F9A8A6 /* StringExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensions.swift; sourceTree = ""; }; + 5D449B63198B412E00F9A8A6 /* ReadMe.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = ReadMe.md; sourceTree = ""; }; + 5D8DC808198D8BA0007CD61B /* StringExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringExtensionTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5D06883F19674D7D00BBFAAC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5D06884B19674D7D00BBFAAC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5D06884F19674D7D00BBFAAC /* CommandLine.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 5D06883919674D7D00BBFAAC = { + isa = PBXGroup; + children = ( + 5D449B63198B412E00F9A8A6 /* ReadMe.md */, + 5D06884519674D7D00BBFAAC /* CommandLine */, + 5D06885219674D7D00BBFAAC /* CommandLineTests */, + 5D06884419674D7D00BBFAAC /* Products */, + ); + sourceTree = ""; + }; + 5D06884419674D7D00BBFAAC /* Products */ = { + isa = PBXGroup; + children = ( + 5D06884319674D7D00BBFAAC /* CommandLine.framework */, + 5D06884E19674D7D00BBFAAC /* CommandLineTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 5D06884519674D7D00BBFAAC /* CommandLine */ = { + isa = PBXGroup; + children = ( + 5D06885F19674DA600BBFAAC /* CommandLine.swift */, + 5D068866196A212A00BBFAAC /* Option.swift */, + 5D449A17198B015E00F9A8A6 /* StringExtensions.swift */, + 5D06884619674D7D00BBFAAC /* Supporting Files */, + ); + path = CommandLine; + sourceTree = ""; + }; + 5D06884619674D7D00BBFAAC /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 5D06884719674D7D00BBFAAC /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 5D06885219674D7D00BBFAAC /* CommandLineTests */ = { + isa = PBXGroup; + children = ( + 5D06885519674D7D00BBFAAC /* CommandLineTests.swift */, + 5D8DC808198D8BA0007CD61B /* StringExtensionTests.swift */, + 5D06885319674D7D00BBFAAC /* Supporting Files */, + ); + path = CommandLineTests; + sourceTree = ""; + }; + 5D06885319674D7D00BBFAAC /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 5D06885419674D7D00BBFAAC /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 5D06884019674D7D00BBFAAC /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 5D06884219674D7D00BBFAAC /* CommandLine */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5D06885919674D7D00BBFAAC /* Build configuration list for PBXNativeTarget "CommandLine" */; + buildPhases = ( + 5D06883E19674D7D00BBFAAC /* Sources */, + 5D06883F19674D7D00BBFAAC /* Frameworks */, + 5D06884019674D7D00BBFAAC /* Headers */, + 5D06884119674D7D00BBFAAC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CommandLine; + productName = CommandLine; + productReference = 5D06884319674D7D00BBFAAC /* CommandLine.framework */; + productType = "com.apple.product-type.framework"; + }; + 5D06884D19674D7D00BBFAAC /* CommandLineTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 5D06885C19674D7D00BBFAAC /* Build configuration list for PBXNativeTarget "CommandLineTests" */; + buildPhases = ( + 5D06884A19674D7D00BBFAAC /* Sources */, + 5D06884B19674D7D00BBFAAC /* Frameworks */, + 5D06884C19674D7D00BBFAAC /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 5D06885119674D7D00BBFAAC /* PBXTargetDependency */, + 5D0688631969D22E00BBFAAC /* PBXTargetDependency */, + 5D0688651969D22E00BBFAAC /* PBXTargetDependency */, + 5D449B5E198B40F100F9A8A6 /* PBXTargetDependency */, + 5D449B60198B40F100F9A8A6 /* PBXTargetDependency */, + 5D449B62198B410C00F9A8A6 /* PBXTargetDependency */, + 5D449BA8198B490500F9A8A6 /* PBXTargetDependency */, + 5D449BC4198B4ACD00F9A8A6 /* PBXTargetDependency */, + ); + name = CommandLineTests; + productName = CommandLineTests; + productReference = 5D06884E19674D7D00BBFAAC /* CommandLineTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 5D06883A19674D7D00BBFAAC /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0700; + LastUpgradeCheck = 0700; + ORGANIZATIONNAME = "Ben Gollmer"; + TargetAttributes = { + 5D06884219674D7D00BBFAAC = { + CreatedOnToolsVersion = 6.0; + }; + 5D06884D19674D7D00BBFAAC = { + CreatedOnToolsVersion = 6.0; + TestTargetID = 5D06884219674D7D00BBFAAC; + }; + }; + }; + buildConfigurationList = 5D06883D19674D7D00BBFAAC /* Build configuration list for PBXProject "CommandLine" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 5D06883919674D7D00BBFAAC; + productRefGroup = 5D06884419674D7D00BBFAAC /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 5D06884219674D7D00BBFAAC /* CommandLine */, + 5D06884D19674D7D00BBFAAC /* CommandLineTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 5D06884119674D7D00BBFAAC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5D06884C19674D7D00BBFAAC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 5D06883E19674D7D00BBFAAC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5D449A18198B015E00F9A8A6 /* StringExtensions.swift in Sources */, + 5D068867196A212A00BBFAAC /* Option.swift in Sources */, + 5D06886019674DA600BBFAAC /* CommandLine.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5D06884A19674D7D00BBFAAC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 5D449B5C198B40B600F9A8A6 /* StringExtensions.swift in Sources */, + 5D06885619674D7D00BBFAAC /* CommandLineTests.swift in Sources */, + 5D068868196A212A00BBFAAC /* Option.swift in Sources */, + 5D8DC809198D8BA0007CD61B /* StringExtensionTests.swift in Sources */, + 5D06886119674DA600BBFAAC /* CommandLine.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 5D06885119674D7D00BBFAAC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5D06884219674D7D00BBFAAC /* CommandLine */; + targetProxy = 5D06885019674D7D00BBFAAC /* PBXContainerItemProxy */; + }; + 5D0688631969D22E00BBFAAC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5D06884219674D7D00BBFAAC /* CommandLine */; + targetProxy = 5D0688621969D22E00BBFAAC /* PBXContainerItemProxy */; + }; + 5D0688651969D22E00BBFAAC /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5D06884219674D7D00BBFAAC /* CommandLine */; + targetProxy = 5D0688641969D22E00BBFAAC /* PBXContainerItemProxy */; + }; + 5D449B5E198B40F100F9A8A6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5D06884219674D7D00BBFAAC /* CommandLine */; + targetProxy = 5D449B5D198B40F100F9A8A6 /* PBXContainerItemProxy */; + }; + 5D449B60198B40F100F9A8A6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5D06884219674D7D00BBFAAC /* CommandLine */; + targetProxy = 5D449B5F198B40F100F9A8A6 /* PBXContainerItemProxy */; + }; + 5D449B62198B410C00F9A8A6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5D06884219674D7D00BBFAAC /* CommandLine */; + targetProxy = 5D449B61198B410C00F9A8A6 /* PBXContainerItemProxy */; + }; + 5D449BA8198B490500F9A8A6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5D06884219674D7D00BBFAAC /* CommandLine */; + targetProxy = 5D449BA7198B490500F9A8A6 /* PBXContainerItemProxy */; + }; + 5D449BC4198B4ACD00F9A8A6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 5D06884219674D7D00BBFAAC /* CommandLine */; + targetProxy = 5D449BC3198B4ACD00F9A8A6 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 5D06885719674D7D00BBFAAC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + METAL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 5D06885819674D7D00BBFAAC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.10; + METAL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 5D06885A19674D7D00BBFAAC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = CommandLine/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.github.jatoben.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 5D06885B19674D7D00BBFAAC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_VERSION = A; + INFOPLIST_FILE = CommandLine/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.github.jatoben.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; + 5D06885D19674D7D00BBFAAC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = CommandLineTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + METAL_ENABLE_DEBUG_INFO = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.github.jatoben.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 5D06885E19674D7D00BBFAAC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COMBINE_HIDPI_IMAGES = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(DEVELOPER_FRAMEWORKS_DIR)", + "$(inherited)", + ); + INFOPLIST_FILE = CommandLineTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + METAL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = "com.github.jatoben.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 5D06883D19674D7D00BBFAAC /* Build configuration list for PBXProject "CommandLine" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5D06885719674D7D00BBFAAC /* Debug */, + 5D06885819674D7D00BBFAAC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5D06885919674D7D00BBFAAC /* Build configuration list for PBXNativeTarget "CommandLine" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5D06885A19674D7D00BBFAAC /* Debug */, + 5D06885B19674D7D00BBFAAC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 5D06885C19674D7D00BBFAAC /* Build configuration list for PBXNativeTarget "CommandLineTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 5D06885D19674D7D00BBFAAC /* Debug */, + 5D06885E19674D7D00BBFAAC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 5D06883A19674D7D00BBFAAC /* Project object */; +} diff --git a/Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..75b816c --- /dev/null +++ b/Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/xcshareddata/xcschemes/CommandLine.xcscheme b/Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/xcshareddata/xcschemes/CommandLine.xcscheme new file mode 100644 index 0000000..ce177ad --- /dev/null +++ b/Carthage/Checkouts/CommandLine/CommandLine.xcodeproj/xcshareddata/xcschemes/CommandLine.xcscheme @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Carthage/Checkouts/CommandLine/CommandLine/CommandLine.swift b/Carthage/Checkouts/CommandLine/CommandLine/CommandLine.swift new file mode 100644 index 0000000..456dfce --- /dev/null +++ b/Carthage/Checkouts/CommandLine/CommandLine/CommandLine.swift @@ -0,0 +1,300 @@ +/* + * CommandLine.swift + * Copyright (c) 2014 Ben Gollmer. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Required for setlocale(3) */ +@exported import Darwin + +let ShortOptionPrefix = "-" +let LongOptionPrefix = "--" + +/* Stop parsing arguments when an ArgumentStopper (--) is detected. This is a GNU getopt + * convention; cf. https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html + */ +let ArgumentStopper = "--" + +/* Allow arguments to be attached to flags when separated by this character. + * --flag=argument is equivalent to --flag argument + */ +let ArgumentAttacher: Character = "=" + +/* An output stream to stderr; used by CommandLine.printUsage(). */ +private struct StderrOutputStream: OutputStreamType { + static let stream = StderrOutputStream() + func write(s: String) { + fputs(s, stderr) + } +} + +/** + * The CommandLine class implements a command-line interface for your app. + * + * To use it, define one or more Options (see Option.swift) and add them to your + * CommandLine object, then invoke `parse()`. Each Option object will be populated with + * the value given by the user. + * + * If any required options are missing or if an invalid value is found, `parse()` will throw + * a `ParseError`. You can then call `printUsage()` to output an automatically-generated usage + * message. + */ +public class CommandLine { + private var _arguments: [String] + private var _options: [Option] = [Option]() + + /** A ParseError is thrown if the `parse()` method fails. */ + public enum ParseError: ErrorType, CustomStringConvertible { + /** Thrown if an unrecognized argument is passed to `parse()` in strict mode */ + case InvalidArgument(String) + + /** Thrown if the value for an Option is invalid (e.g. a string is passed to an IntOption) */ + case InvalidValueForOption(Option, [String]) + + /** Thrown if an Option with required: true is missing */ + case MissingRequiredOptions([Option]) + + public var description: String { + switch self { + case let .InvalidArgument(arg): + return "Invalid argument: \(arg)" + case let .InvalidValueForOption(opt, vals): + let vs = vals.joinWithSeparator(", ") + return "Invalid value(s) for option \(opt.flagDescription): \(vs)" + case let .MissingRequiredOptions(opts): + return "Missing required options: \(opts.map { return $0.flagDescription })" + } + } + } + + /** + * Initializes a CommandLine object. + * + * - parameter arguments: Arguments to parse. If omitted, the arguments passed to the app + * on the command line will automatically be used. + * + * - returns: An initalized CommandLine object. + */ + public init(arguments: [String] = Process.arguments) { + self._arguments = arguments + + /* Initialize locale settings from the environment */ + setlocale(LC_ALL, "") + } + + /* Returns all argument values from flagIndex to the next flag or the end of the argument array. */ + private func _getFlagValues(flagIndex: Int) -> [String] { + var args: [String] = [String]() + var skipFlagChecks = false + + /* Grab attached arg, if any */ + var attachedArg = _arguments[flagIndex].splitByCharacter(ArgumentAttacher, maxSplits: 1) + if attachedArg.count > 1 { + args.append(attachedArg[1]) + } + + for var i = flagIndex + 1; i < _arguments.count; i++ { + if !skipFlagChecks { + if _arguments[i] == ArgumentStopper { + skipFlagChecks = true + continue + } + + if _arguments[i].hasPrefix(ShortOptionPrefix) && Int(_arguments[i]) == nil && + _arguments[i].toDouble() == nil { + break + } + } + + args.append(_arguments[i]) + } + + return args + } + + /** + * Adds an Option to the command line. + * + * - parameter option: The option to add. + */ + public func addOption(option: Option) { + _options.append(option) + } + + /** + * Adds one or more Options to the command line. + * + * - parameter options: An array containing the options to add. + */ + public func addOptions(options: [Option]) { + _options += options + } + + /** + * Adds one or more Options to the command line. + * + * - parameter options: The options to add. + */ + public func addOptions(options: Option...) { + _options += options + } + + /** + * Sets the command line Options. Any existing options will be overwritten. + * + * - parameter options: An array containing the options to set. + */ + public func setOptions(options: [Option]) { + _options = options + } + + /** + * Sets the command line Options. Any existing options will be overwritten. + * + * - parameter options: The options to set. + */ + public func setOptions(options: Option...) { + _options = options + } + + /** + * Parses command-line arguments into their matching Option values. Throws `ParseError` if + * argument parsing fails. + * + * - parameter strict: Fail if any unrecognized arguments are present (default: false). + */ + public func parse(strict: Bool = false) throws { + for (idx, arg) in _arguments.enumerate() { + if arg == ArgumentStopper { + break + } + + if !arg.hasPrefix(ShortOptionPrefix) { + continue + } + + let skipChars = arg.hasPrefix(LongOptionPrefix) ? + LongOptionPrefix.characters.count : ShortOptionPrefix.characters.count + let flagWithArg = arg[Range(start: arg.startIndex.advancedBy(skipChars), end: arg.endIndex)] + + /* The argument contained nothing but ShortOptionPrefix or LongOptionPrefix */ + if flagWithArg.isEmpty { + continue + } + + /* Remove attached argument from flag */ + let flag = flagWithArg.splitByCharacter(ArgumentAttacher, maxSplits: 1)[0] + + var flagMatched = false + for option in _options { + if option.flagMatch(flag) { + let vals = self._getFlagValues(idx) + guard option.setValue(vals) else { + throw ParseError.InvalidValueForOption(option, vals) + } + + flagMatched = true + break + } + } + + /* Flags that do not take any arguments can be concatenated */ + let flagLength = flag.characters.count + if !flagMatched && !arg.hasPrefix(LongOptionPrefix) { + for (i, c) in flag.characters.enumerate() { + for option in _options { + if option.flagMatch(String(c)) { + /* Values are allowed at the end of the concatenated flags, e.g. + * -xvf + */ + let vals = (i == flagLength - 1) ? self._getFlagValues(idx) : [String]() + guard option.setValue(vals) else { + throw ParseError.InvalidValueForOption(option, vals) + } + + flagMatched = true + break + } + } + } + } + + /* Invalid flag */ + guard !strict || flagMatched else { + throw ParseError.InvalidArgument(arg) + } + } + + /* Check to see if any required options were not matched */ + let missingOptions = _options.filter { $0.required && !$0.wasSet } + guard missingOptions.count == 0 else { + throw ParseError.MissingRequiredOptions(missingOptions) + } + } + + /* printUsage() is generic for OutputStreamType because the Swift compiler crashes + * on inout protocol function parameters in Xcode 7 beta 1 (rdar://21372694). + */ + + /** + * Prints a usage message. + * + * - parameter to: An OutputStreamType to write the error message to. + */ + public func printUsage(inout to: TargetStream) { + let name = _arguments[0] + + var flagWidth = 0 + for opt in _options { + flagWidth = max(flagWidth, " \(opt.flagDescription):".characters.count) + } + + print("Usage: \(name) [options]", toStream: &to) + for opt in _options { + let flags = " \(opt.flagDescription):".paddedToWidth(flagWidth) + print("\(flags)\n \(opt.helpMessage)", toStream: &to) + } + } + + /** + * Prints a usage message. + * + * - parameter error: An error thrown from `parse()`. A description of the error + * (e.g. "Missing required option --extract") will be printed before the usage message. + * - parameter to: An OutputStreamType to write the error message to. + */ + public func printUsage(error: ErrorType, inout to: TargetStream) { + print("\(error)\n", toStream: &to) + printUsage(&to) + } + + /** + * Prints a usage message. + * + * - parameter error: An error thrown from `parse()`. A description of the error + * (e.g. "Missing required option --extract") will be printed before the usage message. + */ + public func printUsage(error: ErrorType) { + var out = StderrOutputStream.stream + printUsage(error, to: &out) + } + + /** + * Prints a usage message. + */ + public func printUsage() { + var out = StderrOutputStream.stream + printUsage(&out) + } +} diff --git a/Carthage/Checkouts/CommandLine/CommandLine/Info.plist b/Carthage/Checkouts/CommandLine/CommandLine/Info.plist new file mode 100644 index 0000000..e095737 --- /dev/null +++ b/Carthage/Checkouts/CommandLine/CommandLine/Info.plist @@ -0,0 +1,28 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSHumanReadableCopyright + Copyright © 2014 Ben Gollmer. Licensed under the Apache License, Version 2.0. + NSPrincipalClass + + + diff --git a/Carthage/Checkouts/CommandLine/CommandLine/Option.swift b/Carthage/Checkouts/CommandLine/CommandLine/Option.swift new file mode 100644 index 0000000..74466bb --- /dev/null +++ b/Carthage/Checkouts/CommandLine/CommandLine/Option.swift @@ -0,0 +1,271 @@ +/* + * Option.swift + * Copyright (c) 2014 Ben Gollmer. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The base class for a command-line option. + */ +public class Option { + public let shortFlag: String? + public let longFlag: String? + public let required: Bool + public let helpMessage: String + + /** True if the option was set when parsing command-line arguments */ + public var wasSet: Bool { + return false + } + + public var flagDescription: String { + switch (shortFlag, longFlag) { + case (let sf, let lf) where sf != nil && lf != nil: + return "\(ShortOptionPrefix)\(sf!), \(LongOptionPrefix)\(lf!)" + case (_, let lf) where lf != nil: + return "\(LongOptionPrefix)\(lf!)" + default: + return "\(ShortOptionPrefix)\(shortFlag!)" + } + } + + private init(_ shortFlag: String?, _ longFlag: String?, _ required: Bool, _ helpMessage: String) { + if let sf = shortFlag { + assert(sf.characters.count == 1, "Short flag must be a single character") + assert(Int(sf) == nil && sf.toDouble() == nil, "Short flag cannot be a numeric value") + } + + if let lf = longFlag { + assert(Int(lf) == nil && lf.toDouble() == nil, "Long flag cannot be a numeric value") + } + + self.shortFlag = shortFlag + self.longFlag = longFlag + self.helpMessage = helpMessage + self.required = required + } + + /* The optional casts in these initalizers force them to call the private initializer. Without + * the casts, they recursively call themselves. + */ + + /** Initializes a new Option that has both long and short flags. */ + public convenience init(shortFlag: String, longFlag: String, required: Bool = false, helpMessage: String) { + self.init(shortFlag as String?, longFlag, required, helpMessage) + } + + /** Initializes a new Option that has only a short flag. */ + public convenience init(shortFlag: String, required: Bool = false, helpMessage: String) { + self.init(shortFlag as String?, nil, required, helpMessage) + } + + /** Initializes a new Option that has only a long flag. */ + public convenience init(longFlag: String, required: Bool = false, helpMessage: String) { + self.init(nil, longFlag as String?, required, helpMessage) + } + + func flagMatch(flag: String) -> Bool { + return flag == shortFlag || flag == longFlag + } + + func setValue(values: [String]) -> Bool { + return false + } +} + +/** + * A boolean option. The presence of either the short or long flag will set the value to true; + * absence of the flag(s) is equivalent to false. + */ +public class BoolOption: Option { + private var _value: Bool = false + + public var value: Bool { + return _value + } + + override public var wasSet: Bool { + return _value + } + + override func setValue(values: [String]) -> Bool { + _value = true + return true + } +} + +/** An option that accepts a positive or negative integer value. */ +public class IntOption: Option { + private var _value: Int? + + public var value: Int? { + return _value + } + + override public var wasSet: Bool { + return _value != nil + } + + override func setValue(values: [String]) -> Bool { + if values.count == 0 { + return false + } + + if let val = Int(values[0]) { + _value = val + return true + } + + return false + } +} + +/** + * An option that represents an integer counter. Each time the short or long flag is found + * on the command-line, the counter will be incremented. + */ +public class CounterOption: Option { + private var _value: Int = 0 + + public var value: Int { + return _value + } + + override public var wasSet: Bool { + return _value > 0 + } + + override func setValue(values: [String]) -> Bool { + _value += 1 + return true + } +} + +/** An option that accepts a positive or negative floating-point value. */ +public class DoubleOption: Option { + private var _value: Double? + + public var value: Double? { + return _value + } + + override public var wasSet: Bool { + return _value != nil + } + + override func setValue(values: [String]) -> Bool { + if values.count == 0 { + return false + } + + if let val = values[0].toDouble() { + _value = val + return true + } + + return false + } +} + +/** An option that accepts a string value. */ +public class StringOption: Option { + private var _value: String? = nil + + public var value: String? { + return _value + } + + override public var wasSet: Bool { + return _value != nil + } + + override func setValue(values: [String]) -> Bool { + if values.count == 0 { + return false + } + + _value = values[0] + return true + } +} + +/** An option that accepts one or more string values. */ +public class MultiStringOption: Option { + private var _value: [String]? + + public var value: [String]? { + return _value + } + + override public var wasSet: Bool { + return _value != nil + } + + override func setValue(values: [String]) -> Bool { + if values.count == 0 { + return false + } + + _value = values + return true + } +} + +/** An option that represents an enum value. */ +public class EnumOption: Option { + private var _value: T? + public var value: T? { + return _value + } + + override public var wasSet: Bool { + return _value != nil + } + + /* Re-defining the intializers is necessary to make the Swift 2 compiler happy, as + * of Xcode 7 beta 2. + */ + + private override init(_ shortFlag: String?, _ longFlag: String?, _ required: Bool, _ helpMessage: String) { + super.init(shortFlag, longFlag, required, helpMessage) + } + + /** Initializes a new Option that has both long and short flags. */ + public convenience init(shortFlag: String, longFlag: String, required: Bool = false, helpMessage: String) { + self.init(shortFlag as String?, longFlag, required, helpMessage) + } + + /** Initializes a new Option that has only a short flag. */ + public convenience init(shortFlag: String, required: Bool = false, helpMessage: String) { + self.init(shortFlag as String?, nil, required, helpMessage) + } + + /** Initializes a new Option that has only a long flag. */ + public convenience init(longFlag: String, required: Bool = false, helpMessage: String) { + self.init(nil, longFlag as String?, required, helpMessage) + } + + override func setValue(values: [String]) -> Bool { + if values.count == 0 { + return false + } + + if let v = T(rawValue: values[0]) { + _value = v + return true + } + + return false + } +} diff --git a/Carthage/Checkouts/CommandLine/CommandLine/StringExtensions.swift b/Carthage/Checkouts/CommandLine/CommandLine/StringExtensions.swift new file mode 100644 index 0000000..8d3777e --- /dev/null +++ b/Carthage/Checkouts/CommandLine/CommandLine/StringExtensions.swift @@ -0,0 +1,162 @@ +/* + * StringExtensions.swift + * Copyright (c) 2014 Ben Gollmer. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Required for localeconv(3) */ +import Darwin + +internal extension String { + /* Retrieves locale-specified decimal separator from the environment + * using localeconv(3). + */ + private func _localDecimalPoint() -> Character { + let locale = localeconv() + if locale != nil { + let decimalPoint = locale.memory.decimal_point + if decimalPoint != nil { + return Character(UnicodeScalar(UInt32(decimalPoint.memory))) + } + } + + return "." + } + + /** + * Attempts to parse the string value into a Double. + * + * - returns: A Double if the string can be parsed, nil otherwise. + */ + func toDouble() -> Double? { + var characteristic: String = "0" + var mantissa: String = "0" + var inMantissa: Bool = false + var isNegative: Bool = false + let decimalPoint = self._localDecimalPoint() + + for (i, c) in self.characters.enumerate() { + if i == 0 && c == "-" { + isNegative = true + continue + } + + if c == decimalPoint { + inMantissa = true + continue + } + + if Int(String(c)) != nil { + if !inMantissa { + characteristic.append(c) + } else { + mantissa.append(c) + } + } else { + /* Non-numeric character found, bail */ + return nil + } + } + + return (Double(Int(characteristic)!) + + Double(Int(mantissa)!) / pow(Double(10), Double(mantissa.characters.count - 1))) * + (isNegative ? -1 : 1) + } + + /** + * Splits a string into an array of string components. + * + * - parameter splitBy: The character to split on. + * - parameter maxSplit: The maximum number of splits to perform. If 0, all possible splits are made. + * + * - returns: An array of string components. + */ + func splitByCharacter(splitBy: Character, maxSplits: Int = 0) -> [String] { + var s = [String]() + var numSplits = 0 + + var curIdx = self.startIndex + for(var i = self.startIndex; i != self.endIndex; i = i.successor()) { + let c = self[i] + if c == splitBy && (maxSplits == 0 || numSplits < maxSplits) { + s.append(self[Range(start: curIdx, end: i)]) + curIdx = i.successor() + numSplits++ + } + } + + if curIdx != self.endIndex { + s.append(self[Range(start: curIdx, end: self.endIndex)]) + } + + return s + } + + /** + * Pads a string to the specified width. + * + * - parameter width: The width to pad the string to. + * - parameter padBy: The character to use for padding. + * + * - returns: A new string, padded to the given width. + */ + func paddedToWidth(width: Int, padBy: Character = " ") -> String { + var s = self + var currentLength = self.characters.count + + while currentLength++ < width { + s.append(padBy) + } + + return s + } + + /** + * Wraps a string to the specified width. + * + * This just does simple greedy word-packing, it doesn't go full Knuth-Plass. + * If a single word is longer than the line width, it will be placed (unsplit) + * on a line by itself. + * + * - parameter width: The maximum length of a line. + * - parameter wrapBy: The line break character to use. + * - parameter splitBy: The character to use when splitting the string into words. + * + * - returns: A new string, wrapped at the given width. + */ + func wrappedAtWidth(width: Int, wrapBy: Character = "\n", splitBy: Character = " ") -> String { + var s = "" + var currentLineWidth = 0 + + for word in self.splitByCharacter(splitBy) { + let wordLength = word.characters.count + + if currentLineWidth + wordLength + 1 > width { + /* Word length is greater than line length, can't wrap */ + if wordLength >= width { + s += word + } + + s.append(wrapBy) + currentLineWidth = 0 + } + + currentLineWidth += wordLength + 1 + s += word + s.append(splitBy) + } + + return s + } +} diff --git a/Carthage/Checkouts/CommandLine/CommandLineTests/CommandLineTests.swift b/Carthage/Checkouts/CommandLine/CommandLineTests/CommandLineTests.swift new file mode 100644 index 0000000..31e2817 --- /dev/null +++ b/Carthage/Checkouts/CommandLine/CommandLineTests/CommandLineTests.swift @@ -0,0 +1,723 @@ +/* + * CommandLineTests.swift + * Copyright (c) 2014 Ben Gollmer. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import XCTest + +internal class CommandLineTests: XCTestCase { + + func testBoolOptions() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-a", "--bool", "-c", "-c", "-ddd" ]) + + /* Short flag */ + let a = BoolOption(shortFlag: "a", longFlag: "a1", helpMessage: "") + + /* Long flag */ + let b = BoolOption(shortFlag: "b", longFlag: "bool", helpMessage: "") + + /* Multiple flags + * Do not throw an error if a bool value is specified more than once + */ + let c = BoolOption(shortFlag: "c", longFlag: "c1", helpMessage: "") + + /* Concatenated multiple flags + * As with separate multiple flags, don't barf if this happens + */ + let d = BoolOption(shortFlag: "d", longFlag: "d1", helpMessage: "") + + /* Missing flag */ + let e = BoolOption(shortFlag: "e", longFlag: "e1", helpMessage: "") + + cli.addOptions(a, b, c, d, e) + + do { + try cli.parse() + XCTAssertTrue(a.value, "Failed to get true value from short bool") + XCTAssertTrue(b.value, "Failed to get true value from long bool") + XCTAssertTrue(c.value, "Failed to get true value from multi-flagged bool") + XCTAssertTrue(d.value, "Failed to get true value from concat multi-flagged bool") + XCTAssertFalse(e.value, "Failed to get false value from missing bool") + } catch { + XCTFail("Failed to parse bool options: \(error)") + } + } + + func testIntOptions() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-a", "1", "--bigs", "2", "-c", "3", + "-c", "4", "-ddd", "-e", "bad", "-f", "-g", "-5" ]) + + /* Short flag */ + let a = IntOption(shortFlag: "a", longFlag: "a1", required: false, helpMessage: "") + + /* Long flag */ + let b = IntOption(shortFlag: "b", longFlag: "bigs", required: false, helpMessage: "") + + /* Multiple short flags + * If an int is specified multiple times, return the last (rightmost) value + */ + let c = IntOption(shortFlag: "c", longFlag: "c1", required: false, helpMessage: "") + + cli.addOptions(a, b, c) + + do { + try cli.parse() + XCTAssertEqual(a.value!, 1, "Failed to get correct value from short int") + XCTAssertEqual(b.value!, 2, "Failed to get correct value from long int") + XCTAssertEqual(c.value!, 4, "Failed to get correct value from multi-flagged int") + } catch { + XCTFail("Failed to parse int options: \(error)") + } + + /* Concatenated multiple flags + * Concat flags can't have values + */ + let d = IntOption(shortFlag: "d", longFlag: "d1", required: false, helpMessage: "") + cli.setOptions(d) + + do { + try cli.parse() + XCTFail("Parsed invalid concat int option") + } catch { + XCTAssertNil(d.value, "Got non-nil value from concat multi-flagged int") + } + + /* Non-int value */ + let e = IntOption(shortFlag: "e", longFlag: "e1", required: false, helpMessage: "") + cli.setOptions(e) + + do { + try cli.parse() + XCTFail("Parsed invalid int option") + } catch let CommandLine.ParseError.InvalidValueForOption(opt, vals) { + XCTAssert(opt === e, "Incorrect option in ParseError: \(opt.longFlag)") + XCTAssertEqual(vals, ["bad"], "Incorrect values in ParseError: \(vals)") + XCTAssertNil(e.value, "Got non-nil value from invalid int") + } catch { + XCTFail("Unexpected parse error: \(error)") + } + + /* No value */ + let f = IntOption(shortFlag: "f", longFlag: "f1", required: false, helpMessage: "") + cli.setOptions(f) + + do { + try cli.parse() + XCTFail("Parsed int option with no value") + } catch let CommandLine.ParseError.InvalidValueForOption(opt, vals) { + XCTAssert(opt === f, "Incorrect option in ParseError: \(opt.longFlag)") + XCTAssertEqual(vals, [], "Incorrect values in ParseError: \(vals)") + XCTAssertNil(f.value, "Got non-nil value from no value int") + } catch { + XCTFail("Unexpected parse error: \(error)") + } + + /* Negative int */ + let g = IntOption(shortFlag: "g", longFlag: "g1", required: false, helpMessage: "") + cli.setOptions(g) + + do { + try cli.parse() + XCTAssertEqual(g.value!, -5, "Failed to get correct value from int option with negative value") + } catch { + XCTFail("Failed to parse int option with negative value: \(error)") + } + } + + func testCounterOptions() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-a", "--bach", "-c", "-c", + "--doggerel", "-doggerel", "--doggerel", "-eeee"]) + + /* Short flag */ + let a = CounterOption(shortFlag: "a", longFlag: "a1", helpMessage: "") + + /* Long flag */ + let b = CounterOption(shortFlag: "b", longFlag: "bach", helpMessage: "") + + /* Multiple short flags + * If a double is specified multiple times, return the last (rightmost) value + */ + let c = CounterOption(shortFlag: "c", longFlag: "c1", helpMessage: "") + + /* Multiple long flags */ + let d = CounterOption(shortFlag: "d", longFlag: "doggerel", helpMessage: "") + + /* Concatenated multiple flags */ + let e = CounterOption(shortFlag: "e", longFlag: "e1", helpMessage: "") + + /* Unspecified option should return 0, not nil */ + let f = CounterOption(shortFlag: "f", longFlag: "f1", helpMessage: "") + + cli.addOptions(a, b, c, d, e, f) + + do { + try cli.parse() + XCTAssertEqual(a.value, 1, "Failed to get correct value from short counter") + XCTAssertEqual(b.value, 1, "Failed to get correct value from long counter") + XCTAssertEqual(c.value, 2, "Failed to get correct value from multi-flagged short counter") + XCTAssertEqual(d.value, 3, "Failed to get correct value from multi-flagged long counter") + XCTAssertEqual(e.value, 4, "Failed to get correct value from concat multi-flagged counter") + XCTAssertEqual(f.value, 0, "Failed to get correct value from unspecified counter") + } catch { + XCTFail("Failed to parse counter options: \(error)") + } + } + + func testDoubleOptions() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-a", "1.4", "--baritone", "2.5", + "-c", "5.0", "-c", "5.2", "--dingus", "8.5", "--dingus", "8.8", "-e", "95", + "-f", "bad", "-g", "-h", "-3.14159" ]) + + /* Short flag */ + let a = DoubleOption(shortFlag: "a", longFlag: "a1", required: true, helpMessage: "") + + /* Long flag */ + let b = DoubleOption(shortFlag: "b", longFlag: "baritone", required: true, helpMessage: "") + + /* Multiple short flags */ + let c = DoubleOption(shortFlag: "c", longFlag: "c1", required: true, helpMessage: "") + + /* Multiple long flags */ + let d = DoubleOption(shortFlag: "d", longFlag: "dingus", required: true, helpMessage: "") + + /* Integer value */ + let e = DoubleOption(shortFlag: "e", longFlag: "e1", required: true, helpMessage: "") + + cli.addOptions(a, b, c, d, e) + + do { + try cli.parse() + XCTAssertEqual(a.value!, 1.4, "Failed to get correct value from short double") + XCTAssertEqual(b.value!, 2.5, "Failed to get correct value from long double") + XCTAssertEqual(c.value!, 5.2, "Failed to get correct value from multi-flagged short double") + XCTAssertEqual(d.value!, 8.8, "Failed to get correct value from multi-flagged long double") + XCTAssertEqual(e.value!, 95.0, "Failed to get correct double value from integer argument") + } catch { + XCTFail("Failed to parse double options: \(error)") + } + + /* Non-double value */ + let f = DoubleOption(shortFlag: "f", longFlag: "f1", required: true, helpMessage: "") + cli.setOptions(f) + + do { + try cli.parse() + XCTFail("Parsed invalid double option") + } catch let CommandLine.ParseError.InvalidValueForOption(opt, vals) { + XCTAssert(opt === f, "Incorrect option in ParseError: \(opt.longFlag)") + XCTAssertEqual(vals, ["bad"], "Incorrect values in ParseError: \(vals)") + XCTAssertNil(f.value, "Got non-nil value from invalid double") + } catch { + XCTFail("Unexpected parse error: \(error)") + } + + + /* No value */ + let g = DoubleOption(shortFlag: "g", longFlag: "g1", required: true, helpMessage: "") + cli.setOptions(g) + + do { + try cli.parse() + XCTFail("Parsed double option with no value") + } catch let CommandLine.ParseError.InvalidValueForOption(opt, vals) { + XCTAssert(opt === g, "Incorrect option in ParseError: \(opt.longFlag)") + XCTAssertEqual(vals, [], "Incorrect values in ParseError: \(vals)") + XCTAssertNil(g.value, "Got non-nil value from no value double") + } catch { + XCTFail("Unexpected parse error: \(error)") + } + + /* Negative double */ + let h = DoubleOption(shortFlag: "h", longFlag: "h1", required: true, helpMessage: "") + cli.setOptions(h) + + do { + try cli.parse() + XCTAssertEqual(h.value!, -3.14159, "Failed to get correct value from double with negative value") + } catch { + XCTFail("Failed to parse double option with negative value: \(error)") + } + } + + func testDoubleOptionsInAlternateLocale() { + let cli = CommandLine(arguments: ["CommandLineTests", "-a", "3,14159"]) + let a = DoubleOption(shortFlag: "a", longFlag: "a1", required: true, helpMessage: "") + + cli.addOptions(a) + + setlocale(LC_ALL, "sv_SE.UTF-8") + + do { + try cli.parse() + XCTAssertEqual(a.value!, 3.14159, "Failed to get correct value from double in alternate locale") + } catch { + XCTFail("Failed to parse double options in alternate locale: \(error)") + } + } + + func testStringOptions() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-a", "one", "--b1", "two", "-c", "x", "-c", "xx", + "--d1", "y", "--d1", "yy", "-e" ]) + + /* Short flag */ + let a = StringOption(shortFlag: "a", longFlag: "a1", required: true, helpMessage: "") + + /* Long flag */ + let b = StringOption(shortFlag: "b", longFlag: "b1", required: true, helpMessage: "") + + /* Multiple short flags */ + let c = StringOption(shortFlag: "c", longFlag: "c1", required: true, helpMessage: "") + + /* Multiple long flags */ + let d = StringOption(shortFlag: "d", longFlag: "d1", required: true, helpMessage: "") + + cli.addOptions(a, b, c, d) + + do { + try cli.parse() + XCTAssertEqual(a.value!, "one", "Failed to get correct value from short string") + XCTAssertEqual(b.value!, "two", "Failed to get correct value from long string") + XCTAssertEqual(c.value!, "xx", "Failed to get correct value from multi-flagged short string") + XCTAssertEqual(d.value!, "yy", "Failed to get correct value from multi-flagged long string") + } catch { + XCTFail("Failed to parse string options: \(error)") + } + + /* No value */ + let e = StringOption(shortFlag: "e", longFlag: "e1", required: false, helpMessage: "") + cli.setOptions(e) + + do { + try cli.parse() + XCTFail("Parsed string option with no value") + } catch let CommandLine.ParseError.InvalidValueForOption(opt, vals) { + XCTAssert(opt === e, "Incorrect option in ParseError: \(opt.longFlag)") + XCTAssertEqual(vals, [], "Incorrect values in ParseError: \(vals)") + XCTAssertNil(e.value, "Got non-nil value from no value string") + } catch { + XCTFail("Unexpected parse error: \(error)") + } + } + + func testMultiStringOptions() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-a", "one", "-b", "two", "2wo", + "--c1", "three", "--d1", "four", "4our", "-e" ]) + + /* Short flags */ + let a = MultiStringOption(shortFlag: "a", longFlag: "a1", required: true, helpMessage: "") + let b = MultiStringOption(shortFlag: "b", longFlag: "b1", required: true, helpMessage: "") + + /* Long flags */ + let c = MultiStringOption(shortFlag: "c", longFlag: "c1", required: true, helpMessage: "") + let d = MultiStringOption(shortFlag: "d", longFlag: "d1", required: true, helpMessage: "") + + cli.addOptions(a, b, c, d) + + do { + try cli.parse() + XCTAssertEqual(a.value!.count, 1, "Failed to get correct number of values from single short multistring") + XCTAssertEqual(a.value![0], "one", "Filed to get correct value from single short multistring") + XCTAssertEqual(b.value!.count, 2, "Failed to get correct number of values from multi short multistring") + XCTAssertEqual(b.value![0], "two", "Failed to get correct first value from multi short multistring") + XCTAssertEqual(b.value![1], "2wo", "Failed to get correct second value from multi short multistring") + XCTAssertEqual(c.value!.count, 1, "Failed to get correct number of values from single long multistring") + XCTAssertEqual(c.value![0], "three", "Filed to get correct value from single long multistring") + XCTAssertEqual(d.value!.count, 2, "Failed to get correct number of values from multi long multistring") + XCTAssertEqual(d.value![0], "four", "Failed to get correct first value from multi long multistring") + XCTAssertEqual(d.value![1], "4our", "Failed to get correct second value from multi long multistring") + } catch { + XCTFail("Failed to parse multi string options: \(error)") + } + + /* No value */ + let e = MultiStringOption(shortFlag: "e", longFlag: "e1", required: false, helpMessage: "") + cli.setOptions(e) + + do { + try cli.parse() + XCTFail("Parsed multi string option with no value") + } catch let CommandLine.ParseError.InvalidValueForOption(opt, vals) { + XCTAssert(opt === e, "Incorrect option in ParseError: \(opt.longFlag)") + XCTAssertEqual(vals, [], "Incorrect values in ParseError: \(vals)") + XCTAssertNil(e.value, "Got non-nil value from no value multistring") + } catch { + XCTFail("Unexpected parse error: \(error)") + } + } + + func testConcatOptionWithValue() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-xvf", "file1", "file2" ]) + + let x = BoolOption(shortFlag: "x", longFlag: "x1", helpMessage: "") + let v = CounterOption(shortFlag: "v", longFlag: "v1", helpMessage: "") + let f = MultiStringOption(shortFlag: "f", longFlag: "file", required: true, helpMessage: "") + + cli.addOptions(x, v, f) + + do { + try cli.parse() + XCTAssertTrue(x.value as Bool, "Failed to get true value from concat flags with value") + XCTAssertEqual(v.value, 1, "Failed to get correct value from concat flags with value") + XCTAssertEqual(f.value!.count, 2, "Failed to get values from concat flags with value") + XCTAssertEqual(f.value![0], "file1", "Failed to get first value from concat flags with value") + XCTAssertEqual(f.value![1], "file2", "Failed to get second value from concat flags with value") + } catch { + XCTFail("Failed to parse concat flags with value: \(error)") + } + } + + func testMissingRequiredOption() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-a", "-b", "foo", "-q", "quux" ]) + let c = StringOption(shortFlag: "c", longFlag: "c1", required: true, helpMessage: "") + + cli.addOption(c) + + do { + try cli.parse() + XCTFail("Parsed missing required option") + } catch let CommandLine.ParseError.MissingRequiredOptions(opts) { + XCTAssert(opts[0] === c, "Failed to identify missing required options: \(opts)") + XCTAssertNil(c.value, "Got non-nil value from missing option") + } catch { + XCTFail("Unexpected parse error: \(error)") + } + } + + func testAttachedArgumentValues() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-a=5", "--bb=klaxon" ]) + + let a = IntOption(shortFlag: "a", longFlag: "a1", required: true, helpMessage: "") + let b = StringOption(shortFlag: "b", longFlag: "bb", required: true, helpMessage: "") + + cli.addOptions(a, b) + + do { + try cli.parse() + XCTAssertEqual(a.value!, 5, "Failed to get correct int attached value") + XCTAssertEqual(b.value!, "klaxon", "Failed to get correct string attached value") + } catch { + XCTFail("Failed to parse attached argument values: \(error)") + } + } + + func testEmojiOptions() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-👻", "3", "--👍", "☀️" ]) + + let a = IntOption(shortFlag: "👻", longFlag: "👻", required: true, helpMessage: "") + let b = StringOption(shortFlag: "👍", longFlag: "👍", required: true, helpMessage: "") + + + cli.addOptions(a, b) + + do { + try cli.parse() + XCTAssertEqual(a.value!, 3) + XCTAssertEqual(b.value!, "☀️") + } catch { + XCTFail("Failed to parse emoji options: \(error)") + } + } + + func testEnumOption() { + enum Operation: String { + case Create = "c" + case Extract = "x" + case List = "l" + case Verify = "v" + } + + let cli = CommandLine(arguments: [ "CommandLineTests", "--operation", "x" ]) + let op = EnumOption(shortFlag: "o", longFlag: "operation", required: true, helpMessage: "") + + cli.setOptions(op) + + do { + try cli.parse() + XCTAssertEqual(op.value!, Operation.Extract, "Failed to get correct value from enum option") + } catch { + XCTFail("Failed to parse enum options: \(error)") + } + } + + func testArgumentStopper() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-a", "--", "-value", "--", "-55" ]) + let op = MultiStringOption(shortFlag: "a", longFlag: "a1", required: true, helpMessage: "") + + cli.setOptions(op) + + do { + try cli.parse() + XCTAssertEqual(op.value!.count, 3, "Failed to get correct number of options with stopper") + XCTAssertEqual(op.value![0], "-value", "Failed to get correct value from options with stopper") + XCTAssertEqual(op.value![1], "--", "Failed to get correct value from options with stopper") + XCTAssertEqual(op.value![2], "-55", "Failed to get correct value from options with stopper") + } catch { + XCTFail("Failed to parse options with an argument stopper: \(error)") + } + } + + func testFlagStyles() { + let argLines = [ + [ "CommandLineTests", "-xvf", "/path/to/file" ], + [ "CommandLineTests", "-x", "-v", "-f", "/path/to/file" ], + [ "CommandLineTests", "-x", "--verbose", "--file", "/path/to/file" ], + [ "CommandLineTests", "-xv", "--file", "/path/to/file" ], + [ "CommandLineTests", "--extract", "-v", "--file=/path/to/file" ] + ] + + for args in argLines { + let cli = CommandLine(arguments: args) + let extract = BoolOption(shortFlag: "x", longFlag: "extract", helpMessage: "") + let verbosity = CounterOption(shortFlag: "v", longFlag: "verbose", helpMessage: "") + let filePath = StringOption(shortFlag: "f", longFlag: "file", required: true, helpMessage: "") + + cli.setOptions(extract, verbosity, filePath) + + do { + try cli.parse() + XCTAssertEqual(extract.value, true, "Failed to parse extract value from arg line \(args)") + XCTAssertEqual(verbosity.value, 1, "Failed to parse verbosity value from arg line \(args)") + XCTAssertEqual(filePath.value!, "/path/to/file", "Failed to parse file path value from arg line \(args)") + } catch { + XCTFail("Failed to parse arg line \(args): \(error)") + } + } + } + + func testEmptyFlags() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-", "--"]) + + do { + try cli.parse() + } catch { + XCTFail("Failed to parse empty flags: \(error)") + } + } + + func testMixedExample() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-dvvv", "--name", "John Q. Public", + "-f", "45", "-p", "0.05", "-x", "extra1", "extra2", "extra3" ]) + + let boolOpt = BoolOption(shortFlag: "d", longFlag: "debug", helpMessage: "Enables debug mode.") + let counterOpt = CounterOption(shortFlag: "v", longFlag: "verbose", + helpMessage: "Enables verbose output. Specify multiple times for extra verbosity.") + let stringOpt = StringOption(shortFlag: "n", longFlag: "name", required: true, + helpMessage: "Name a Cy Young winner.") + let intOpt = IntOption(shortFlag: "f", longFlag: "favorite", required: true, + helpMessage: "Your favorite number.") + let doubleOpt = DoubleOption(shortFlag: "p", longFlag: "p-value", required: true, + helpMessage: "P-value for test.") + let extraOpt = MultiStringOption(shortFlag: "x", longFlag: "Extra", required: true, + helpMessage: "X is for Extra.") + + cli.addOptions(boolOpt, counterOpt, stringOpt, intOpt, doubleOpt, extraOpt) + + do { + try cli.parse() + XCTAssertTrue(boolOpt.value, "Failed to get correct bool value from mixed command line") + XCTAssertEqual(counterOpt.value, 3, "Failed to get correct counter value from mixed command line") + XCTAssertEqual(stringOpt.value!, "John Q. Public", "Failed to get correct string value from mixed command line") + XCTAssertEqual(intOpt.value!, 45, "Failed to get correct int value from mixed command line") + XCTAssertEqual(doubleOpt.value!, 0.05, "Failed to get correct double value from mixed command line") + XCTAssertEqual(extraOpt.value!.count , 3, "Failed to get correct number of multistring options from mixed command line") + } catch { + XCTFail("Failed to parse mixed command line: \(error)") + } + } + + func testWasSetProperty() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-a", "-b", "-c", "str", "-d", "1", + "-e", "3.14159", "-f", "extra1", "extra2", "extra3" ]) + + let setOptions = [ + BoolOption(shortFlag: "a", longFlag: "bool", helpMessage: "A set boolean option"), + CounterOption(shortFlag: "b", longFlag: "counter", helpMessage: "A set counter option"), + StringOption(shortFlag: "c", longFlag: "str", helpMessage: "A set string option"), + IntOption(shortFlag: "d", longFlag: "int", helpMessage: "A set int option"), + DoubleOption(shortFlag: "e", longFlag: "double", helpMessage: "A set double option"), + MultiStringOption(shortFlag: "f", longFlag: "multi", helpMessage: "A set multistring option") + ] + + let unsetOptions = [ + BoolOption(shortFlag: "t", longFlag: "unbool", helpMessage: "An unset boolean option"), + CounterOption(shortFlag: "v", longFlag: "uncounter", helpMessage: "An unset counter option"), + StringOption(shortFlag: "w", longFlag: "unstr", helpMessage: "An unset string option"), + IntOption(shortFlag: "y", longFlag: "unint", helpMessage: "An unset int option"), + DoubleOption(shortFlag: "x", longFlag: "undouble", helpMessage: "An unset double option"), + MultiStringOption(shortFlag: "z", longFlag: "unmulti", helpMessage: "An unset multistring option") + ] + + cli.addOptions(setOptions) + cli.addOptions(unsetOptions) + + do { + try cli.parse() + for opt in setOptions { + XCTAssertTrue(opt.wasSet, "wasSet was false for set option \(opt.flagDescription)") + } + for opt in unsetOptions { + XCTAssertFalse(opt.wasSet, "wasSet was true for unset option \(opt.flagDescription)") + } + } catch { + XCTFail("Failed to parse command line with set & unset options: \(error)") + } + } + + func testShortFlagOnlyOption() { + let cli = CommandLine(arguments: ["-s", "itchy", "--itchy", "scratchy"]) + + let o1 = StringOption(shortFlag: "s", helpMessage: "short only") + let o2 = StringOption(shortFlag: "i", helpMessage: "another short") + cli.addOptions(o1, o2) + + do { + try cli.parse() + XCTAssertEqual(o1.value!, "itchy", "Failed to get correct string value from short-flag-only option") + XCTAssertNil(o2.value, "Incorrectly set value for short-flag-only option") + } catch { + XCTFail("Failed to parse short-flag-only command line: \(error)") + } + } + + func testLongFlagOnlyOption() { + let cli = CommandLine(arguments: ["-s", "itchy", "--itchy", "scratchy"]) + + let o1 = StringOption(longFlag: "scratchy", helpMessage: "long only") + let o2 = StringOption(longFlag: "itchy", helpMessage: "long short") + cli.addOptions(o1, o2) + + do { + try cli.parse() + XCTAssertNil(o1.value, "Incorrectly set value for long-flag-only option") + XCTAssertEqual(o2.value!, "scratchy", "Failed to get correct string value from long-flag-only option") + } catch { + XCTFail("Failed to parse long-flag-only command line: \(error)") + } + } + + func testStrictMode() { + let cli = CommandLine(arguments: [ "CommandLineTests", "--valid", "--invalid"]) + let validOpt = BoolOption(shortFlag: "v", longFlag: "valid", helpMessage: "Known flag.") + cli.addOptions(validOpt) + + do { + try cli.parse() + } catch { + XCTFail("Failed to parse invalid flags in non-strict mode") + } + + do { + try cli.parse(true) + XCTFail("Successfully parsed invalid flags in strict mode") + } catch let CommandLine.ParseError.InvalidArgument(arg) { + XCTAssertEqual(arg, "--invalid", "Incorrect argument identified in InvalidArgument: \(arg)") + } catch { + XCTFail("Unexpected parse error: \(error)") + } + } + + func testInvalidArgumentErrorDescription() { + let cli = CommandLine(arguments: [ "CommandLineTests", "--int", "invalid"]) + let o1 = IntOption(longFlag: "int", helpMessage: "Int flag.") + cli.addOptions(o1) + + do { + try cli.parse() + } catch { + XCTAssertTrue("\(error)".hasSuffix("\(o1.flagDescription): invalid"), "Invalid error description: \(error)") + } + } + + func testMissingRequiredOptionsErrorDescription() { + let cli = CommandLine(arguments: [ "CommandLineTests"]) + let o1 = IntOption(longFlag: "int", required: true, helpMessage: "Int flag.") + cli.addOptions(o1) + + do { + try cli.parse() + } catch { + let requiredOptions = [o1].map { return $0.flagDescription } + XCTAssertTrue("\(error)".hasSuffix("options: \(requiredOptions)"), "Invalid error description: \(error)") + } + } + + func testPrintUsage() { + let cli = CommandLine(arguments: [ "CommandLineTests", "-dvvv", "--name", "John Q. Public", + "-f", "45", "-p", "0.05", "-x", "extra1", "extra2", "extra3" ]) + + let boolOpt = BoolOption(shortFlag: "d", longFlag: "debug", helpMessage: "Enables debug mode.") + let counterOpt = CounterOption(shortFlag: "v", longFlag: "verbose", + helpMessage: "Enables verbose output. Specify multiple times for extra verbosity.") + let stringOpt = StringOption(shortFlag: "n", longFlag: "name", required: true, + helpMessage: "Name a Cy Young winner.") + let intOpt = IntOption(shortFlag: "f", longFlag: "favorite", required: true, + helpMessage: "Your favorite number.") + let doubleOpt = DoubleOption(shortFlag: "p", longFlag: "p-value", required: true, + helpMessage: "P-value for test.") + let extraOpt = MultiStringOption(shortFlag: "x", longFlag: "Extra", required: true, + helpMessage: "X is for Extra.") + + let opts = [boolOpt, counterOpt, stringOpt, intOpt, doubleOpt, extraOpt] + cli.addOptions(opts) + + var out = "" + cli.printUsage(&out) + XCTAssertGreaterThan(out.characters.count, 0) + + /* There should be at least 2 lines per option, plus the intro Usage statement */ + XCTAssertGreaterThanOrEqual(out.splitByCharacter("\n").count, (opts.count * 2) + 1) + } + + func testPrintUsageError() { + let cli = CommandLine(arguments: [ "CommandLineTests" ]) + cli.addOption(StringOption(shortFlag: "n", longFlag: "name", required: true, + helpMessage: "Your name")) + + do { + try cli.parse() + XCTFail("Didn't throw with missing required argument") + } catch { + var out = "" + cli.printUsage(error, to: &out) + + let errorMessage = out.splitByCharacter("\n", maxSplits: 1)[0] + XCTAssertTrue(errorMessage.hasPrefix("Missing required")) + } + } + + func testPrintUsageToStderr() { + let cli = CommandLine(arguments: [ "CommandLineTests" ]) + cli.addOption(StringOption(shortFlag: "n", longFlag: "name", required: true, + helpMessage: "Your name")) + + /* Toss stderr into /dev/null, so the printUsage() output doesn't pollute regular + * XCTest messages. + */ + let origStdErr = dup(fileno(stderr)) + let null = fopen("/dev/null", "w") + dup2(fileno(null), fileno(stderr)) + + defer { + dup2(origStdErr, fileno(stderr)) + fclose(null) + } + + let error = CommandLine.ParseError.InvalidArgument("ack") + + /* Just make sure these doesn't crash or throw */ + cli.printUsage() + cli.printUsage(error) + } +} diff --git a/Carthage/Checkouts/CommandLine/CommandLineTests/Info.plist b/Carthage/Checkouts/CommandLine/CommandLineTests/Info.plist new file mode 100644 index 0000000..5208815 --- /dev/null +++ b/Carthage/Checkouts/CommandLine/CommandLineTests/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + NSHumanReadableCopyright + Copyright © 2014 Ben Gollmer. Licensed under the Apache License, Version 2.0. + + diff --git a/Carthage/Checkouts/CommandLine/CommandLineTests/StringExtensionTests.swift b/Carthage/Checkouts/CommandLine/CommandLineTests/StringExtensionTests.swift new file mode 100644 index 0000000..6482895 --- /dev/null +++ b/Carthage/Checkouts/CommandLine/CommandLineTests/StringExtensionTests.swift @@ -0,0 +1,135 @@ +/* + * StringExtensionTests.swift + * Copyright (c) 2014 Ben Gollmer. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import XCTest +import CommandLine + +class StringExtensionTests: XCTestCase { + + func testToDouble() { + /* Regular ol' double */ + let a = "3.14159".toDouble() + XCTAssertEqual(a!, 3.14159, "Failed to parse pi as double") + + let b = "-98.23".toDouble() + XCTAssertEqual(b!, -98.23, "Failed to parse negative double") + + /* Ints should be parsable as doubles */ + let c = "5".toDouble() + XCTAssertEqual(c!, 5, "Failed to parse int as double") + + let d = "-2099".toDouble() + XCTAssertEqual(d!, -2099, "Failed to parse negative int as double") + + + /* Zero handling */ + let e = "0.0".toDouble() + XCTAssertEqual(e!, 0, "Failed to parse zero double") + + let f = "0".toDouble() + XCTAssertEqual(f!, 0, "Failed to parse zero int as double") + + let g = "0.0000000000000000".toDouble() + XCTAssertEqual(g!, 0, "Failed to parse very long zero double") + + let h = "-0.0".toDouble() + XCTAssertEqual(h!, 0, "Failed to parse negative zero double") + + let i = "-0".toDouble() + XCTAssertEqual(i!, 0, "Failed to parse negative zero int as double") + + let j = "-0.000000000000000".toDouble() + XCTAssertEqual(j!, 0, "Failed to parse very long negative zero double") + + + /* Various extraneous chars */ + let k = "+42.3".toDouble() + XCTAssertNil(k, "Parsed double with extraneous +") + + let l = " 827.2".toDouble() + XCTAssertNil(l, "Parsed double with extraneous space") + + let m = "283_3".toDouble() + XCTAssertNil(m, "Parsed double with extraneous _") + + let n = "💩".toDouble() + XCTAssertNil(n, "Parsed poo") + + /* Locale handling */ + setlocale(LC_NUMERIC, "sv_SE.UTF-8") + + let o = "888,8".toDouble() + XCTAssertEqual(o!, 888.8, "Failed to parse double in alternate locale") + + let p = "888.8".toDouble() + XCTAssertNil(p, "Parsed double in alternate locale with wrong decimal point") + + /* Set locale back so as not to perturb any other tests */ + setlocale(LC_NUMERIC, "") + } + + func testSplitByCharacter() { + let a = "1,2,3".splitByCharacter(",") + XCTAssertEqual(a.count, 3, "Failed to split into correct number of components") + + let b = "123".splitByCharacter(",") + XCTAssertEqual(b.count, 1, "Failed to split when separator not found") + + let c = "".splitByCharacter(",") + XCTAssertEqual(c.count, 0, "Splitting empty string should return empty array") + + let e = "a-b-c-d".splitByCharacter("-", maxSplits: 2) + XCTAssertEqual(e.count, 3, "Failed to limit splits") + XCTAssertEqual(e[0], "a", "Invalid value for split 1") + XCTAssertEqual(e[1], "b", "Invalid value for split 2") + XCTAssertEqual(e[2], "c-d", "Invalid value for last split") + } + + func testPaddedByCharacter() { + let a = "this is a test" + + XCTAssertEqual(a.paddedToWidth(80).characters.count, + 80, "Failed to pad to correct width") + XCTAssertEqual(a.paddedToWidth(5).characters.count, + a.characters.count, "Bad padding when pad width is less than string width") + XCTAssertEqual(a.paddedToWidth(-2).characters.count, + a.characters.count, "Bad padding with negative pad width") + + let b = a.paddedToWidth(80) + let lastBCharIndex = b.endIndex.advancedBy(-1) + XCTAssertEqual(b[lastBCharIndex], " " as Character, "Failed to pad with default character") + + let c = a.paddedToWidth(80, padBy: "+") + let lastCCharIndex = c.endIndex.advancedBy(-1) + XCTAssertEqual(c[lastCCharIndex], "+" as Character, "Failed to pad with specified character") + } + + func testWrappedAtWidth() { + let lipsum = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." + for line in lipsum.wrappedAtWidth(80).splitByCharacter("\n") { + XCTAssertLessThanOrEqual(line.characters.count, 80, "Failed to wrap long line: \(line)") + } + + /* Words longer than the wrap width should not be split */ + let longWords = "Lorem ipsum consectetur adipisicing eiusmod tempor incididunt" + let lines = longWords.wrappedAtWidth(3).splitByCharacter("\n") + XCTAssertEqual(lines.count, 8, "Failed to wrap long words") + for line in lines { + XCTAssertGreaterThan(line.characters.count, 3, "Bad long word wrapping on line: \(line)") + } + } +} diff --git a/Carthage/Checkouts/CommandLine/LICENSE b/Carthage/Checkouts/CommandLine/LICENSE new file mode 100644 index 0000000..5c304d1 --- /dev/null +++ b/Carthage/Checkouts/CommandLine/LICENSE @@ -0,0 +1,201 @@ +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Carthage/Checkouts/CommandLine/README.md b/Carthage/Checkouts/CommandLine/README.md new file mode 100644 index 0000000..9dfe57e --- /dev/null +++ b/Carthage/Checkouts/CommandLine/README.md @@ -0,0 +1,146 @@ +CommandLine +=========== +A pure Swift library for creating command-line interfaces. + +*Note: CommandLine `master` requires Xcode 7 / Swift 2.0. If you're using older versions of Swift, please check out the [earlier releases](https://github.com/jatoben/CommandLine/releases).* + +Usage +----- +CommandLine aims to have a simple and self-explanatory API. + +```swift +import CommandLine + +let cli = CommandLine() + +let filePath = StringOption(shortFlag: "f", longFlag: "file", required: true, + helpMessage: "Path to the output file.") +let compress = BoolOption(shortFlag: "c", longFlag: "compress", + helpMessage: "Use data compression.") +let help = BoolOption(shortFlag: "h", longFlag: "help", + helpMessage: "Prints a help message.") +let verbosity = CounterOption(shortFlag: "v", longFlag: "verbose", + helpMessage: "Print verbose messages. Specify multiple times to increase verbosity.") + +cli.addOptions(filePath, compress, help, verbosity) + +do { + try cli.parse() +} catch { + cli.printUsage(error) + exit(EX_USAGE) +} + +println("File path is \(filePath.value!)") +println("Compress is \(compress.value)") +println("Verbosity is \(verbosity.value)") +``` + +See `Option.swift` for additional Option types. + +To use CommandLine in your project, add it to your workspace, then add CommandLine.framework to the __Build Phases / Link Binary With Libraries__ setting of your target. + +If you are building a command-line tool and need to embed this and other frameworks to it, follow the steps in http://colemancda.github.io/programming/2015/02/12/embedded-swift-frameworks-osx-command-line-tools/ to link Swift frameworks to your command-line tool. + +If you are building a standalone command-line tool, you'll need to add the CommandLine source files directly to your target, because Xcode [can't yet build static libraries that contain Swift code](https://github.com/ksm/SwiftInFlux#static-libraries). + + +Features +-------- + +### Automatically generated usage messages + +``` +Usage: example [options] + -f, --file: + Path to the output file. + -c, --compress: + Use data compression. + -h, --help: + Prints a help message. + -v, --verbose: + Print verbose messages. Specify multiple times to increase verbosity. +``` + +### Supports all common flag styles + +These command-lines are equivalent: + +```bash +$ ./example -c -v -f /path/to/file +$ ./example -cvf /path/to/file +$ ./example -c --verbose --file /path/to/file +$ ./example -cv --file /path/to/file +$ ./example --compress -v --file=/path/to/file +``` + +Option processing can be stopped with '--', [as in getopt(3)](https://www.gnu.org/prep/standards/html_node/Command_002dLine-Interfaces.html). + +### Intelligent handling of negative int & float arguments + +This will pass negative 42 to the int option, and negative 3.1419 to the float option: + +```bash +$ ./example2 -i -42 --float -3.1419 +``` + +### Locale-aware float parsing + +Floats will be handled correctly when in a locale that uses an alternate decimal point character: + +```bash +$ LC_NUMERIC=sv_SE.UTF-8 ./example2 --float 3,1419 +``` + +### Type-safe Enum options + +```swift +enum Operation: String { + case Create = "c" + case Extract = "x" + case List = "l" + case Verify = "v" +} + +let cli = CommandLine() +let op = EnumOption(shortFlag: "o", longFlag: "operation", required: true, + helpMessage: "File operation - c for create, x for extract, l for list, or v for verify.") +cli.setOptions(op) + +do { + try cli.parse() +} catch { + cli.printUsage(error) + exit(EX_USAGE) +} + +switch op.value { + case Operation.Create: + // Create file + + case Operation.Extract: + // Extract file + + // Remainder of cases +} +``` + +Note: Enums must be initalizable from a String value. + +### Fully emoji-compliant + +```bash +$ ./example3 -👍 --👻 +``` + +*(please don't actually do this)* + +License +------- +Copyright (c) 2014 Ben Gollmer. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. From bdc1a51ef97d9a61f9d214638a3fbebaa589d533 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihat=20Gu=CC=88ndu=CC=88z?= Date: Tue, 23 Feb 2016 17:26:34 +0100 Subject: [PATCH 2/5] Refactor code to use CommandLine --- BartyCrouch CLI/main.swift | 148 ++++++++---------- .../xcschemes/BartyCrouch CLI.xcscheme | 6 +- Sources/Code/IBToolCommander.swift | 4 +- 3 files changed, 68 insertions(+), 90 deletions(-) diff --git a/BartyCrouch CLI/main.swift b/BartyCrouch CLI/main.swift index 86ef76d..a6acb5c 100644 --- a/BartyCrouch CLI/main.swift +++ b/BartyCrouch CLI/main.swift @@ -8,125 +8,103 @@ import Foundation -let currentPath = Process.arguments[0] -let inputStoryboardArguments = ["--input-storyboard", "-in"] -let outputStringsFilesArguments = ["--output-strings-files", "-out"] -let outputAllLanguagesArguments = ["--output-all-languages", "-all"] +// Configure command line interface + +let cli = CommandLine() + +let input = StringOption( + shortFlag: "i", + longFlag: "input", + required: true, + helpMessage: "Path to your Storyboard or XIB source file to be translated." +) + +let output = MultiStringOption( + shortFlag: "o", + longFlag: "output", + required: false, + helpMessage: "A list of paths to your strings files to be incrementally updated." +) + +let auto = BoolOption( + shortFlag: "a", + longFlag: "auto", + required: false, + helpMessage: "Automatically finds all strings files to update based on the Xcode defaults." +) + +cli.addOptions(input, output, auto) + + +// Parse input data or exit with usage instructions + +do { + try cli.parse() +} catch { + cli.printUsage(error) + exit(EX_USAGE) +} + + +// Do requested action(s) enum OutputType { case StringsFiles - case AllLanguages + case Automatic case None } func run() { - - let inputStoryboardIndexOptional: Int? = { - for inputStoryboardArgument in inputStoryboardArguments { - if let index = Process.arguments.indexOf(inputStoryboardArgument) { - return index - } - } - return nil - }() - - guard let inputStoryboardIndex = inputStoryboardIndexOptional else { - print("Error! Missing input key '\(inputStoryboardArguments[0])' or '\(inputStoryboardArguments[1])'") - return - } - - guard inputStoryboardIndex+1 <= Process.arguments.count else { - print("Error! Missing input path after key '\(inputStoryboardArguments[0])' or '\(inputStoryboardArguments[1])'") - return - } - - let inputStoryboardPath = Process.arguments[inputStoryboardIndex+1] - - let outputStringsFilesIndexOptional: Int? = { - for outputStringsFilesArgument in outputStringsFilesArguments { - if let index = Process.arguments.indexOf(outputStringsFilesArgument) { - return index - } - } - return nil - }() - - let outputAllLanguagesIndexOptional: Int? = { - for outputAllLanguagesArgument in outputAllLanguagesArguments { - if let index = Process.arguments.indexOf(outputAllLanguagesArgument) { - return index - } - } - return nil - }() - + let outputType: OutputType = { - if outputStringsFilesIndexOptional != nil { + if output.wasSet { return .StringsFiles } - if outputAllLanguagesIndexOptional != nil { - return .AllLanguages + if auto.wasSet { + return .Automatic } return .None }() - guard outputType != .None else { - print("Error! Missing output key '\(outputStringsFilesArguments[1])' or '\(outputAllLanguagesArguments[1])'") - return - } - - let outputIndex: Int = { - switch outputType { - case .StringsFiles: - return outputStringsFilesIndexOptional! - case .AllLanguages: - return outputAllLanguagesIndexOptional! - case .None: - return -1 - } - }() - - guard outputType == .AllLanguages || outputIndex+1 <= Process.arguments.count else { - print("Error! Missing input path(s) after key '\(outputStringsFilesArguments[0])' or '\(outputStringsFilesArguments[1])'") - return - } + let inputIbFilePath = input.value! let outputStringsFilesPaths: [String] = { switch outputType { case .StringsFiles: - return Process.arguments[outputIndex+1].componentsSeparatedByString(",") - case .AllLanguages: - return StringsFilesSearch.sharedInstance.findAll(inputStoryboardPath) + return output.value! + case .Automatic: + return StringsFilesSearch.sharedInstance.findAll(inputIbFilePath) case .None: - return [] + print("Error! Missing output key '\(output.shortFlag!)' or '\(auto.shortFlag!)'.") + exit(EX_USAGE) } }() - guard NSFileManager.defaultManager().fileExistsAtPath(inputStoryboardPath) else { - print("Error! No file exists at input path '\(inputStoryboardPath)'") - return + guard NSFileManager.defaultManager().fileExistsAtPath(inputIbFilePath) else { + print("Error! No file exists at input path '\(inputIbFilePath)'") + exit(EX_NOINPUT) } for outputStringsFilePath in outputStringsFilesPaths { guard NSFileManager.defaultManager().fileExistsAtPath(outputStringsFilePath) else { - print("Error! No file exists at output path '\(outputStringsFilePath)'") - return + print("Error! No file exists at output path '\(outputStringsFilePath)'.") + exit(EX_CONFIG) } } - let extractedStringsFilePath = inputStoryboardPath + ".tmpstrings" + let extractedStringsFilePath = inputIbFilePath + ".tmpstrings" - guard IBToolCommander.sharedInstance.export(stringsFileToPath: extractedStringsFilePath, fromStoryboardAtPath: inputStoryboardPath) else { - print("Error! Could not extract strings from Storyboard at path '\(inputStoryboardPath)'") - return + guard IBToolCommander.sharedInstance.export(stringsFileToPath: extractedStringsFilePath, fromIbFileAtPath: inputIbFilePath) else { + print("Error! Could not extract strings from Storyboard or XIB at path '\(inputIbFilePath)'.") + exit(EX_UNAVAILABLE) } for outputStringsFilePath in outputStringsFilesPaths { guard let stringsFileUpdater = StringsFileUpdater(path: outputStringsFilePath) else { - print("Error! Could not update strings file at path '\(outputStringsFilePath)'") - return + print("Error! Could not read strings file at path '\(outputStringsFilePath)'") + exit(EX_CONFIG) } stringsFileUpdater.incrementallyUpdateKeys(withStringsFileAtPath: extractedStringsFilePath) @@ -135,12 +113,12 @@ func run() { do { try NSFileManager.defaultManager().removeItemAtPath(extractedStringsFilePath) - print("BartyCrouch: Successfully updated Strings files from Storyboard.") } catch { print("Error! Temporary strings file couldn't be deleted at path '\(extractedStringsFilePath)'") - return + exit(EX_IOERR) } + print("BartyCrouch: Successfully updated strings file(s) of Storyboard or XIB file.") } diff --git a/BartyCrouch.xcodeproj/xcshareddata/xcschemes/BartyCrouch CLI.xcscheme b/BartyCrouch.xcodeproj/xcshareddata/xcschemes/BartyCrouch CLI.xcscheme index c764e10..45b6e1a 100644 --- a/BartyCrouch.xcodeproj/xcshareddata/xcschemes/BartyCrouch CLI.xcscheme +++ b/BartyCrouch.xcodeproj/xcshareddata/xcschemes/BartyCrouch CLI.xcscheme @@ -63,12 +63,12 @@ + argument = "-i "/Users/Dschee/Code/Flinesoft/iOS/Cruciverber/Sources/User Interface/Base.lproj/Main.storyboard" -o "/Users/Dschee/Code/Flinesoft/iOS/Cruciverber/Sources/User Interface/de.lproj/Main.strings" "/Users/Dschee/Code/Flinesoft/iOS/Cruciverber/Sources/User Interface/en.lproj/Main.strings"" + isEnabled = "YES"> diff --git a/Sources/Code/IBToolCommander.swift b/Sources/Code/IBToolCommander.swift index cf851aa..9fa489b 100644 --- a/Sources/Code/IBToolCommander.swift +++ b/Sources/Code/IBToolCommander.swift @@ -18,8 +18,8 @@ public class IBToolCommander { // MARK: - Instance Methods - public func export(stringsFileToPath stringsFilePath: String, fromStoryboardAtPath storyboardPath: String) -> Bool { - let (_, exitCode) = self.shell(["ibtool", "--export-strings-file", stringsFilePath, storyboardPath]) + public func export(stringsFileToPath stringsFilePath: String, fromIbFileAtPath ibFilePath: String) -> Bool { + let (_, exitCode) = self.shell(["ibtool", "--export-strings-file", stringsFilePath, ibFilePath]) if exitCode == 0 { return true From 7dd9b426f150f8d8becc5b9c536660247c759bb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihat=20Gu=CC=88ndu=CC=88z?= Date: Tue, 23 Feb 2016 17:27:53 +0100 Subject: [PATCH 3/5] Remove test command line arguments --- .../xcshareddata/xcschemes/BartyCrouch CLI.xcscheme | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/BartyCrouch.xcodeproj/xcshareddata/xcschemes/BartyCrouch CLI.xcscheme b/BartyCrouch.xcodeproj/xcshareddata/xcschemes/BartyCrouch CLI.xcscheme index 45b6e1a..824ccc7 100644 --- a/BartyCrouch.xcodeproj/xcshareddata/xcschemes/BartyCrouch CLI.xcscheme +++ b/BartyCrouch.xcodeproj/xcshareddata/xcschemes/BartyCrouch CLI.xcscheme @@ -61,16 +61,6 @@ ReferencedContainer = "container:BartyCrouch.xcodeproj"> - - - - - - From ea860b629d06b5927e46859ecd610d9451904ff6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihat=20Gu=CC=88ndu=CC=88z?= Date: Tue, 23 Feb 2016 17:41:45 +0100 Subject: [PATCH 4/5] Update README to reflect changes to argument names --- README.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 773bb8b..c2c7f37 100644 --- a/README.md +++ b/README.md @@ -43,29 +43,29 @@ Before using BartyCrouch please **make sure you have committed your code**. The `bartycrouch` main command accepts one of the following two combinations of arguments: -1. Input-Storyboard and Output-Strings-Files -2. Input-Storyboard and Output-All-Languages +1. Input and Output +2. Input and Auto -#### Input-Storyboard (aka `-in`) +#### Input (aka `-i`) -You can specify the input storyboard file using `--input-storyboard "path/to/my.storyboard"` or `-in "path/to/my.storyboard"` using the shorthand syntax. +You can specify the input Storyboard or XIB file using `--input "path/to/my.storyboard"` or `-i "path/to/my.storyboard"` using the shorthand syntax. -#### Output-Strings-Files (aka `-out`) +#### Output (aka `-o`) -You can pass a list of `strings` files to be incrementally updated using `--output-strings-files "path/to/en.strings,path/to/de.strings"` or `-out "path/to/en.strings,path/to/de.strings"` using the shorthand syntax. +You can pass a list of `.strings` files to be incrementally updated using `--output "path/to/en.strings" "path/to/de.strings"` or `-o "path/to/en.strings" "path/to/de.strings"` using the shorthand syntax. -#### Output-All-Languages (aka `-all`) +#### Auto (aka `-a`) -If you use base internationalization (recommended) you can also let BartyCrouch find and update all `.strings` files automatically by passing `--output-all-languages` or `-all` using the shorthand syntax. +If you use base internationalization (recommended) you can also let BartyCrouch find and update all `.strings` files automatically by passing `--auto` or `-a` using the shorthand syntax. ### Complete Examples All of the above put together, you can run the following (replace `path`): ``` shell -bartycrouch -in "path/Base.lproj/Main.storyboard" -out "path/en.lproj/Main.strings" -bartycrouch -in "path/Base.lproj/Main.storyboard" -out "path/en.lproj/Main.strings,path/de.lproj/Main.strings" -bartycrouch -in "path/Base.lproj/Main.storyboard" -all +bartycrouch -i "path/Base.lproj/Main.storyboard" -o "path/en.lproj/Main.strings" +bartycrouch -i "path/Base.lproj/Main.storyboard" -o "path/en.lproj/Main.strings" "path/de.lproj/Main.strings" +bartycrouch -i "path/Base.lproj/Main.storyboard" -a ``` ### Build Script @@ -74,19 +74,19 @@ You may want to **update your `.strings` files on each build automatically** wha ``` shell if which bartycrouch > /dev/null; then - # Set path to base internationalized storyboards + # Set path to base internationalized Storyboard/XIB files BASE_PATH="$PROJECT_DIR/Sources/User Interface/Base.lproj" - # Incrementally update all storyboards strings files - bartycrouch -in "$BASE_PATH/Main.storyboard" -all - bartycrouch -in "$BASE_PATH/LaunchScreen.storyboard" -all - bartycrouch -in "$BASE_PATH/CustomView.xib" -all + # Incrementally update all Storyboards/XIBs strings files + bartycrouch -i "$BASE_PATH/Main.storyboard" -a + bartycrouch -i "$BASE_PATH/LaunchScreen.storyboard" -a + bartycrouch -i "$BASE_PATH/CustomView.xib" -a else echo "BartyCrouch not installed, download it from https://github.com/Flinesoft/BartyCrouch" fi ``` -Add a `bartycrouch -in ... -all` line for each of your base internationalized storyboards/xibs and you're good to go. Xcode will now run BartyCrouch each time you build your project and update your `.strings` files accordingly. +Add a `bartycrouch -i ... -a` line for each of your base internationalized Storyboards/XIBs and you're good to go. Xcode will now run BartyCrouch each time you build your project and update your `.strings` files accordingly. *Note: Please make sure you commit your code using source control regularly when using the build script method.* From 39b327d67455fdae1d9e64052494d7da6f1851fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cihat=20Gu=CC=88ndu=CC=88z?= Date: Tue, 23 Feb 2016 18:04:55 +0100 Subject: [PATCH 5/5] Add migration guides section with first entry --- README.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c2c7f37..32c2148 100644 --- a/README.md +++ b/README.md @@ -48,11 +48,11 @@ The `bartycrouch` main command accepts one of the following two combinations of #### Input (aka `-i`) -You can specify the input Storyboard or XIB file using `--input "path/to/my.storyboard"` or `-i "path/to/my.storyboard"` using the shorthand syntax. +You can specify the input Storyboard or XIB file using `-i "path/to/my.storyboard"` (`-i` is short `--input`). #### Output (aka `-o`) -You can pass a list of `.strings` files to be incrementally updated using `--output "path/to/en.strings" "path/to/de.strings"` or `-o "path/to/en.strings" "path/to/de.strings"` using the shorthand syntax. +You can pass a list of `.strings` files to be incrementally updated using `-o "path/to/en.strings" "path/to/de.strings"` (`-o` is short for `--output`). #### Auto (aka `-a`) @@ -100,6 +100,19 @@ Here's an example of how a base localized view in a XIB file with partly ignored +## Migration Guides + +This project follows [Semantic Versioning](http://semver.org). Please follow the appropriate guide below when **upgrading to a new major version** of BartyCrouch (e.g. 0.3 -> 1.0). + +### Upgrade from 0.x to 1.x + +- `--input-storyboard` and `-in` were **renamed** to `--input` and `-i` +- `--output-strings-files` and `-out` were **renamed** to `--output` and `-o` +- Multiple paths passed to `-output` are now **separated by whitespace instead of comma** + - e.g. `-out "path/one,path/two"` should now be `-o "path/one" "path/two"` +- `--output-all-languages` and `-all` were **renamed** to `--auto` and `-a` + + ## Contributing Contributions are welcome. Please just open an Issue on GitHub to discuss a point or request a feature there or send a Pull Request with your suggestion. Please also make sure to write tests for your changes in order to make sure they don't break in the future. Please note that there is a framework target within the project alongside the command line utility target to make testing easier.