Всем хорош Visual Studio (хоть и не все так думают), но порядка в нем нет. Нет порядка с номерами версий и билдов, вот и изгаляются несчастные программисты создавая папки типа 08_02_2004_12_47_56 или смотрят напрягая натруженные глаза на дату создания файла. А ведь иногда хочется, чтобы и в заголовке окна любимой программы было написано V2.0 Build 2600 SP2 или что-то типа того.
В этой статье я расскажу как это сделать. Мой способ не претендует на рулезность, но работает и я им давно пользуюсь.
Мы используем возможность сборщика Visual Studio выполнять так называемый Pre-Build step. Обычно такие вещи используются для регистрации COM компонентов, но мы вдохнем в него новую жизнь.
Идея такая: будем в тех местах, где нам нужен номер версии или билда, использовать макросы, определенные в специальном заголовочном файле, а этот файл у нас будет генерировать/обновлять специальная прога, которую мы сейчас напишем.
Итак, создаем новый проект, Win32 console project под названием version, ставим поддержку для ATL и MFC и редактируем файл чтобы получилось что-то типа (если не в лом, прочитайте комментарии – сразу все ясно станет):
#include "stdafx.h"
#include "version.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// The one and only application object
CWinApp theApp;
using namespace std;
//Функция печатает синтаксис командной строки
void Usage()
{
//Первый параметр - имя файла заголовка
//Потом - название приложения
//Номер версии (по умолчанию - 1.0.0.1)
std::cout<<"Usage: version header.h AppName [x.x.x.x]"<
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
//Если программе передано неверное количество параметров, показать синтаксис и выйти
if ((argc<3)||(argc>4))
{
Usage();
return -1;
}
//Определить наличие файла заголовка
HANDLE hFile=CreateFile(argv[1],GENERIC_READ, 0,NULL, OPEN_EXISTING,0, NULL);
if (hFile==INVALID_HANDLE_VALUE)
{
//Файла нет, создать новый
CFile file(argv[1], CFile::modeWrite|CFile::modeCreate);
CArchive arOut(&file,CArchive::store);
//Добавить макрос APPNAME
CString sFormat;
sFormat.Format("#define APPNAME \"%s\"\r\n",argv[2]);
arOut.WriteString(sFormat);
//Добавить макрос VERSION
//Если не указан номер версии
if (argc==3)
//Использовать версию по умолчанию
sFormat.Format("#define VERSION \"%s\"\r\n","1.0.0.1");
else
//Использовать указанную версию
sFormat.Format("#define VERSION \"%s\"\r\n",argv[3]);
arOut.WriteString(sFormat);
//добавить макрос BUILD
sFormat.Format("#define BUILD \"%d\"\r\n",1);
arOut.WriteString(sFormat);
//Добавить макрос TIME
CTime time=CTime::GetCurrentTime();
sFormat.Format("#define TIME \"%02d.%02d.%4d %02d:%02d:%02d\"",
time.GetDay(),time.GetMonth(), time.GetYear(),time.GetHour(), time.GetMinute(),time.GetSecond());
arOut.WriteString(sFormat);
//Закрыть файл
arOut.Close();
file.Close();
}
else
{
//Файл существует
//Прочитать и разобрать его
CloseHandle(hFile);
CFile fileIn(argv[1],CFile::modeRead);
CArchive arIn(&fileIn,CArchive::load);
CString sLine;
//Разобрать макрос APPNAME
arIn.ReadString(sLine);
int i=0;
sLine.Tokenize("\"",i);
CString sAppName=sLine.Tokenize("\"",i);
//Разобрать макрос VERSION
arIn.ReadString(sLine);
i=0;
sLine.Tokenize("\"",i);
int nVersion1=atoi(sLine.Tokenize(".",i));
int nVersion2=atoi(sLine.Tokenize(".",i));
int nVersion3=atoi(sLine.Tokenize(".",i));
int nVersion4=atoi(sLine.Tokenize("\"",i));
//Разобрать макрос BUILD
arIn.ReadString(sLine);
i=0;
sLine.Tokenize("\"",i);
int nBuild=atoi(sLine.Tokenize("\"",i));
//Теперь обработка - увеличим номер билда
//И младший номер версии
nVersion4++;
nBuild++;
//Переоткроем файл на запись
arIn.Close();
fileIn.Close();
//И запишем то, что получилось в результате
CFile file(argv[1], CFile::modeWrite|CFile::modeCreate);
CArchive arOut(&file,CArchive::store);
CString sFormat;
//Макрос APPNAME
sFormat.Format("#define APPNAME \"%s\"\r\n",argv[2]);
arOut.WriteString(sFormat);
//Макрос VERSION
sFormat.Format("#define VERSION \"%d.%d.%d.%d\"\r\n",
nVersion1, nVersion2, nVersion3, nVersion4);
arOut.WriteString(sFormat);
//Макрос BUILD
sFormat.Format("#define BUILD \"%d\"\r\n",nBuild);
arOut.WriteString(sFormat);
//Макрос TIME
CTime time=CTime::GetCurrentTime();
sFormat.Format("#define TIME \"%02d.%02d.%4d %02d:%02d:%02d\"",time.GetDay(),
time.GetMonth(),time.GetYear(), time.GetHour(),time.GetMinute(),
time.GetSecond());
arOut.WriteString(sFormat);
//Закроем файл
arOut.Close();
file.Close();
}
}
Компилируем, полученный файл version.exe (9728 байт у меня) кладем в папку с бинарниками Visual Studio (у меня C:\Program Files\Microsoft Visual Studio.NET
2003\Vc7\bin). Теперь посмотрим как использовать эту вещь. Создаем новый проект, к примеру под названием test, Console application, поддержку ATL и MFC не надо. VS генерит файл типа
#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
return 0;
}
Подписываем под stdafx.h что-то типа #include “versioninfo.h” и в свойствах проекта Build Events/Pre-Build Event/Command Line пишем version versioninfo.h Megacooltest. Теперь в теле функции _tmain пишем что-то типа
std::cout<
#include "stdafx.h"
#include "versioninfo.h"
int _tmain(int argc, _TCHAR* argv[])
{
std::cout<
Компилируем, запускаем:
D:\andrew\Projects\test1\Debug>test1
Megacooltest Version 1.0.0.1 Build 1 compiled 07.09.2004 14:08:53
А теперь компилируем еще раз и опять запускаем, только выберем Rebuild solution, иначе VS подумает, что раз ничего не изменилось, то и делать ничего не надо:
D:\andrew\Projects\test1\Debug>test1
Megacooltest Version 1.0.0.2 Build 2 compiled 07.09.2004 14:10:29
Вуаля! Теперь заказчик будет видеть сколько раз вы компилировали несчастную прогу пока добились результата. А еще он увидит во сколько времени несчастный программист
этого добился. Если надо сбросить номер билда, то просто сотрите файл versioninfo.h и он будет создан заново с номером билда 1. Или измените в файле versioninfo.h номер билда, и нумерация пойдет дальше с этого номера.
Описанное выше не есть руководство к действию, а скорее метод. Экспериментируйте, улучшайте, затачивайте под себя. К примеру, можно изменять номер версии как-то по другому, или добавить имя юзера, скомпилировавшего файл. В общем, много еще чего можно делать под нашу музыку (©Масяня). Удачи.