Как вы отслеживаете количество сборок вашей библиотеки, когда несколько авторов используют контроль версий? - PullRequest
4 голосов
/ 10 октября 2011

Я не знаю, является ли это чем-то обычным делом для людей или нет, но я лично всегда отслеживаю, сколько раз я создавал свой код. То есть и количество звонков, которое я набрал make, и количество успешных сборок.

Мое текущее решение

У меня есть простой код, который принимает файл в качестве параметра, открывает его, увеличивает число внутри и перезаписывает его. Этот код компилируется, во-первых, когда вызывается make.

Сразу после этого вызывается ./increase_build build.txt, что увеличивает количество раз, которое я набрал make для построения библиотеки.

Затем код компилируется и создается файл lib (с ar cq ...). После этого вызывается ./increase_build libbuild.txt, который увеличивает количество успешных сборок. Наконец тесты построены.

Этот является примером одного из моих Makefile.

Почему я так обеспокоен

Это всегда работало нормально, пока я не начал использовать контроль версий. Казалось, что нет никаких проблем: я единственный автор своих собственных библиотек, и я добавляю функции одну за другой.

Однажды я тестировал ветвление и слияние (я использую git для себя и svn на работе), поэтому я добавил одну функцию в ветку и что-то изменил в master, и я слил две. Теперь файлы подсчета сборок имеют разные значения.

проблема

Проблема, скажем, во время ветвления, счетчик сборок равен 100. Затем я пишу что-то в ветке, и счетчик сборок достигает 110. Я пишу что-то в основной ветке, и счетчик сборок достигает 120. Когда Я объединяю два, я вижу, что один - 110, а другой - 120 (кстати, это конфликт). Правильным решением было бы установить сборку 130.

Однако я не могу (читай, я не хочу) вернуться к коммиту, где ветвь взлетела, и найти, что это было 100, поэтому я вычисляю 100+ (110-100) + (120-100) = 130! Я хочу, чтобы это было автоматически

Вопрос

Ну, вопрос очевиден: как мне это сделать? Как мне отслеживать количество сборок (не коммитов!), Когда я работаю с контролем версий? Мне не нужен ответ, основанный на функции контроля версий, потому что проблема возникает заново, если я меняю систему контроля версий.

Я мог бы добавить одну строку в файле подсчета сборок для каждой сборки, что-то сказать с данными и временем. Тогда номер сборки будет количеством строк в файлах счетчиков сборок. Кроме того, если я не получу две сборки на двух ветвях, которые были сделаны ТОЧНО в то же время, то объединение файлов будет просто объединением двух.

Интересно, есть ли лучшие решения для этого? Стоит ли то, что я хочу (счетчик сборок)?

P.S. Если вам интересно, почему я делаю это как с количеством сборок, так и с количеством успешных сборок, это просто что-то личное. Мне нравится видеть, сколько я получаю для восстановления за мелкие опечатки и ошибки, которые я делаю, когда кодирую.

Редактировать: Я программирую на C и C ++, поэтому решение в любом из них работает для меня.

Ответы [ 4 ]

2 голосов
/ 14 октября 2011

Поскольку номер сборки не является частью ветви, в которой вы находитесь, он должен отслеживаться по-другому.Я не использую git, но для SVN у нас есть система, которая создает определенную ветку, копируя ее в определенный тег, добавляя некоторые артефакты, специфичные для этого тега (ваш номер сборки был бы ярким примеромдобавление) и фиксация только в случае успешной сборки.

Другими словами, есть назначенное место (имя тега в SVN или это может быть отдельный репозиторий), где вы делаете только сборки,это единственное место, где вы строите, и именно там информация о номере сборки сохраняется и обновляется.Ваш скрипт сборки будет выглядеть примерно так:

# 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.

1 голос
/ 14 октября 2011

Я собираюсь опубликовать и принять реализацию моей собственной идеи в качестве ответа, поскольку она представляется наиболее практичной.

Итак, вот решение:

  • При каждой сборке добавьте одну строку в файл сборки, содержащий следующие данные:
    • Дата
    • Время
    • Случайное число
  • При каждом слиянии сохраняйте строки из обоих файлов сборки
  • Количество сборок - это общее количество строк в файле сборки.

Каждая строка файла сборки должна бытьуникальный.Дата и время делают это почти уникальным.Маловероятно, что два человека выпускают сборку в своей собственной ветке одновременно.Однако это может случиться.Таким образом, случайное число генерируется и добавляется, чтобы уменьшить этот шанс.

Хотя есть одна проблема.Если вы посеете srand с помощью time(NULL), то, поскольку обе сборки предположительно совпадают, сгенерированное число может также оказаться одинаковым.Следовательно, генератор случайных чисел может быть заполнен другим числом, таким как clock() или миллисекундной частью gettimeofday().Даже если они не были сгенерированы случайным образом, вместо случайного числа можно было бы поставить сами эти числа.

В случае, если две строки оказались одинаковыми, я буду применять алгоритм страуса .

Обновление:

Я реализовал это и все отлично работает.Наконец, я использовал clock_gettime(CLOCK_MONOTONIC, ...) и напечатал наносекунды, полученные этой функцией, как случайное число.Причина, по которой я не использовал clock(), заключалась в том, что, поскольку программа была довольно короткой, она работала меньше разрешения clock(), и поэтому я продолжал получать 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 (/4270659/portirovanie-clockgettime-na-windows#4270673)
 * 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 последний элемент действительно довольно случайный.
  • Время от времени мне приходилось делать коммит "Build" только для того, чтобы получитьстроки в файлах сборки зафиксированы, чтобы я мог слить.Однако этого можно избежать с помощью git stash.
  • Использование этого почти всегда приводит к конфликту при объединении, но очень просто разрешить его (просто удалите маркеры diff, так как нужны строки из обоих файлов).
  • wc -l ваш друг.
1 голос
/ 10 октября 2011

Ваше решение с журналом сборки (одна строка на сборку) кажется довольно умным.Вы можете добавить IP-адрес (или Mac-адрес) машины, выполняющей сборку, если вы отметите ее, чтобы избежать риска дублирования строки.Однако в зависимости от вашей VCS вам, вероятно, придется вручную объединить файл журнала сборки.С помощью git вы можете настроить его так, чтобы слияние всегда сохраняло обе версии (и, в конечном итоге, сортировало строки по дате и т. Д.)

0 голосов
/ 10 октября 2011

Я не могу (читай, я не хочу) вернуться к коммиту, где ветвь взлетела, и найти, что это было 100, поэтому я вычисляю ... Я хочу, чтобы это было автоматически

Грязная идея: каждая сборка добавляет строку к версионному файлу (или к одной из пары PASS / FAIL в зависимости от результата), немного отличающуюся для каждой ветви. Ветви слияния потребуют ручного слияния этих файлов сигналов, где различия в оформлении строк облегчают эту задачу. wc -l позже будет считать числа

...