Delphi XE3: проблемы со сложными событиями предварительной сборки
В настоящее время мы переходим от Delphi XE к Delphi XE3, и у нас возникают серьезные проблемы с нашими событиями предварительной сборки.
Наши события предварительной сборки выглядят следующим образом:
SubWCRev "<SVN-Path>" "<InputFile>" VersionInfo.rc
brcc32 -foProject.res VersionInfo.rc
(обратите внимание, что эти две команды появляются на отдельных строках и содержат абсолютные пути в наших "реальных" командах)
то есть мы сначала извлекаем текущую версию SVN из рабочей копии, записываем эту информацию в VersionInfo.rc, а затем используем компилятор ресурсов Borland для создания файла ресурсов.
Это отлично работало в предыдущих версиях Delphi, но всякий раз, когда мы открываем параметры проекта в XE3, XE3 преобразует его в:
SubWCRev "<SVN-Path>" "<InputFile>" VersionInfo.rc &brcc32 -foProject.res VersionInfo.rc
(обратите внимание, что это одна строка, обе команды разделены одним амперсандом).
что приводит к сбою сборки.
Наше текущее решение состоит в том, чтобы вручную изменить это на
SubWCRev "<SVN-Path>" "<InputFile>" VersionInfo.rc && brcc32 -foProject.res VersionInfo.rc
то есть. мы используем два амперсанда для выполнения второй команды, если первая успешно выполнена.
Это работает, но только до тех пор, пока мы снова не изменим параметры проекта. Delphi XE3 всегда испортил событие предварительной сборки: - (
Кто-нибудь знает решение/обходное решение для этого? Я думаю, мы могли бы написать простой инструмент командной строки, который вызывает SubWCRev и brcc32, но я бы предпочел более простое решение.
ОБНОВЛЕНИЕ. Шаги по легкому воспроизведению этой ошибки
IDE
- Файл → Создать → Приложение форм VCL (Delphi)
- Build Project1
- Файл → Сохранить все, сохранить предлагаемые имена Unit1.pas/Project1.dpr
- Проект → Параметры
- выберите цель "Все конфигурации - все платформы"
-
События сборки → события предварительной сборки, введите это (две строки, извините за форматирование):
echo one > out.txt
echo two → out.txt
-
Создайте проект из среды IDE
- Сохранить и закрыть проект
Командная строка RAD Studio
- Перейдите в каталог проекта
- msbuild Project1.dproj = > OK
IDE
- Проект → Параметры
- нажмите "Путь поиска"
- нажмите ok
- Проект → Проект сборки
- Сохранить и закрыть проект
Командная строка RAD Studio
- msbuild Project1.dproj = > ОШИБКА
Ответы
Ответ 1
В итоге мы использовали обходное решение, подобное тому, что было предложено Дэвидом Хеффернаном:
- объединить все наши вызовы в один (Ruby) script PreBuild.rb
- скомпилируйте этот Ruby script в автономный исполняемый файл (так как не все разработчики имеют Ruby)
- используйте одно событие предварительной сборки в Delphi
В случае, если кто-то заинтересован, здесь наше событие PreBuild:
PreBuild "<path_to_SVN_working_copy>" "VersionInfo.rc.in" $(OUTPUTNAME).res
и здесь script PreBuild.rb:
#!/usr/bin/env ruby
require 'tempfile'
if ARGV.length < 3
puts "usage: #{$0} <path> <infile> <outfile>"
exit 1
end
# svnversion.exe is part of the SVN command line client
svnversion = "svnversion.exe"
path, infile, outfile = ARGV[0], ARGV[1], ARGV[2]
# call svnversion executable, storing its output in rev
rev_str = `#{svnversion} "#{path}"`.chop
# extract the first number (get rid of M flag for modified source)
rev = /^[0-9]+/.match(rev_str)[0]
# get current date
date = Time.new
# remove old output file (ignore errors, e.g. if file didn't exist)
begin
File.delete(outfile)
rescue
end
input = File.new(infile, "r")
tmpname = "VersionInfo.rc"
tmp = File.new(tmpname, "w+")
input.each do |line|
# replace $WCREV$ with revision from svnversion call
outline = line.gsub(/\$WCREV\$/, rev)
# replace $WCDATE$ with current date + time
outline = outline.gsub(/\$WCDATE\$/, date.to_s)
# write modified line to output file
tmp.puts(outline)
end
input.close
tmp.close
puts "SubWCRev: Revision: #{rev}, date: #{date}, written to #{tmpname}"
call = "brcc32 -fo#{outfile} #{tmpname}"
puts call
system(call)
Ответ 2
Я использую Delphi XE4, и у меня была та же проблема с почти теми же командами. Наш PreBuildEvent имеет 4 строки, я попробовал то, что описано здесь, поместил все на 1 строку и разделил мои команды с помощью & &, и это сработало. Затем я попытался изменить, чтобы увидеть, будет ли XE4 испортить мою предварительную сборку, но после того, как я вернул свою предварительную сборку на 4 строки, она все еще работала.
Наконец-то я понял, что с другими проектами, где я смог воспроизвести эту ошибку, просто отредактировав script, удалив CRLF в конце каждой строки и вернув обратно из среды XE4, он установил PreBuildEvent.
Ответ 3
Я столкнулся с этой проблемой в последние недели и решил ее самостоятельно с помощью Delphi.
Что вызвало эту проблему, это формат dproj. Поскольку dproj имеет формат XML, а события до сборки/после сборки использовали "&" в качестве метки для новой строки, dproj сохранит его как "&".
В некотором смысле Delphi сохранит его как "\n &&" при сохранении проекта. Это приводит к тому, что MSBuild неправильно понимает символ и показывает "Синтаксическая ошибка".
Следовательно, чтобы решить эту проблему, мы должны определить, существует ли sLineBreak + '&&' в dproj, который мы отправим в MSBuild.
С модификацией MSBuild будет отлично обрабатывать dproj. Я делюсь своим кодом в следующем блоке, программа может помочь нам изменить номер версии, исправить события до/после сборки:
program changeProjVer;
////////////////////////////////////////////////////////////////////////////////
/// Created by Dennies Chang [email protected], [email protected]
///
/// If you need to use this utility, please refer the original URL:
/// https://firemonkeylessons.blogspot.com/2019/04/delphiBuildCommandAndTools.html
///
/// And do not remve these lines.
/// The code is opened for all Delphi programmers, you can use it as
/// commercial/non-commercial usage, what you have to do, is to have a notice
/// for the original author.
///
/// And send an Email to [email protected] to me, thanks.
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, IdGlobal, Classes;
var
currentFile, tmpStr, completeStr, tmpMajor, tmpMinor, tmpRelease,
tmpBuild, configName: String;
lineIdx: Integer;
src: TStringList;
bDebug : boolean;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
if ParamCount < 2 then begin
writeln('Usage: changeProjVer.exe dprojFileFullPath versionNo [Debug|Release]');
writeln('versionNo should be contain 3 dots, e.g.,: 107.1.108.321');
writeln;
Readln;
end
else begin
currentFile := ParamStr(1);
tmpBuild := ParamStr(2);
bDebug := False;
if ParamCount >= 3 then begin
configName := ParamStr(3);
bDebug := configName.ToLower = 'debug';
end;
tmpMajor := Trim(Fetch(tmpBuild, '.'));
tmpMinor := Trim(Fetch(tmpBuild, '.'));
tmpRelease := Trim(Fetch(tmpBuild, '.'));
tmpBuild := Trim(Fetch(tmpBuild, '.'));
if FileExists(currentFile) then begin
src := TStringList.Create;
try
src.LoadFromFile(currentFile, TEncoding.UTF8);
for lineIdx := 0 to src.Count - 1 do begin
completeStr := src.Strings[lineIdx];
tmpStr := '';
if Pos('<VerInfo_MajorVer>', completeStr) > 0 then begin
tmpStr := Fetch(completeStr, '<VerInfo_MajorVer>');
tmpStr := #9 + #9 + '<VerInfo_MajorVer>' + tmpMajor +
'</VerInfo_MajorVer>';
// completeStr := tmpStr;
end
else if Pos('<VerInfo_MinorVer>', completeStr) > 0 then begin
tmpStr := Fetch(completeStr, '<VerInfo_MinorVer>');
tmpStr := #9 + #9 + '<VerInfo_MinorVer>' + tmpMinor +
'</VerInfo_MinorVer>';
// completeStr := tmpStr;
end
else if Pos('<VerInfo_Release>', completeStr) > 0 then begin
tmpStr := Fetch(completeStr, '<VerInfo_Release>');
tmpStr := #9 + #9 + '<VerInfo_Release>' + tmpRelease +
'</VerInfo_Release>';
// completeStr := tmpStr;
end
else if Pos('<VerInfo_Build>', completeStr) > 0 then begin
tmpStr := Fetch(completeStr, '<VerInfo_Build>');
tmpStr := #9 + #9 + '<VerInfo_Build>' + tmpBuild +
'</VerInfo_Build>';
// completeStr := tmpStr;
end
else if Pos('FileVersion=', completeStr) > 0 then begin
// FileVersion
completeStr := src.Strings[lineIdx];
tmpStr := '';
while Pos('FileVersion=', completeStr) > 0 do begin
tmpStr := Fetch(completeStr, 'FileVersion=');
tmpStr := tmpStr + 'FileVersion=' +
StringReplace(ParamStr(2), ' ', '',
[rfReplaceAll]) + ';';
Fetch(completeStr, ';');
end;
if Length(completeStr) > 0 then begin
tmpStr := tmpStr + completeStr;
end;
end;
// 這兩個會出現在同一行, 不要加 else
if Pos('ProductVersion=', completeStr) > 0 then begin
completeStr := tmpStr;
tmpStr := '';
// ProductVersion
while Pos('ProductVersion=', completeStr) > 0 do begin
tmpStr := Fetch(completeStr, 'ProductVersion=');
tmpStr := tmpStr + 'ProductVersion=' +
StringReplace(ParamStr(2), ' ', '',
[rfReplaceAll]) + ';';
Fetch(completeStr, ';');
end;
if Length(completeStr) > 0 then begin
tmpStr := tmpStr + completeStr;
end;
end;
if (tmpStr = '') and (tmpStr <> completeStr) then
tmpStr := completeStr;
src.Strings[lineIdx] := tmpStr;
end;
src.Text := StringReplace(src.Text, sLineBreak + '&&', '&', [rfReplaceAll]);
src.SaveToFile(currentFile, TEncoding.UTF8);
finally
src.Free;
end;
end;
end;
except
on E: Exception do
writeln(E.ClassName, ': ', E.Message);
end;
end.