Давно сие спрашивают, и наконец-то руки дошли. Это все для VC# 2005 Express, но если немного поменять пару строк, то можно и в обычных Win32-проектах использовать.
Работа скрипта основана на простом поиске вхождения указанной подстроки с версией, выдирании фрагмента в кавычках, разборе его и инкрементировании номера ревизии.
Я тут извращения ради изменяю сразу и AssemblyVersion, и AssemblyFileVersion
Если вы пишете под Win, то в файле ресурсов имеется секция VERSIONINFO, порывшись в которой, можно подправить сей скрипт.
Как его использовать?
1. Засуньте в каталог Properties
2. Запишите в свойствах проекта pre-build event как "$(ProjectDir)Properties\buildinc.js", причем, обязательно в кавычках
3. Готово, правда при каждом нажатии на Ctrl+F5, даже если проект уже был до этого собран, pre-build event снова сработает, соответственно, скрипт тоже запустится и build снова произойдет.
Удачи, и жду замечаний.
/*
buildinc.js
Скрипт для автоматического увеличения номера ревизии/сборки
для проектов Visual C# (VS 2005 Express)
файл должен содержать строку вида
[assembly: AssemblyVersion("0.1.0.0")]
эта строка ищется и изменяется с инкрементированием номера ревизии
*/
// чтение содержимого файла
function fnLoadContent(filename)
{
fileObj = new ActiveXObject("Scripting.FileSystemObject");
var ForReading = 1, ForWriting = 2, ForAppending = 8;
var TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0;
f = fileObj.GetFile(filename);
ts = f.OpenAsTextStream(ForReading, TristateUseDefault);
var s = ts.ReadAll(); // прочитать и вывести весь файл
ts.Close(); // закрыть текстовый потокreturn s;
}
// sContent - содержимое файла. В случае обнаружения искомой
// строки оно изменяется на новое значение
function fnIncrementVerInfo(sContent, sAssemblyOrFile)
{
var searchString = sAssemblyOrFile;
var begin = sContent.indexOf(searchString);
if (begin < 0)
return sContent;
begin += searchString.length;
// найти конец строки с информацией о версии
var end = sContent.indexOf("\")]", begin);
if (end < 0)
return sContent;
var verMajor = 0;
var verMinor = 0;
var build = 0;
var revision = 0;
// эту часть текста мы вырежем и заменим новой информацией о версии
var cutBegin = begin;
var cutEnd = end;
// копируем строку с версией
var info = sContent.substring(begin, end);
// разбираем
// старший номер версии
begin = 0;
end = info.indexOf(".");
if (end < 0)
return sContent;
verMajor = info.substring(begin, end);
info = info.substr(end+1);
// младший номер версии
begin = 0;
end = info.indexOf(".");
if (end < 0)
return sContent;
verMinor = info.substring(begin, end);
info = info.substr(end+1);
// сборка
begin = 0;
end = info.indexOf(".");
if (end < 0)
return sContent;
build = info.substring(begin, end);
info = info.substr(end+1);
// ревизия
begin = 0;
end = info.length;
if (end < 0)
return sContent;
revision = info;
// увеличиваем номер
revision++;
var info = verMajor + "." + verMinor + "." + build + "." + revision;
// собираем новый файл
var s = sContent.substring(0, cutBegin) + info + sContent.substring(cutEnd, sContent.length);
return s;
}
// основная программа
function main()
{
var sFileToParse = "..\\..\\Properties\\AssemblyInfo.cs";
// открыть файл
var content = fnLoadContent(sFileToParse);
// увеличить номер ревизии
content = fnIncrementVerInfo(content, "[assembly: AssemblyVersion(\"");
content = fnIncrementVerInfo(content, "[assembly: AssemblyFileVersion(\"");
// Создать объект FileSystemObject.
var myFileSysObj = new ActiveXObject("Scripting.FileSystemObject")
// Создать объект TextStream.
var myTextStream = myFileSysObj.OpenTextFile(sFileToParse, 2, true)
// Записать в файл результат
myTextStream.Write(content);
}
// пуск
main();
Небольшое замечание. Бывает так, что вместо того, чтобы скрипту запуститься, выскакивает Блокнот. Просто кто-то с кривыми руками попортил вашу систему. В свойствах папки выберите "Типы файлов", найдите расширение JS, нажмите кнопку "Дополнительно", для действия "Открыть" запишите
// основная программа
function main()
{
var args = WScript.Arguments;if( args(0) == "Release")
{
var sFileToParse = "..\\..\\Properties\\AssemblyInfo.cs";
// открыть файл
var content = fnLoadContent(sFileToParse);
// увеличить номер ревизии
content = fnIncrementVerInfo(content, "[assembly: AssemblyVersion(\"");
content = fnIncrementVerInfo(content, "[assembly: AssemblyFileVersion(\"");
// Создать объект FileSystemObject.
var myFileSysObj = new ActiveXObject("Scripting.FileSystemObject")
// Создать объект TextStream.
var myTextStream = myFileSysObj.OpenTextFile(sFileToParse, 2, true)
// Записать в файл результат
myTextStream.Write(content);
}
}
Здравствуйте, denaturat, Вы писали:
D>Удачи, и жду замечаний.
Вот натолкнулся, решил поделится своей разработкой
То что ниже не увеличивает номер версии, а устанавливает его равным ревизии SVN. Это удобно, так как позволяет по бинарнику точно понять с какого кода он скомпилирован.
CMD скрипт
@if "%SVN_PATH%" == "" (
@goto NO_SVN
)
"%SVN_PATH%" update "Source"
Executables\Utilities\Nabu.SvnToVersion.exe -pd "Source" -td "Source" -tf "AssemblyInfo.cs" -re ?.net
"%SVN_PATH%" commit --editor-cmd notepad.exe "Source"
@goto EXIT
:NO_SVN
@echo Environment variable %%SVN_PATH%% with value of path to svn.exe must be defined.
@goto EXIT
:EXIT
D>2. Запишите в свойствах проекта pre-build event как "$(ProjectDir)Properties\buildinc.js", причем, обязательно в кавычках
cscript.exe //NoLogo "$(ProjectDir)Properties\buildinc.js"
И никто больше шаловливыми ручками систему не испортит
D>Удачи, и жду замечаний.
Из есть у меня
Искать подстроки по indexOf — это путь (как бы так сказать) "нелёгкий". В JS (как и во многих других языках) рулят регэкспы. Вот для примера кусок моего скрипта который работает с ресурсным файлом (инкрементит младший номер версии, старшие выставляет в месяц-день):
function IncrementRCVersion(strRCFileName, iVersion)
{
var objStream = objFileSystem.OpenTextFile(strRCFileName, 1);
var objOutStream = objFileSystem.CreateTextFile(strRCFileName + ".new", true);
var arrToSearch = [
"^(\\s*FILEVERSION\\s+)(\\d+)(,\\s*)(\\d+)(,\\s*)(\\d+)(,\\s*)(\\d+)(.*)$",
"^(\\s*PRODUCTVERSION\\s+)(\\d+)(,\\s*)(\\d+)(,\\s*)(\\d+)(,\\s*)(\\d+)(.*)$",
"^(\\s*VALUE\\s*\"FolderVersion\"\\s*,\\s*\")(\\d+)(,\\s*)(\\d+)(,\\s*)(\\d+)(,\\s*)(\\d+)(\".*)",
"^(\\s*VALUE\\s*\"FileVersion\"\\s*,\\s*\")(\\d+)(,\\s*)(\\d+)(,\\s*)(\\d+)(,\\s*)(\\d+)(\".*)",
"^(\\s*VALUE\\s*\"ProductVersion\"\\s*,\\s*\")(\\d+)(,\\s*)(\\d+)(,\\s*)(\\d+)(,\\s*)(\\d+)(\".*)"
];
while (!objStream.AtEndOfStream)
{
var strLine = objStream.ReadLine();
for (var i=0; i<arrToSearch.length; ++i)
{
var objRegExp = new RegExp(arrToSearch[i], "");
if (objRegExp.exec(strLine) != null)
{
var strMonth = "" + ((new Date()).getMonth() + 1);
var strDay = "" + ((new Date()).getDate());
if (typeof(iVersion) == "undefined")
{
iVersion = parseInt(RegExp.$8, 10) + 1;
}
var strBuild = "" + iVersion;
strLine = RegExp.$1 +
"1" + RegExp.$3 + // Version
strMonth + RegExp.$5 + // Month
strDay + RegExp.$7 + // Day
strBuild + RegExp.$9; // Version number (auto-increment)
}
}
objOutStream.WriteLine(strLine);
}
objStream.Close();
objOutStream.Close();
// Replace RC file
CopyFile(strRCFileName + ".new", strRCFileName);
// Remove temporary file
objFileSystem.DeleteFile(strRCFileName + ".new");
return iVersion;
}
_FR>ИМХО, кузявее эту логику вынести из скрипта в Pre-Build Event:
Честно говоря не люблю Pre- и Post- ивенты. Натыкался на глюки непонятности при их работе. Посему обычно добавляю в проект отдельный текстовый файл типа IncrementVersion.txt у которого custom build step выставляю в вызов скрипта. А для rc-файла ставлю этот IncrementVersion.txt в additional dependencies. Тогда и с тем чтобы отключить это в Debug или Release проблем не возникает.
А вообще самый правильный путь здесь (ИМХО!) это написать скрипт который билдит релиз, благо кроме собственно билда Exe и Dll наверняка приходится делать кучу дополнительных задач (у меня к примеру есть в проекте куча XML-файлов, билд скрипт проверяет их на валидность, и т.д. и т.п.). Вот именно в этом скрипте самое место инкременту версии.
D>>2. Запишите в свойствах проекта pre-build event как "$(ProjectDir)Properties\buildinc.js", причем, обязательно в кавычках L>cscript.exe //NoLogo "$(ProjectDir)Properties\buildinc.js" L>И никто больше шаловливыми ручками систему не испортит
Да, кстати — огромное преимущество использования JScript для таких задач — это то что эти скрипты очень легко отладить. Для этого добавляем ключик /D
cscript.exe //D //NoLogo "$(ProjectDir)Properties\buildinc.js"
И в теле скрипта пишем там где нам нужно остановиться:
debugger;
Всё, при достижении этой строчки нас спросят каким дебагером аттачится (если на машине стоит более одной версии Visual Studio). Аттачимся любой из них (можно даже той же из которой запустили билд) и отлаживаемся на здоровье. Можно даже после окончания отладки ключик //D не выкидывать — на нормальную работу он не окажет никакого влияния, а вот при возникновении исключения тут же предложит всё это отладить.
Здравствуйте, denaturat, Вы писали:
D>Небольшое замечание. Бывает так, что вместо того, чтобы скрипту запуститься, выскакивает Блокнот. Просто кто-то с кривыми руками попортил вашу систему. В свойствах папки выберите "Типы файлов", найдите расширение JS, нажмите кнопку "Дополнительно", для действия "Открыть" запишите D>
D>cscript.exe "%1"
D>
D>вроде теперь все.
Не проще ли сразу батник сделать который скрипт запускает?
cscript.exe "%~1"//Nologo
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Здравствуйте, Left2, Вы писали:
_FR>>ИМХО, кузявее эту логику вынести из скрипта в Pre-Build Event:
L>Честно говоря не люблю Pre- и Post- ивенты. Натыкался на глюки непонятности при их работе.
L>Посему обычно добавляю в проект отдельный текстовый файл типа IncrementVersion.txt у которого custom build step выставляю в вызов скрипта. А для rc-файла ставлю этот IncrementVersion.txt в additional dependencies. Тогда и с тем чтобы отключить это в Debug или Release проблем не возникает.
Ага, ты это в шестой студии делаешь или где?
L>А вообще самый правильный путь здесь (ИМХО!) это написать скрипт который билдит релиз, благо кроме собственно билда Exe и Dll наверняка приходится делать кучу дополнительных задач (у меня к примеру есть в проекте куча XML-файлов, билд скрипт проверяет их на валидность, и т.д. и т.п.). Вот именно в этом скрипте самое место инкременту версии.
Build — скрипт это немного другое. Например, используя указанный If по конфигурации могу запускать post-build утилиты (которые, вообще говоря, понятия не имеют о конфигурациях средства разработки) с разным набором параметров:
If "$(ConfigurationName)" == "Debug" (
Rem Создаёт результат SGen в отдельной папке, сохраняя исходники для отладки
SGen.exe /Force /Assembly:"$(TargetPath)" /Out:"$(TargetDir)SGenOutput" /ParsableErrors /NoLogo /Debug /Keep
Copy /Y "$(TargetDir)SGenOutput\$(TargetName).XmlSerializers.dll" "$(TargetDir)"
Copy /Y "$(TargetDir)SGenOutput\$(TargetName).XmlSerializers.pdb" "$(TargetDir)"
) Else (
Rem Просто создаёт результирующю сборку "без мусора"
SGen.exe /Force /Assembly:"$(TargetPath)" /ParsableErrors /NoLogo
)
Выносить этот If в скрипт, которому передаётся параметр — — из скрипта не будет доступа к таким полезным вещам как $(TargetDir), $(TargetPath), … Пережавать их все в качестве параметров тоже не красиво.
Help will always be given at Hogwarts to those who ask for it.
L>>Посему обычно добавляю в проект отдельный текстовый файл типа IncrementVersion.txt у которого custom build step выставляю в вызов скрипта. А для rc-файла ставлю этот IncrementVersion.txt в additional dependencies. Тогда и с тем чтобы отключить это в Debug или Release проблем не возникает. _FR>Ага, ты это в шестой студии делаешь или где?
Да натыкался по-моему и в 2003-й.
L>>А вообще самый правильный путь здесь (ИМХО!) это написать скрипт который билдит релиз, благо кроме собственно билда Exe и Dll наверняка приходится делать кучу дополнительных задач (у меня к примеру есть в проекте куча XML-файлов, билд скрипт проверяет их на валидность, и т.д. и т.п.). Вот именно в этом скрипте самое место инкременту версии. _FR>Build — скрипт это немного другое.
Ну у меня к примеру есть два вида скриптов для билда:
1. Главный билд-скрипт, у которого на выходе — готовая инсталляция продукта. В числе прочих задач он запускает devenv.com с параметром Release.
2. "Мелкие" билд-скрипты — они вызываются самой студией и работают в custom build steps, нужны для кодогенерации каких-то кусков, т.д. и т.п.
К примеру, инкрементить версию лучше в главном билд-скрипте, дабы экономить время на билдах. Он, в свою очередь, запустит "мелкие" билд-скрипты в процессе билда solution-а (вернее, запустит-то их devenv.com).