16

We are currently in the process of switching from Delphi XE to Delphi XE3, and we are having serious problems with our pre-build-events.

Our pre-build events look like this:

  SubWCRev "<SVN-Path>" "<InputFile>" VersionInfo.rc
  brcc32 -foProject.res VersionInfo.rc

(note that these two commands appear on separate lines; and contain the absolute paths in our "real" commands) i.e. we first extract the current SVN version from the working copy, write this information to VersionInfo.rc and then use the Borland resource compiler to generate a resource file.

This worked perfectly in previous Delphi versions, but whenever we open the project options in XE3, XE3 converts this to:

  SubWCRev "<SVN-Path>" "<InputFile>" VersionInfo.rc &brcc32 -foProject.res VersionInfo.rc

(note that this is a single line, both commands separated by a single ampersand). which causes the build to fail.

Our current workaround is to manually change this to

  SubWCRev "<SVN-Path>" "<InputFile>" VersionInfo.rc && brcc32 -foProject.res VersionInfo.rc

i.e. we use two ampersands to execute the second command if the first one succeeds.

This works, but only until we edit the project options again - Delphi XE3 always messes up the pre-build event :-(

Does anybody know a solution/workaround for this? I guess we could write a simple command line tool that calls SubWCRev and brcc32, but I'd prefer a simpler solution.

UPDATE: Steps to easily reproduce this bug

IDE

  • File -> New -> VCL forms application (Delphi)
  • Build Project1
  • File -> Save all, keep suggested names Unit1.pas / Project1.dpr
  • Project -> Options
  • choose target "All configurations - all platforms"
  • Build Events -> Pre-build events, enter this (two lines, sorry for the formatting):

    echo one > out.txt

    echo two >> out.txt

  • Build the project from the IDE

  • Save & close the project

RAD Studio command prompt

  • Navigate to the project directory
  • msbuild Project1.dproj => OK

IDE

  • Project -> Options
    • click into "Search path"
      • Enter "a"
      • delete the "a"
    • click ok
  • Project -> Build project
  • Save & close the project

RAD Studio command prompt

  • msbuild Project1.dproj => ERROR
Frank Schmitt
  • 30,195
  • 12
  • 73
  • 107
  • 10
    I've encountered this. I gave up trying to find a solution. My pre-build actions now all read `call PreBuild.bat` – David Heffernan Feb 14 '13 at 13:08
  • Did either of you post a Quality Central bug report? Nothing turns up looking for XE3 bugs containing 'build' – Jan Doggen Feb 14 '13 at 19:00
  • @JanDoggen No, not yet, since we started noticing this yesterday. I'll try to post one tomorrow. – Frank Schmitt Feb 14 '13 at 19:04
  • @DavidHeffernan what parameters are you passing to your `PreBuild.bat`? – Jeroen Wiert Pluimers Feb 14 '13 at 20:17
  • 5
    @JeroenWiertPluimers At the moment my actiona read: `if exist PostBuild.bat call PostBuild.bat $(Platform) $(Config) $(OutputDir)`. And then the `PostBuild.bat` script calls a Python script so that I can write my scripts in a real language. I actually impose the build actions in a shared option set that I reference from all of my projects. That way I enforce consistency and predictability. I know others use tools like FinalBuilder but building is so important that I feel it's worth my effort in rolling my own tooling. – David Heffernan Feb 14 '13 at 20:24
  • Update: we've now posted a Quality Central bug report - #112728 – Frank Schmitt Feb 15 '13 at 07:43
  • 1
    I've just been playing with this and wonder if the problem is down to the upgrade process. Try in a new XE3 project. I can enter a multi-line action. Then save, open, edit project settings, save, open, and the multi-line action is preserved just fine. – David Heffernan Feb 18 '13 at 10:26
  • @DavidHeffernan Doesn't seem to work for me. I created a new project from scratch, added my pre-build events, saved & closed the project -> ok. Re-opened the project, opened the options (didn't even chagne anything), build from IDE -> ok. close project (save: yes), build from command line => same error as before, Delphi again messed up the Settings :-( Did you use variables (e.g. $OUTPUTNAME) in your script? – Frank Schmitt Feb 18 '13 at 10:49
  • I really cannot get XE3 to misbehave. – David Heffernan Feb 18 '13 at 13:59
  • @DavidHeffernan I've updated the question with step-by-step instructions - this breaks the build every time (at least for me). – Frank Schmitt Feb 20 '13 at 10:47
  • Our script call is similar to Davids except that we include the full paths of the dproj and output which you use in your scripts and parse e.g. %~n2 for the name of your output file. CALL PostBuildEvent.bat "$(INPUTPATH)" "$(OUTPUTPATH)" "$(Platform)" "$(Config)" – Tobias R Feb 21 '13 at 08:38
  • You've offered a bounty on this question, but it's not clear what solution you're seeking. You already know you're dealing with a Delphi bug, and you already know an easy workaround. What more do you expect? – Rob Kennedy Feb 22 '13 at 14:09
  • 1
    I was looking for a hint on what I might be doing wrong, since I didn't want to believe XE3 contains such a (IMHO) serious error. – Frank Schmitt Feb 22 '13 at 19:59
  • Frank is your XE3 updated? Maybe that's the difference between yours and @david's. – jachguate Feb 22 '13 at 21:30
  • Your repro works for me. Sorry! – David Heffernan Feb 22 '13 at 22:13

3 Answers3

2

We ended up using a workaround similar to what was proposed by David Heffernan:

  • combine all our calls into a single (Ruby) script PreBuild.rb
  • compile this Ruby script into a standalone executable (since not all developers have Ruby installed)
  • use a single pre-build event in Delphi

In case anyone's interested, here's our PreBuild event:

PreBuild "<path_to_SVN_working_copy>" "VersionInfo.rc.in" $(OUTPUTNAME).res

and here's the 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)
Frank Schmitt
  • 30,195
  • 12
  • 73
  • 107
2

I'm using Delphi XE4, and I had the same problem with almost the same commands. Our PreBuildEvent has 4 lines, I tried what is described here, put all on 1 line and separating my commands with &&, and it worked. I then tried to modify to see if XE4 will mess my prebuild, but after putting back my prebuild on 4 lines, it was still working.

I finally figured out with other projects where I was able to reproduce this error, that simply editing the script by removing the CRLF at the end of each line, and putting it back, from XE4 environment, it fixed the PreBuildEvent.

  • For a minute there, I thought you had posted a "me too" non-answer - we get a lot of those. Looks like you really do have an answer in there, though, so it's all good. – user1618143 Dec 06 '13 at 20:10
0

I met this problem in recent weeks, and I solved it with Delphi by myself.

What caused this problem, is the format of dproj. Since dproj is XML format, and the pre-build/post-build events used "&" as the mark for new line, the dproj will save it as "&".

In somehow, Delphi will save it as "\n&&" when the project is saved. That cause MSBuild misunderstand the symbol and show "Syntax error".

Hence, what we do to solve this problem, is to detect if sLineBreak + '&&' exists in dproj which we will send to MSBuild.

With the modification, MSBuild will process the dproj perfectly. I share my code in the following block, the program can help us the change the version number, correct the pre/post build events:

program changeProjVer;

////////////////////////////////////////////////////////////////////////////////
/// Created by Dennies Chang dennies@ms4.hinet, dennies226@gmail.com
///
///   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 dennies@ms4.hinet.net 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. 
Dennies Chang
  • 564
  • 5
  • 15