Delphi XE3: проблемы со сложными событиями предварительной сборки - программирование
Подтвердить что ты не робот

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

  • Проект → Параметры
    • нажмите "Путь поиска"
      • Введите "a"
      • удалить "a"
    • нажмите ok
  • Проект → Проект сборки
  • Сохранить и закрыть проект

Командная строка RAD Studio

  • msbuild Project1.dproj = > ОШИБКА
4b9b3361

Ответ 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 + '&amp;&amp;', '&amp;', [rfReplaceAll]);
               src.SaveToFile(currentFile, TEncoding.UTF8);
            finally
               src.Free;
            end;
         end;
      end;
   except
      on E: Exception do
         writeln(E.ClassName, ': ', E.Message);
   end;

end.