4

我不知道这是否是人们常做的事情,但我个人总是跟踪我构建代码的次数。也就是说,我调用make的次数和构建成功的次数。

我目前的解决方案

我有一个简单的代码,它将文件作为参数,打开它,增加里面的数字并覆盖它。make这段代码在被调用时首先被编译。

紧接着,./increase_build build.txt被调用,它增加了我调用make构建库的次数。

然后,编译代码并制作 lib 文件(使用ar cq ...)。之后,./increase_build libbuild.txt调用增加成功构建的数量。最后构建测试。

是我的 Makefile 之一的示例。

为什么我会担心

这一直很好,直到我开始使用版本控制。似乎没有问题:我是我自己的库的唯一作者,我一一添加功能。

不过有一天,我正在测试分支和合并(我自己使用 git,工作时使用 svn),所以我在分支中添加了一个功能并在 master 中更改了一些内容,然后将两者合并。现在构建计数文件具有不同的值。

问题

问题是,假设在分支时,构建计数为 100。然后我在分支中写入一些内容,构建计数达到 110。我在 master 分支中写入一些内容,构建计数达到 120。当我合并二,我看到一个是110,一个是120(顺便说一下冲突)。正确的解决方案是将构建设置为 130。

但是,我不能(读到我不想)回到分支起飞的提交并发现它是 100,所以我计算 100+(110-100)+(120-100)= 130!我希望这是自动的

问题

那么问题很明显:我该怎么做?当我使用版本控制时,如何跟踪我的构建计数(不是提交计数!)?我不想要基于版本控制功能的答案,因为如果我更改版本控制系统,问题就会重新出现。

虽然我可以工作的是在每次构建的构建计数文件中添加一行,其中包含数据和时间。然后构建号将是构建计数文件中的行数。此外,除非我在两个完全同时完成的分支上获得两个构建,否则合并文件将只是两者的结合。

不过我想知道,有没有更好的解决方案?我想要的(构建计数)是否值得付出努力?

PS 如果您想知道为什么我要同时使用构建数量和成功构建数量,那只是个人问题。我想看看我在编码时犯的小错别字和错误得到了多少重建。

编辑:我用 C 和 C++ 编程,所以任何一个解决方案都适合我。

4

4 回答 4

2

因为内部版本号不是您所在分支的功能,所以应该以不同的方式对其进行跟踪。我不使用 git,但对于 SVN,我们有一个系统在工作,它通过将其复制到特定标签来构建特定分支,添加一些特定于该标签的人工制品(您的内部版本号将是此类的主要示例要添加的东西),并且仅在构建成功时才提交。

换句话说,有一个指定的地方(SVN中的标签名称,或者它可以是一个单独的repo)你只做构建,这是你唯一构建的地方,也是存储构建号信息的地方并更新。您的构建脚本看起来像

# I don't know git -- this is all very much pseudocode

# Where did you commit the code you want to build?
source=git://server/path/to/my/branch

# Replace builddir tree with yours
git replace git://server/special/place/build/thisproject with code from $source

cd /tmp
git checkout git://sever/special/place/build/thisproject into new builddir
cd builddir

update local version-controlled file buildnumber+=1

if make
    # Build was successful
    git commit buildnumber
    copy build artefacts to where-ever
endif

cd /tmp
rm -rf /tmp/builddir      

有竞争条件;如果有人在您之后签入构建请求,但不知何故最终首先到达服务器,您最终将构建他们的签入。

通过使用像 Hudson/Jenkins 这样的指定构建主机,这可能会变得更简单。

于 2011-10-14T14:27:00.610 回答
1

您的带有构建日志(每个构建一行)的解决方案似乎非常聪明。您可以添加进行构建的机器的 IP(或 Mac 地址),您是否 timstample,这样您就可以消除重复行的风险。但是,根据您的 VCS,您可能必须手动合并您的构建日志文件。使用 git,您可以对其进行配置,以便合并始终保留两个版本(并最终按日期对行进行排序等)

于 2011-10-10T13:37:42.490 回答
1

我将发布并接受我自己想法的实施作为答案,因为它似乎是最实用的。

所以这是解决方案:

  • 每次构建时,在包含以下数据的构建文件中添加一行:
    • 日期
    • 时间
    • 一个随机数
  • 每次合并时,保留两个构建文件中的行
  • 构建数是构建文件中的总行数。

构建文件的每一行都必须是唯一的。日期和时间使它几乎是独一无二的。两个人同时在他们自己的分支上发布构建的可能性很小。但是,它可能会发生。因此,生成并添加一个随机数以减少该机会。

不过有一个问题。如果您使用 播种srandtime(NULL)那么由于两个构建据说是同时的,生成的数字也可能恰好是相同的。因此,随机数生成器可以使用不同的数字作为种子,例如clock()或 的毫秒部分gettimeofday()。即使不是随机生成的,也可以放置这些数字本身而不是随机数。

在仍然有两条线最终相同的情况下,我将应用ostrich algorithm

