前言
最近看到一个有趣的东西,用swift编写脚本,实现诸如自动打包程序呀,自动扫描项目中未使用的文件呀等等。其中就用到了用swift编写CLT(commandlinetool)。
看完后我觉得可以做个练习,写了个列出项目中各编程语言文件占比的脚本程序。
创建项目
Swift Package Manager 为我们提供了不亚于cocoapods的包管理能力,这里我们就使用 SPM 来构建和管理我们的项目。
准备一个空的文件夹FileCounter
,输入如下命令来创建模版文件:
swift package init --type executable // 创建可执行程序的模版
swift package generate-xcodeproj // 生成*.xcodeproj工程
复制代码
--type executable 表示我们创建的是一个可执行程序,默认不传是Dynamic Library类型。这个可以在Target->Build Setting->Mach-O Type 下查看。
此时,大家的文件结构应该如下所示:
.
├── FileCounter.xcodeproj
│ ├── FileCounterTests_Info.plist
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ └── xcschemes
│ ├── FileCounter-Package.xcscheme
│ └── FileCounter.xcscheme
├── Package.swift
├── README.md
├── Sources
│ └── FileCounter
│ └── main.swift
└── Tests
└── FileCounterTests
└── FileCounterTests.swift
9 directories, 10 files
复制代码
其中main.swift
就是我们的程序入口,也就是我们要编写代码的地方。打开项目FileCounter.xcodeproj
,添加一个依赖库,修改Package.swift
文件如下:
let package = Package(
name: "FileCounter",
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser", from: "0.4.0"),
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "FileCounter",
dependencies: [.product(name: "ArgumentParser", package: "swift-argument-parser"),]),
]
)
复制代码
修改依赖配之后,我们需要下载并更新我们的项目:
swift package update
swift build generate-xcodeproj
复制代码
至此,项目的环境就算搭好了。
思路
这个程序的逻辑很简单,通过遍历项目的所有文件,读取文件的后缀判断这个文件属于哪个编程语言的,从而实现语言占比。例如 Objective-C
的文件后缀有 .h,.m,.mm
,Swift
的文件后缀有 .swift
。其他没有后缀的都统一归为其他即可。
实现
/// 编程语言
struct CodeLanguage {
var itemType: ItemType = .other
var name: String {
switch itemType {
case .oc:
return "Objective-C"
case .swift:
return "Swift"
case .c:
return "C/C++"
case .ruby:
return "Ruby"
case .shell:
return "Shell"
case .java:
return "Java"
default:
return "Other"
}
}
var count: Int = 0
static func +=( lhs: inout CodeLanguage, rhs: Int) {
lhs.count = lhs.count + rhs
}
}
/// 文件对象
struct FileItem {
var name: String
var absolutePath: String
var itemType: ItemType = .other
}
/// 列出文件夹下所有的文件对象:FileItem
func list(path: String) throws -> [FileItem] {
let l = try FileManager.default.subpathsOfDirectory(atPath: path)
var items = [FileItem]()
for p in l {
if p.isDirectory() {
continue
}
let url = URL(string: path)?.appendingPathComponent(p)
let absolutePath = url?.path ?? "\(path)\\\(p)"
var item = FileItem(name: p.name(), absolutePath: absolutePath)
// 根据文件后缀辨识编程语言种类
let extname = absolutePath.extname()
switch extname {
case "h","m", "mm":
item.itemType = .oc
case "swift":
item.itemType = .swift
case "c", "hpp", "cpp":
item.itemType = .c
case "rb":
item.itemType = .ruby
case "sh":
item.itemType = .shell
case "java":
item.itemType = .java
default:
item.itemType = .other
}
items.append(item)
}
return items
}
复制代码
脚本逻辑实现:
import Foundation
import ArgumentParser
struct FileCounter: ParsableCommand {
/// 用户输入参数
@Argument(help: "List all files")
var file: String?
func run() throws {
let root = currentPath()
print(root)
var total = 0
var oc = CodeLanguage(itemType: .oc)
var swift = CodeLanguage(itemType: .swift)
var c = CodeLanguage(itemType: .c)
var ruby = CodeLanguage(itemType: .ruby)
var sh = CodeLanguage(itemType: .shell)
var java = CodeLanguage(itemType: .java)
var other = CodeLanguage(itemType: .other)
do {
let _l = try list(path: root)
for item in _l {
total += 1
switch item.itemType {
case .oc:
oc += 1
case .swift:
swift += 1
case .c:
c += 1
case .ruby:
ruby += 1
case .shell:
sh += 1
case .java:
java += 1
default:
other += 1
}
}
print("Total \(total) files percentile:")
let inputc = true
let filter = file != nil
// 根据文件数降序
var sorts = [oc, swift, c, ruby, java,sh, other]
sorts.sort { (c1, c2) -> Bool in
c1.count > c2.count
}
for c in sorts {
let p = Float(c.count) / Float(total)
if p == 0 {
continue
}
var pstr = "\(p * 100)"
if pstr.contains(".") {
let parr = pstr.components(separatedBy: ".")
let last = parr.last ?? ""
if Int(last) != 0 {
var pindex = 1
for c in last {
if c == "0" {
pindex += 1
} else {
break
}
}
pstr = String(format: "%.\(pindex)f", p * 100)
} else {
pstr = parr.first ?? ""
}
}
var output = "\(c.name)"
if filter {
if file!.lowercased() != c.name.lowercased() {
continue
}
}
if inputc {
output += " \(c.count)"
}
output += " "
output += pstr
output += "%"
print(output)
}
}
}
}
最后执行脚本程序:
FileCounter.main()
复制代码
我们编写完程序,可以直接运行Xcode run进行测试,当然也可以直接编译生成一个可执行文件。通过以下命令即可。
在项目根目录输入:
swift package -c release
复制代码
这会在./build/release/
目录下生成我们需要的可执行文件。
cd .build/release
cp -f FileCounter /usr/local/bin/fcounter
复制代码
cp(copy file)命令主要用于复制文件或目录。cp -f: 覆盖已经存在的目标文件而不给出提示。
这样我们可以直接在我们任意项目下执行命令:fcounter
或者 fcounter swift
,即可列出所有编程语言占比,或是我们想要的编程语言的占比。
作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS开发交流群:130 595 548,不管你是小白还是大牛都欢迎入驻 ,让我们一起进步,共同发展!(群内会免费提供一些群主收藏的免费学习书籍资料以及整理好的几百道面试题和答案文档!)
最终实现效果:
/Users/zl/Developer/Project/你的项目根目录
Total 18027 files percentile:
Other 10053 55.8%
Objective-C 7252 40.2%
Swift 580 3.2%
C/C++ 136 0.8%
Shell 4 0.02%
Ruby 2 0.01%
网友评论