- 开发工具
【免费下载链接】synx
A command-line tool that reorganizes your Xcode project folder to match your Xcode groups
导读
synx 是一个面向 Xcode 开发者的命令行工具,它解析.xcodeproj工程文件,把真实的文件系统目录结构重排成与 Xcode 侧边栏中的 groups 完全一致,解决“工程里看得到、Finder 里找不到”“目录乱糟糟、group 结构对不上”的经典混乱。本文将以 README.md 为骨架,结合仓库源码(lib/synx、bin/synx)与测试用例(spec),完整讲解 synx 的安装、基本用法、全部命令行参数、实际应用示例以及底层同步原理,读完后你可以安全地在自己的 iOS/macOS 工程上重排目录结构。
一个常见痛点:Xcode Groups 与磁盘文件夹脱节
在 Xcode 中,“Group”(组)只是工程导航视图里的逻辑容器,它和磁盘上的真实目录并没有强绑定关系。开发者手动拖动文件、复制目录、从其他地方粘贴资源之后,常常出现:
- Xcode 导航器里井井有条的层级,在 Finder 中却是扁平或错乱的;
- 工程中引用了某个文件,但磁盘上文件的实际位置与 group 路径不一致;
- 目录里堆着大量“工程里根本没用到的”源码与图片资源。
synx 的定位正是解决这一痛点:让磁盘目录结构回归到与 Xcode groups 一一对应的状态。官方对它的定义是:
A command-line tool that reorganizes your Xcode project folder to match your Xcode groups.
仓库的 synx.gemspec 补充了更精确的描述:synx 解析你的.xcodeproj,然后在文件系统上重建出与 group 结构相同的目录结构(Synx parses your .xcodeproj to build the same group structure out on the file system.)。
下面这张 Finder 前后对比图直观展示了同步前后的差异(图片来自 docs/images/synx-finder-before-after.png):
安装
synx 以 Ruby Gem 形式分发,安装命令与 README 完全一致:
$ gem install synx从仓库的 synx.gemspec 可以看到它的运行时依赖为:
clamp ~> 0.6:命令行参数解析框架(bin/synx 中的选项与子命令即由 clamp 定义);colorize ~> 0.7:终端彩色输出;xcodeproj ~> 1.0:官方 Xcode 工程解析库,synx 的所有工程读写能力都建立在它之上。
安装完成后,synx命令即通过bin/可执行文件对外暴露。此外,synx 还提供了一个 README 中未列出的版本查询能力(由 CHANGELOG.md v0.0.6 引入,实现在 bin/synx 中):
$ synx --version # 输出形如:Synx 0.2.1(版本号来自 lib/synx/version.rb)基本用法
第一步:备份(务必执行)
README 首先给出了醒目警告:
WARNING: Make sure that your project is backed up through source control before doing anything
synx 会真实地移动磁盘文件并改写.pbxproj,因此运行前请确保工程已纳入 Git 等版本控制,以便随时回滚。这一要求也可以从实现层面理解:同步过程会遍历文件系统执行FileUtils.mv(见 lib/synx/xcodeproj_ext/project/object/pbx_file_reference.rb),并在结束时保存工程,属于“移动 + 改写”型操作。
第二步:执行同步
对工程执行命令,让它重排文件系统上的文件:
$ synx path/to/my/project.xcodeproj注意参数是.xcodeproj工程的路径,不是工程所在目录。bin/synx中将其声明为位置参数parameter "xcodeproj",并在执行时通过Synx::Project.open(xcodeproj_path)打开工程。
第三步(可选):CocoaPods 用户执行 pod install
README 特别提醒:同步操作“可能会让 CocoaPods 感到困惑”,如果项目使用 CocoaPods,同步后应重新执行:
$ pod install这条提示与源码实现是对应的:lib/synx/project.rb中默认排除列表包含/Pods组,同时整个同步会改写.pbxproj中的文件引用与路径,因此重新执行pod install可以让 Pods 相关的工程配置回到一致状态。
高级选项详解
README 给出了 synx 支持的全部命令行选项。结合 bin/synx 的 clamp 声明,完整参数清单如下:
| 选项 | 短选项 | 作用(来自 README) |
|---|---|---|
--prune | -p | 删除未被 Xcode 工程引用的源文件与图片资源 |
--no-color | — | 去除输出中的所有颜色 |
--no-default-exclusions | — | 不使用默认排除项/Libraries、/Frameworks、/Products |
--no-sort-by-name | — | 禁用按名称对 group 排序 |
--quiet | -q | 静默所有输出 |
--exclusion EXCLUSION | -e | 同步时忽略某个 Xcode group(可多次指定) |
下面逐一深入讲解每个参数的实现与使用要点。
--prune, -p:清除工程未引用的源文件与图片
同步时,synx 会检查磁盘目录中是否存在“不被任何 Xcode 对象引用”的条目。该逻辑实现在 lib/synx/xcodeproj_ext/project/object/pbx_group.rb 的handle_unused_file中:
- 被判定为“可清理的源码/图片类型”包括:
.h、.m、.swift、.mm、.c、.xib、.png、.jpg、.jpeg; - 开启
--prune时,这些未被引用的源码与图片会被直接删除(输出中标记为 removed); - 未开启
--prune时,它们会被移动到新结构中保留下来,但会以黄色警告提示“未被 Xcode 工程引用”; - 其他类型的文件(如
.xml、.json)无论是否开启 prune 都只移动不删除。
注意:.DS_Store这类 macOS 系统文件会被永久忽略(源码中entries_to_ignore列表),不会被移动也不会被清理。
--no-color:关闭彩色输出
synx 的终端输出大量使用颜色区分状态:绿色表示正常同步的分组、黄色表示被排除/未被引用的文件、红色表示被删除/被跳过或出错的条目。颜色功能来自colorizeGem,关闭逻辑实现在 lib/synx/tabber.rb:当options[:no_color]为真时,输出前会先调用uncolorize剥去颜色码。适合在 CI 日志或纯文本管道中使用。
--no-default-exclusions:取消默认排除组
同步前,synx 会默认排除若干固定 group,它们的定义在 lib/synx/project.rb:
DEFAULT_EXCLUSIONS = %W(/Libraries /Frameworks /Products /Pods)也就是说:/Libraries、/Frameworks、/Products以及/Pods这四个顶层组默认不会被同步、不会被移动。README 中描述的默认排除是前三个,而当前仓库源码(并见 CHANGELOG.md v0.0.53“Pods as a default exclusion”)实际还包括/Pods,使用时应以当前版本为准。加上--no-default-exclusions后,group_exclusions会被置为空数组,即取消所有默认排除。
--no-sort-by-name:关闭按名称排序
默认情况下 synx 还会让 group 的子项按名称排序,排序规则实现在 pbx_group.rb 的sort_by_name:group 永远排在文件前面,同类型内部再按display_name字典序排列。如果你希望保持工程文件中原有的顺序,可以加上--no-sort-by-name。
--quiet, -q:静默模式
Tabber.puts在options[:quiet]为真时完全放弃输出(lib/synx/tabber.rb),适合脚本化调用。测试用例 spec/synx/tabber_spec.rb 对该行为有明确的断言。
--exclusion, -e EXCLUSION:排除指定 group
这是最有针对性的参数:告诉 synx“这个 group 不要动”。每次传入一个 group 路径(以/开头的层级路径),可重复传入多次。排除路径的处理规则在 lib/synx/project.rb 的group_exclusions=中:
- 路径自动以
/开头补全,因此写dummy与/dummy效果相同(“对缺失/宽容”这一点在 spec/synx/project_spec.rb 中有专门测试); - 会校验该 group 是否真实存在:若不存在会直接抛出
IndexError: No group ... exists; - 校验逻辑对默认排除组(
/Libraries等)放行,因为用户工程里未必有这些组。
实战示例:OCMock 场景
README 用 OCMock 项目演示了组合使用多个参数:
$ synx -p -e "/OCMock/Core Mocks" -e /OCMockTests Source/OCMock.xcodeproj/这个命令的含义是:
-e "/OCMock/Core Mocks"与-e /OCMockTests:同步时跳过这两个 group(及其下的文件与子组),保持它们原样;-p:删除 synx 发现的所有未被任何 Xcode group 引用的源码/图片文件;Source/OCMock.xcodeproj/:目标工程路径。
这是“部分整理 + 清理冗余”的典型组合:希望保留某些结构不动(如测试目录、特定模块),同时让其余部分严格对齐 group,并顺手清掉孤儿文件。
同步原理:从源码看 synx 做了什么
同步主流程
入口在 lib/synx/project.rb 的sync方法,流程为:
- presync_check 预检查:扫描所有 group,若发现任何 group 名称中包含
/,则中止并列出问题组清单。因为这种命名无法在文件系统上建立一一对应的一层目录,见presync_check与 pbx_group.rb 的groups_containing_forward_slash(测试见 spec/synx/xcodeproj_ext/project/object/pbx_group_spec.rb)。 - 同步工程内文件:遍历
main_group下所有 group,逐个把文件移动到“工作区”中与 group 层级一致的位置(gr.sync(main_group))。 - 处理未被引用的条目:
move_entries_not_in_xcodeproj检查每个目录的真实内容,将不属于工程的条目移走或按 prune 选项删除。 - 排序与回填:按需对 group 排序,再把工作区的完整结构“移植”回工程目录。
- 保存:将改动写回
.pbxproj。
两次遍历的“工作区”设计
值得注意的实现细节是:synx 并没有直接在当前目录里搬来搬去,而是先在临时工作区中构建新结构,最后一次性移植回来:
work_root_pathname位于$HOME/.synx/<工程目录名>,每次运行都会先清空再重建(FileUtils.rm_rf+mkpath),保证幂等;- 同步期间文件先移动到工作区对应路径(
pathname_to_work_pathname); - 完成后再用
transplant_work_project把工作区内容整体移回工程根目录。
这种设计保证了“先全部按新结构放好,再统一替换”,避免中途状态破坏现有工程。相关测试见 spec/synx/project_spec.rb 的#work_root_pathname组。
文件引用的路径修复与构建设置修正
移动文件后,.pbxproj中的引用路径需要同步更新,这部分逻辑在 pbx_file_reference.rb:
should_move?:文件在工程根目录内、且不是.xcodeproj本身时,才被移动;位于工程外的文件(比如引用 SDK 目录)只修正相对引用,不移动;should_sync?:跳过磁盘上不存在、或路径含$(SDKROOT)、$(DEVELOPER_DIR)、$(BUILT_PRODUCTS_DIR)等构建变量占位的文件;change_build_settings_reference:当移动的是.pch或.plist时,会遍历所有 target 的 build settings,同步改写INFOPLIST_FILE与GCC_PREFIX_HEADER的路径。这正是 spec 中断言“pch 与 info.plist 的 build setting 路径被更新”所验证的行为(见 spec/synx/project_spec.rb)。
重复引用与多父组的自动修复
同步过程还会顺带修复工程文件里的一些“历史遗留问题”:
squash_duplicate_file_references:同一路径被同一个 group 引用了多次时,只保留一个引用;ensure_internal_consistency:同一文件被多个 group 引用时,只保留当前遍历到的那个父组,并在输出中提示 “had multiple parent groups, removed from groups: ...”(见 abstract_object.rb)。
本地化(.lproj)文件的特殊处理
Xcode 的本地化文件(PBXVariantGroup)在磁盘上会折叠成同名目录下的多个.lproj子目录,无法与 group 结构一一对应。work_pathname中对这类对象做了专门分支:本地化对象被放到父组路径/<显示名>.lproj/<显示名>(见 abstract_object.rb),这也是 CHANGELOG.md 提到的“Make synx work for localized files / en.lproj syncing bug fix”所对应的能力。
输出风格
同步过程的终端输出由 lib/synx/tabber.rb 管理:每级缩进为两个空格,increase/decrease控制层级,配合--no-color与--quiet即可控制输出样式。spec/synx/tabber_spec.rb对缩进、静默、去色等行为均有覆盖。
测试与验证方式
仓库自带完整的 RSpec 测试,核心在 spec/synx/project_spec.rb:
- 使用
spec/dummy下的示例工程(含 dummy 与 dummyTests 两个 target、本地化资源、Core Data 模型、故意放置的未引用文件等)作为输入; - 通过 spec/synx/expected_file_structure.yml 与 spec/synx/expected_group_structure.yml 两份预期结构,分别校验同步后的磁盘文件结构与Xcode group 结构;
- 验证点包括:group 结构除修复重复引用外不被改动、pch/plist 的 build setting 路径被更新、
prune模式下未引用的image-not-in-xcodeproj.png、FileNotInXcodeProj.h等被删除、no_default_exclusions会清空排除列表、group_exclusions校验不存在组会抛IndexError等。
如果你想在本地验证,可运行:
$ bundle install $ bundle exec rspec使用注意事项与限制
结合 README 警告与源码约束,使用 synx 前应了解以下几点:
- 必须先行版本控制备份:工具会真实移动文件并改写
.pbxproj,没有版本控制兜底时风险较高; - 不要以 root 运行:
bin/synx在Process.uid == 0时会拒绝执行并提示 “You cannot run Synx as root.”,这是为了避免因文件权限导致工程损坏(见 CHANGELOG.md v0.0.6); - group 名称中不能包含
/:否则同步会直接中止,必须先重命名这些组; -e指定的组必须真实存在:否则会报IndexError;- 默认排除组含
/Pods:README 只列出三个,当前源码的DEFAULT_EXCLUSIONS实际是/Libraries、/Frameworks、/Products、/Pods四个,以当前仓库实现为准; - CocoaPods 用户同步后建议重跑
pod install; - synx 面向 Xcode/iOS/macOS 工程场景,依赖 macOS 或兼容环境下的
xcodeproj、FileUtils等能力,在非 Apple 平台上无法发挥实际作用。
结语
synx 用一次命令行调用解决了 Xcode 工程“导航视图与磁盘目录脱节”的长期痛点:以.xcodeproj中的 groups 为唯一权威,重排磁盘文件、修正引用与构建设置、可选清理孤儿资源,并内置默认排除、局部排除与输出控制等精细选项。本文基于 README.md 展开,结合 lib/synx 源码与 spec 测试给出了参数背后的实现依据,你可以在充分备份后按synx -p -e等组合参数在自己的工程上实践,让 Finder 与 Xcode 重新变得“所见即所得”。
- 开发工具
【免费下载链接】synx
A command-line tool that reorganizes your Xcode project folder to match your Xcode groups
相关推荐
如何使用spring4shell-scan快速发现Spring Cloud RCE漏洞(CVE-2022-22963)
如何使用spring4shell scan快速发现Spring Cloud RCE漏洞(CVE 2022 22963) spring4shell scan是一款
终极指南:如何使用 Synx 工具快速重构 Xcode 项目文件结构
终极指南:如何使用 Synx 工具快速重构 Xcode 项目文件结构 Synx 是一款强大的命令行工具,能够帮助开发者自动将 Xcode 项目的文件夹结构重构为
开发工具探索GDU:一款强大的命令行磁盘清理工具
探索GDU:一款强大的命令行磁盘清理工具 项目简介 是一个由Dundee开发的高效、易用的命令行磁盘使用情况分析器,适用于Linux、macOS和Windows
CLI开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考