更新:

我实现了它,一切正常。最后,我使用clock_gettime(CLOCK_MONOTONIC, ...)并打印了这个函数获得的纳秒作为随机数。我没有使用的原因clock()是由于程序很短,它运行的分辨率低于clock()0,因此我一直得到 0。

更新:

这是我编写的最终代码(其中的某些部分是从其他地方偷来的!)。您可能需要-lrt在某些平台上。

/*
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <time.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef _WIN32
#include <windows.h>

struct timespec
{
    long tv_sec;
    long tv_nsec;
};

/* Note: I copy-pasted this from internet (https://stackoverflow.com/questions/5404277/porting-clock-gettime-to-windows/5404467#5404467)
 * I tweaked it to return nanoseconds instead of microseconds
 * It is much more complete than just finding tv_nsec, but I'm keeping it for possible future use. */
LARGE_INTEGER getFILETIMEoffset(void)
{
    SYSTEMTIME s;
    FILETIME f;
    LARGE_INTEGER t;

    s.wYear = 1970;
    s.wMonth = 1;
    s.wDay = 1;
    s.wHour = 0;
    s.wMinute = 0;
    s.wSecond = 0;
    s.wMilliseconds = 0;
    SystemTimeToFileTime(&s, &f);
    t.QuadPart = f.dwHighDateTime;
    t.QuadPart <<= 32;
    t.QuadPart |= f.dwLowDateTime;
    return t;
}

int clock_gettime(int X, struct timespec *tv)
{
    LARGE_INTEGER t;
    FILETIME f;
    double microseconds;
    static LARGE_INTEGER offset;
    static double frequencyToNanoseconds;
    static int initialized = 0;
    static BOOL usePerformanceCounter = 0;

    if (!initialized)
    {
        LARGE_INTEGER performanceFrequency;
        initialized = 1;
        usePerformanceCounter = QueryPerformanceFrequency(&performanceFrequency);
        if (usePerformanceCounter)
        {
            QueryPerformanceCounter(&offset);
            frequencyToNanoseconds = (double)performanceFrequency.QuadPart/1000000000.0;
        }
        else
        {
            offset = getFILETIMEoffset();
            frequencyToNanoseconds = 0.010;
        }
    }
    if (usePerformanceCounter)
        QueryPerformanceCounter(&t);
    else
    {
        GetSystemTimeAsFileTime(&f);
        t.QuadPart = f.dwHighDateTime;
        t.QuadPart <<= 32;
        t.QuadPart |= f.dwLowDateTime;
    }

    t.QuadPart -= offset.QuadPart;
    microseconds = (double)t.QuadPart/frequencyToNanoseconds;
    t.QuadPart = microseconds;
    tv->tv_sec = t.QuadPart/1000000000;
    tv->tv_nsec = t.QuadPart%1000000000;
    return 0;
}

#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC 0       /* not used anyway */
#endif
#endif

int main(int argc, char **argv)
{
    time_t now_sec;
    struct tm *now;
    FILE *bout;
    struct timespec now_clk;
    if (argc < 2)
    {
        printf("Usage: %s build_file_name\n\n", argv[0]);;
        return EXIT_FAILURE;
    }
    bout = fopen(argv[1], "a");
    if (!bout)
    {
        printf("Could not open file: %s\n\n", argv[1]);
        return EXIT_FAILURE;
    }
    time(&now_sec);
    now = gmtime(&now_sec);
    fprintf(bout, "%02d/%02d/%04d %02d:%02d:%02d", now->tm_mday, now->tm_mon+1, now->tm_year+1900, now->tm_hour, now->tm_min, now->tm_sec);
    clock_gettime(CLOCK_MONOTONIC, &now_clk);
    fprintf(bout, " %ld\n", now_clk.tv_nsec);
    return EXIT_SUCCESS;
}

希望这会对某人有所帮助。

更新

在使用了大约 9 个月后,我可以说这非常有用。一些观察结果是:

  • 在 Windows 上,由实现给出的最后一个元素clock_gettime非常小,有一半的时间具有相同的值。尽管如此,它仍然使它更加随机。
  • 在 Linux 上,最后一个元素确实是非常随机的。
  • 时不时地我不得不做一个“构建”提交,只是为了让构建文件中的行被提交,这样我就可以合并了。但是,这可以通过 来避免git stash
  • 在合并时使用它几乎总是会导致冲突,但解决它非常简单(只需删除差异标记,因为需要两个文件中的行)。
  • wc -l是你的朋友。
于 2011-10-14T13:09:24.393 回答
-1

我不能(读到我不想)回到分支起飞的提交并发现它是 100 所以我计算......我希望这是自动的

肮脏的想法:每个构建都将字符串添加到版本化文件(或根据结果从一对PASS / FAIL中添加一个),每个分支略有不同。合并分支将需要手动合并此信号文件,其中字符串修饰的差异使此任务更容易。wc -l 稍后将计算数字

于 2011-10-10T14:38:39.443 回答