【问题标题】:Makefile - How to organize and build multiple library versionsMakefile - 如何组织和构建多个库版本
【发布时间】:2018-05-08 06:17:35
【问题描述】:

我正在尝试创建一个构建系统,但我应该使用的结构不清楚。 我有各种库文件及其相关的包含。 这些库文件(或模块)可能有不同的版本。因此,module1 可能只有 1 个版本,但 ModuleN 可能有 50 个版本。 现在我的问题是我不确定如何组织我的树以便我可以为给定版本构建我的 Library.a 包。

我的第一个想法是按以下方式组织我的文件:

           Libraries                      Includes
               ¦                              ¦
    ----------------------          ----------------------
    ¦          ¦          ¦         ¦          ¦          ¦
   V1.0      V1.1       V1.2       V1.0      V1.1       V1.2
    ¦          ¦          ¦         ¦          ¦          ¦         
 Lib1.c     Lib3.c     Lib2.c     Lib1.h     Lib3.h     Lib2.h   
 Lib2.c                Lib3.c     Lib2.h                Lib3.h
 Lib3.c                           Lib3.h
 Lib4.c                           Lib4.h 

现在,我将如何构建我的包 V1.2,考虑到它还需要在整个树中合并最新库的联合(即 V1.2 包 = V1.0 U V1.1 U V1.2 )。在这种情况下,手动定义规则并不会太难,但如果有 100 多个文件,它很快就会变得难以管理。

在常见的做法中,可能会将未更改的文件从一个版本复制到下一个版本,但随后就很难知道是什么。

有解决这个问题的典型方法吗?

【问题讨论】:

  • 你可以使用 Git。每个版本使用一个分支。
  • 为什么在线? git 是本地的。
  • @progLearner 这绝对没有做错什么。您将版本控制与备份混淆了。那是两种不同的东西。如果您考虑将在线存储库推送到备份,那么您做错了什么。 (不过我同意,如果你不备份你的东西,那你就做错了。)
  • 使用“我不能保证在线”作为不使用版本控制的理由是非常错误的。 Git 的目的是版本控制,而不是备份。您基本上是在说“由于我无法备份,所以我也不妨跳过版本控制”
  • 如果您已妥善设置所有内容,您可以使用 git checkout v1.1; make

标签: c makefile build msbuild


【解决方案1】:

处理这个问题的正确方法是使用像 Git 这样的版本控制系统。它旨在处理完全您所描述的内容。

cmets 中有几个关于使用 Git 完成您想要的任务的建议。但是,您似乎对库中文件的存储方式存在误解。

你在cmets中说:

我想从最新的存储库中构建任何版本的库(如果你愿意,可以从 Git 中新鲜提取),而无需从 Git 进行潜在的 100 次拉取

还有:

但是如果您了解分支,您就会知道通过切换会丢失之前的快照。因此,如果您从构建一个分支(获取 V1.0)开始,然后切换到第二个分支构建 V1.1,您将失去 V1.0。

查看这些 cmets 和您的示例文件结构,这表明您认为 V1.0 分支将包含 Lib1.cLib2.cLib3.cLib4.c,而 V1.1 分支将仅包含Lib3.c 并且 V1.2 分支将仅包含 Lib2.cLib3.c。这不是Git的工作方式。

以下是如何在 Git 中存储这些文件的示例:

首先将 V1.0 文件放入目录结构中:

[dbush@db-centos7 mylib]$ ls -lR
.:
total 0
drwxrwxr-x. 2 dbush dbush 62 Jan 20 10:19 inc
drwxrwxr-x. 2 dbush dbush 62 Jan 20 10:17 src

./inc:
total 16
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:18 Lib1.h
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:19 Lib2.h
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:18 Lib3.h
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:19 Lib4.h

./src:
total 16
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:16 Lib1.c
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:17 Lib2.c
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:17 Lib3.c
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:17 Lib4.c

然后创建一个新的 git repo,添加文件并提交它们:

[dbush@db-centos7 mylib]$ git init
Initialized empty Git repository in /home/dbush/mylib/.git/
[dbush@db-centos7 mylib]$ git add *
[dbush@db-centos7 mylib]$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#   new file:   inc/Lib1.h
#   new file:   inc/Lib2.h
#   new file:   inc/Lib3.h
#   new file:   inc/Lib4.h
#   new file:   src/Lib1.c
#   new file:   src/Lib2.c
#   new file:   src/Lib3.c
#   new file:   src/Lib4.c
#
[dbush@db-centos7 mylib]$ git commit

