其实已经有一个不错的用Swift写的命令行工具检测不用的图片资源了,就是喵神的FengNiao,至于为何要再写一个呢,主要是为了学习SwiftSyntax,前段时间我写了篇文章简单的介绍了SwiftSyntax,文章在这里SwiftSyntax详解,对SwiftSyntax不熟悉的能够先看看。node
项目在这里UnusedResources,下面来简单介绍一下原理。python
我这里找的图片资源有如下几种:git
let imageExtensions: [String] = ["png", "jpg", "gif", "pdf"]
复制代码
这里不得不提如下Homebrew的做者mxcl开源的库Path,这个库真的很好用。咱们先定义一个方法来来查找所需扩展名的文件github
func recursiveFiles(withExtensions exts: [String], at path: Path) throws -> [Path] {
if path.isFile {
if exts.contains(path.extension) {
return [path]
}
return []
} else if path.isDirectory {
var files: [Path] = []
for entry in try path.ls() {
let list = try recursiveFiles(withExtensions: exts, at: entry.path)
files.append(contentsOf: list)
}
return files
}
return []
}
复制代码
找到全部的图片资源就只要下面简单的一段代码就能够了json
var images: [Path] = try recursiveFiles(withExtensions: imageExtensions, at: path)
复制代码
找到源代码文件swift
var files: [Path] = try recursiveFiles(withExtensions: ["swift", "h", "m", "mm"], at: path)
复制代码
这里就须要用到SwiftSyntax,咱们定义一个StringVisitor遍历语法树,提取字符串并判断是否和图片名称同样,同样就说明图片被使用了。bash
咱们一个简单的swift代码的语法树,能够用xcrun swiftc -frontend -emit-syntax ./Cat.swift | python -m json.tool
命令查看源代码语法树结构,可是仍是比较复杂,包含import库的部分,因此我选择用Swift AST Explorer,这个是基于SwiftSyntax实现,语法树更简洁。 app
咱们只要拿到StringSegmentSyntax的内容就行了,而后和图片进行对比,和图片同样,就表示图片被使用到了。frontend
public struct StringVisitor: SyntaxVisitor {
public var images: [Path] = []
public init(_ images: [Path]) {
self.images = images
}
// Ignore string interpolated literal because it's too complex to compare public mutating func visit(_ node: StringLiteralExprSyntax) -> SyntaxVisitorContinueKind { guard node.segments.count == 1 else { return .skipChildren } return .visitChildren } public mutating func visit(_ node: StringSegmentSyntax) -> SyntaxVisitorContinueKind { let text = node.content.text .trimmingCharacters(in: .whitespacesAndNewlines) guard !text.isEmpty else { return .skipChildren } images = images.filter { $0.basename(dropExtension: true) != text } return .skipChildren } } 复制代码
OC和Swift的语法书稍微有点不同,因此咱们能够获得全部的TokenSyntax,而后找到类型是stringLiteral的,这里我处理比较简单,直接判断stringLiteral是否包含图片名称,若是包含就表示图片被使用了。工具
因此咱们再给StringVisitor添加一个方法
// for Objective-C
public mutating func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind {
switch token.tokenKind {
case .stringLiteral(let text):
images = images.filter { !text.contains($0.basename(dropExtension: true)) }
default:
break
}
return .visitChildren
}
复制代码
let xmlExtensions: [String] = ["xib", "storyboard"]
var xmlFiles: [Path] = try recursiveFiles(withExtensions: xmlExtensions, at: path)
复制代码
这里须要用到XMLParser找到attributeDict中key为“image”的value,若是和图片名称同样就表示图片被使用了。
使用Makefile来build项目,自动将生成的unused-resources拷贝到/usr/local/bin
中。
水文一篇,最近在研究编译相关的知识,进展十分缓慢,心累。