2

首先,让我说是的,这个问题可能是主观的。但是,如果考虑到所有相关因素,我相信可能会有一个“最佳”答案。无论如何,值得试一试并询问:)

假设我有三个库,A、B 和 C。

库 B 使用库 A。库 C 使用库 A。

我希望人们能够同时使用 A、B 和 C,或者如果他们愿意,可以任意组合使用 A、B 和 C。

我希望能够分发带有源代码的库,以便人们可以根据需要自行构建它们,或者只是抓取并使用单个文件。

我真的不想将它们分布在一个大的整体块中。

除了散装问题,我不想这样做是有充分理由的。假设 B 对它旨在使用的其他一些库具有外部依赖关系。我不想强迫只想使用 C 的人必须链接到另一个库,只是因为 B 使用它。因此,将 A、B 和 C 放在一个包中并不好。

我想让那些只想要 C 的人更容易抓住 C 并知道他们拥有使用它所需的一切。

鉴于以下情况,处理此问题的最佳方法是什么:

  • 有问题的语言是Objective-c
  • 我首选的交付机制是一个或多个框架(但我会考虑其他选项)
  • 我首选的托管机制是 git / github
  • 我宁愿不需要包管理器

这似乎是一个相对简单的问题,但在你潜入并这么说之前,我可以建议它实际上非常微妙。为了说明,这里有一些可能的,也可能有缺陷的解决方案。

遏制/子模块

B 和 C 使用 A 的事实表明它们可能应该包含 A。这很容易通过 git 子模块实现。但是当然,在他们自己的项目中同时使用 B 和 C 的人最终得到了 A 的两个副本。如果他们的代码也想使用 A,那么它使用哪一个?如果 B 和 C 包含稍微不同的 A 版本怎么办?

相对位置

另一种方法是设置 B 和 C,以便他们期望 A 的副本存在于相对于 B 和 C 的某个已知位置。例如,在与 B 和 C 相同的包含文件夹中。

像这样:

libs/
  libA/
  libB/ -- expects A to live in ../
  libC/ -- expects A to live in ../

这听起来不错,但它没有通过“让人们抓住 C 并拥有一切”的测试。仅抓住 C 本身是不够的,您还必须抓住 A 并将其安排在正确的位置。

这很痛苦——例如,如果你想设置自动化测试,你甚至必须自己做——但更糟糕的是,哪个版本的 A?您只能针对给定版本的 A 测试 C,因此当您将其发布到野外时,您如何确保其他人可以获得该版本。如果 B 和 C 需要不同的版本怎么办?

隐式要求

这是上述“相对位置”的变体——唯一的区别是您没有将 C 的项目设置为期望 A 位于给定的相对位置,您只需将其设置为期望它位于搜索路径中某处。

这是可能的,尤其是在 Xcode 中使用工作区。如果您的 C 项目希望添加到也添加了 A 的工作区,您可以安排一些事情,以便 C 可以找到 A。

但是,这并没有解决“相对位置”解决方案的任何问题。您甚至不能为 C 提供示例工作区,除非该工作区对 A 的相对位置做出假设!

分层子模块

上述解决方案的变体如下:

  • A、B 和 C 都生活在自己的仓库中
  • 您公开“集成”存储库(我们称它们为 BI 和 CI),它们可以很好地安排事情,以便您可以构建和测试(或使用)B 或 C。

所以 CI 可能包含:

- C.xcworksheet
- modules/
    - A (submodule)
    - C (submodule)

这看起来好一点。如果有人只想使用 C,他们可以抓住 CI 并拥有一切。

由于它们是子模块,它们将获得正确的版本。当您发布新版本的 CI 时,您会隐含地说“此版本的 C 可与此版本的 A 一起使用”。好吧,希望,假设你已经测试过了。

使用 CI 的人将获得一个工作区来构建/测试。CI 存储库甚至可以包含示例代码、示例项目等。

但是,想要同时使用 B 和 C 的人仍然存在问题。如果他们只接受 BI 和 CI,他们最终会得到两个 A 副本。这可能会发生冲突。

各种组合的分层子模块

不过,上述问题并非不可克服。

您可以提供如下所示的 BCI 存储库:

- BC.xcworkspace
- modules/
    - A (submodule)
    - B (submodule)
    - C (submodule)

现在你说“如果你想同时使用 B 和 C”,我知道这是一个有效的发行版。

这一切听起来都不错,但维护起来有点困难。我现在可能不得不维护和推送以下存储库的各种组合:A、B、C、BI、CI、BCI。

到目前为止,我们只讨论了三个库。这对我来说是一个真正的问题,但在现实世界中,我可能有大约十个。那一定很痛

所以,我对你的问题是:

  • 你会怎么做?
  • 有没有更好的办法?
  • 我是否必须接受在小模块和大型单体框架之间进行选择是在模块用户更好的灵活性和维护者更多工作之间的权衡?
4

2 回答 2

2

图书馆就像洋葱,有很多层。层违规会造成讨厌的洋葱;内层不能包含外层。

  • 创建 3 个独立的静态库项目(假设您的目标可能是 iOS);甲、乙、丙

  • B 可以包含来自 A 的标头,C 可以包含来自 A 的标头

  • B 和 C 不能包含彼此的标题。A 不能包含来自 B 或 C 的标头

  • 为您想要支持的每个库组合创建一个工作区

  • 将适当的项目添加到工作区

  • 在每个工作区中创建一个新项目以包含该组合的测试应用程序和/或单元测试

关键是工作区。使用工作区,您可以组合任意一组项目,只要它们的配置相同(调试与发布),构建/运行/分析/配置文件将正确确定依赖关系(您可以手动设置它们),将所有内容构建到一个派生数据/产品文件夹中,它就可以工作了。

如果你想独立构建 C 项目,那么 A 将需要按预期安装(通常安装到 /usr/local/,但也可以安装到 ~/ 中),并且与客户系统上的完全一样(如果您要支持二进制库安装)。

这正是我们中有多少人在 Apple 管理我们的项目,而且效果很好。在管理方面,尽量保持简单。你不可能有一个完整的团队致力于构建和配置,因此你的配置应该很简单。

如果您要诚实地评估情况并得出结论,A 永远不会被 B 使用,那么将 B 折叠到 A 中并完成它。编写可重用的 API 很难做好。我已经看到许多项目陷入了试图为应该只是一个特定用途创建一个完全通用的解决方案的困境中,在这个过程中浪费了大量的时间(有时会失败)。

于 2012-08-11T14:25:14.067 回答
1

当你注意到

我宁愿不需要包管理器

我仍然会向你推荐CocoaPods。它完成了所有其他的事情,比如深度依赖管理,对 git 非常友好,并且总体上安装和使用都非常简单。

尽管如此,这并不是您设定的要求的确切答案。

于 2012-08-12T16:47:02.687 回答