现在你已经提交了 1.0 版本的文件,接下来我们根据当前版本创建一个标签:

[dbush@db-centos7 mylib]$ git tag -a -m "version 1.0" V1.0
[dbush@db-centos7 mylib]$ git tag  -l
V1.0

现在,Lib3.c 看起来像这样:

#include <stdio.h>

void lib3()
{
    printf("lib3 1.0\n");
}

现在让我们对 1.1 版本进行更改:

#include <stdio.h>

void lib3()
{
    printf("lib3 1.1\n");
}

我们可以看到工作副本和签入版本之间存在差异:

[dbush@db-centos7 mylib]$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   src/Lib3.c
#
no changes added to commit (use "git add" and/or "git commit -a")
[dbush@db-centos7 mylib]$ git diff
diff --git a/src/Lib3.c b/src/Lib3.c
index 3593018..12542d9 100644
--- a/src/Lib3.c
+++ b/src/Lib3.c
@@ -2,5 +2,5 @@

 void lib3()
 {
-    printf("lib3 1.0\n");
+    printf("lib3 1.1\n");
 }

然后我们提交更改:

[dbush@db-centos7 mylib]$ git add src/Lib3.c
[dbush@db-centos7 mylib]$ git commit -m "updated Lib3.c for version 1.1"

并将其标记为 1.1 版:

[dbush@db-centos7 mylib]$ git tag  -a -m "version 1.1" V1.1

现在让我们假设我们想要构建 1.0 代码。我们签出标签:

[dbush@db-centos7 mylib]$ git checkout V1.0
Note: checking out 'V1.0'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at a1ee8c1... initial commit
[dbush@db-centos7 mylib]$ ls -lR
.:
total 0
drwxrwxr-x. 2 dbush dbush 62 Jan 20 10:19 inc
drwxrwxr-x. 2 dbush dbush 62 Jan 20 10:33 src

./inc:
total 16
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:18 Lib1.h
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:19 Lib2.h
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:18 Lib3.h
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:19 Lib4.h

./src:
total 16
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:16 Lib1.c
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:17 Lib2.c
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:33 Lib3.c
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:17 Lib4.c
[dbush@db-centos7 mylib]$ cat src/Lib3.c
#include <stdio.h>

void lib3()
{
    printf("lib3 1.0\n");
}
[dbush@db-centos7 mylib]$ cat src/Lib2.c
#include <stdio.h>

void lib2()
{
    printf("lib2 1.0\n");
}

您可以看到完整的源代码树可用,并且 Lib3.c 是 1.0 版本,Lib2.c 也是如此。现在我们要构建 1.1,所以让我们获取 1.1 标签:

[dbush@db-centos7 mylib]$ git checkout V1.1
Previous HEAD position was a1ee8c1... initial commit
HEAD is now at 95a429c... updated Lib3.c for version 1.1
[dbush@db-centos7 mylib]$ ls -lR
.:
total 0
drwxrwxr-x. 2 dbush dbush 62 Jan 20 10:19 inc
drwxrwxr-x. 2 dbush dbush 62 Jan 20 10:35 src

./inc:
total 16
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:18 Lib1.h
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:19 Lib2.h
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:18 Lib3.h
-rw-rw-r--. 1 dbush dbush 13 Jan 20 10:19 Lib4.h

./src:
total 16
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:16 Lib1.c
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:17 Lib2.c
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:35 Lib3.c
-rw-rw-r--. 1 dbush dbush 62 Jan 20 10:17 Lib4.c
[dbush@db-centos7 mylib]$ cat src/Lib3.c
#include <stdio.h>

void lib3()
{
    printf("lib3 1.1\n");
}
[dbush@db-centos7 mylib]$ cat src/Lib2.c
#include <stdio.h>

void lib2()
{
    printf("lib2 1.0\n");
}

您可以从这里看到,不仅存在 Lib3.c 的 1.1 副本,还存在所有其他文件及其 V1.0 内容。您无需进行多次签出,也不会丢失任何未在最新提交中更新的文件。

您可以按照上面的示例来设置您的 repo。所以你会:

  • 将 1.0 文件放在所需的目录结构中
  • 创建一个新的 git 存储库并提交文件
  • 标签版本 1.0
  • 复制 1.1 版的文件更改
  • 提交更改的文件,然后标记版本 1.1
  • 复制 1.2 版的文件更改
  • 提交更改的文件,然后标记版本 1.2
  • 等等……

如果您在转至新版本时需要删除文件,这也适用。例如,如果您在 1.3 版中删除了 Lib4.c,那么您将在签出标签 1.0、1.1 和 1.2 时看到该文件,但在签出标签 1.3 时不会看到。

因此,一旦您设置了 Git 存储库,您只需签出一个标签一次,您就拥有了所需的一切。

编辑:

当需要将这些文件用于多个(可能是并发的)构建时,Git 仍然非常适合。如果要保留建议的文件夹结构,则需要了解文件的完整历史记录以获取每个文件的正确版本,并且您仍然希望将每个相关源文件的副本复制到“活动”构建目录中这样一个构建创建的目标文件就不会被另一个构建覆盖。 Git 将为您完全管理所有这些。

Git 允许克隆本地存储库,并执行特定分支或标签的最小克隆,这样您只复制您需要的内容,而不是在进程中获取整个 git 历史记录,因此拥有多个副本是不是问题。你可以这样做:

[dbush@db-centos7 mylib]$ git status
# On branch master
nothing to commit, working directory clean
[dbush@db-centos7 mylib]$ cd ..
[dbush@db-centos7 ~]$ git clone --single-branch --branch V1.0 ./mylib mylib-v1.0
Cloning into 'mylib-v1.0'...
done.
Note: checking out 'a1ee8c1bfdbb20f3e0716212582338371b60d9bc'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

[dbush@db-centos7 ~]$ cd mylib-v1.0
[dbush@db-centos7 mylib-v1.0]$ git status
# Not currently on any branch.
nothing to commit, working directory clean
[dbush@db-centos7 mylib-v1.0]$ git branch -l
* (no branch)
[dbush@db-centos7 mylib-v1.0]$ 

--branch 选项表示克隆特定的分支或标签,而 --single-branch 选项创建一个仅包含给定标签的浅层克隆,因此克隆的 .git 目录不会很大并且可以创建很快。

【讨论】:

  • 我真的很讨厌破坏精心设计的答案,但恕我直言,原始发帖人非常了解git 的工作机制。她/他没有很清楚地理解的是为这个问题设想的用例:据我当时的理解,她/他希望同时访问所有文件的所有版本或多或少相同的位置,没有间歇性 git 操作。显然,他/她必须以特别的方式为未知数量的项目基线提供服务——如果这来自优化或带宽或任何保持开放的观点。
  • +1 确实@Vroomfondel,您的陈述和假设是正确的。另外,我赞成 dbush 的回答,因为他在“不”回答我的问题方面付出了很大的努力(仍然很有价值):-)
  • @progLearner 查看我的编辑。您仍然可以通过最少的复制来做到这一点。
【解决方案2】:

我知道 OP 说他不想使用 Git,所以这个答案更适合阅读这个问题的其他人。

从存储库构建任意版本的一种非常常见的做法是使用标签。标记是一种突出特定提交的方法。 Git 支持两种类型的标签:轻量级和带注释的。

轻量级标签就像一个分支。它只是一个提交的指针。与分支的区别在于,该指针不会随着新的提交而改变。不建议将这些标签用于您的目的。它们在开发过程中更适合开发人员。要创建轻量级标签,请使用此命令

git tag <name>

另一种标签类型是带注释的标签。它们包含更多信息并经过校验和。这些是您要用于发布的那些。与提交一样,您为带注释的标签指定标签消息。要创建带注释的标签,请使用以下命令:

git tag -a <name>

这将打开一个编辑器,您可以在其中编写标签消息。您也可以使用-m "&lt;message&gt;"在命令行上指定消息

要查看所有标签,只需使用:

git tag

您可以像签出分支一样签出标签:

git checkout <tag>

在此处阅读有关标记的更多信息:https://git-scm.com/book/en/v2/Git-Basics-Tagging

因此,您所做的是为与发布相对应的每个提交创建一个(最好是带注释的)标签。然后,假设您可以通过调用 make 来构建项目,您可以使用以下两个命令构建任何版本:

git checkout <tag>
make

注意:在上面的 cmets 中,我说的是分支。那是我暂时的困惑。可以为此目的使用分支,但标记是正确的方法。

【讨论】:

    猜你喜欢
    • 2010-09-09
    • 1970-01-01
    • 1970-01-01
    • 2012-12-04
    • 2010-10-25
    • 1970-01-01
    • 1970-01-01
    • 2017-12-13
    • 2016-06-20
    相关资源
    最近更新 更多