Recent

Author Topic: [SOLVED] How to compile conditional for FPC 3.0.4 and all later?  (Read 3916 times)

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Control "TSpinEditEx" was introduced in Lazarus 1.8.0 which came out with FPC 3.0.4. I used it frequently, but wanted my sources to be backwards-compatible, so I created a lot of constructs like this:
Code: Pascal  [Select][+][-]
  1. type
  2.   TForm1 = class(TForm)
  3.   ...
  4. {$IFDEF VER3_0_4}
  5.    SpinEdit1: TSpinEditEx;
  6. {$ELSE}
  7.    SpinEdit1: TSpinEdit;  
  8. {$ENDIF}  
  9.   ...
  10.   private
  11.   public
  12.   end;  

But now I installed FPC 3.2.0 beta and must adapt all this constructs to compile conditional also for all later FPC versions than 3.0.4. I want to do this in a way, which works for all future, even if I grow old enough to install someday FPC 4.x or 5.x ;-)

The best way which I found is this:

a) put this in the beginning of each unit where I need it:
Code: Pascal  [Select][+][-]
  1. // define symbol "USE_TSpinEditEx" for FPC 3.0.4 and all later:
  2. {$DEFINE USE_TSpinEditEx}    // Default: define symbol
  3. {$IFDEF VER3_0_2}
  4.    {$UNDEF USE_TSpinEditEx}  // exclude 3.0.2
  5. {$ENDIF}
  6. {$IFDEF VER3_0_0}
  7.    {$UNDEF USE_TSpinEditEx}  // exclude 3.0.0
  8. {$ENDIF}
  9. {$IFDEF VER2}
  10.    {$UNDEF USE_TSpinEditEx}  // exclude 2.x.y
  11. {$ENDIF}
  12. // because I never installed FPC 1.x.y I must not exclude it

b) then use symbol "USE_TSpinEditEx" in the rest of the unit like:
Code: Pascal  [Select][+][-]
  1. type
  2.   TForm1 = class(TForm)
  3.   ...
  4. {$IFDEF USE_TSpinEditEx}
  5.    SpinEdit1: TSpinEditEx;
  6. {$ELSE}
  7.    SpinEdit1: TSpinEdit;  
  8. {$ENDIF}  
  9.   ...
  10.   private
  11.   public
  12.   end;

This looks a bit complicated... So my questions are:
 - is there a better/shorter/more elegant way? How do you do things like this?
 - is it true, that a unit can't "export" a defined symbol like "USE_TSpinEditEx", so that I must copy the code from a) in every unit where I need symbol "USE_TSpinEditEx"?

Thanks in advance.
« Last Edit: January 08, 2020, 07:27:13 pm by Hartmut »

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: How to compile conditional for FPC 3.0.4 an all later?
« Reply #1 on: January 08, 2020, 10:21:44 am »
Add LCLVersion to "uses". Then check for {$IF LCL_FullVersion >= 1080000} which is true for Laz v1.8+. Don't use {$IFDEF ...} here!

But such directives are not allowed in the lfm file. Therefore you must create the spin edit at runtime so that all the conditional code remains in the unit file!

Code: Pascal  [Select][+][-]
  1. uses
  2.   LCLVersion, ...
  3.  
  4. type
  5.   TForm1 = class(TForm)
  6.     procedure FormCreate(Sender: TObject);
  7.   ...
  8.   private
  9.   {$IF LCL_FullVersion >= 1080000}
  10.     SpinEdit1: TSpinEditEx;
  11.   {$ELSE}
  12.     SpinEdit1: TSpinEdit;
  13.   {$IFEND}
  14.   end;
  15.  
  16. procedure TForm1.FormCreate(Sender: TObject);
  17. begin
  18.   {$IF LCL_FullVersion >= 1080000}
  19.   SpinEdit1 := TSpinEditEx.Create(self);
  20.   {$ELSE}
  21.   SpinEdit1 := TSpinEdit.Create(self);
  22.   {$IFEND}
  23.   SpinEdit1.Parent := Self; // or whichever control contains the spin edit
  24.   SpinEdit1.Left := ....
  25.   SpinEdit1.Top := ....
  26.   // ... fill in other properties that you had set in the object inspector



Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: How to compile conditional for FPC 3.0.4 an all later?
« Reply #2 on: January 08, 2020, 11:58:46 am »
Great! That makes life much easier. Thanks a lot for this (including the advice how to avoid manually having to patch the lfm-file when switching TSpinEditEx <=> TSpinEdit).

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: [SOLVED] How to compile conditional for FPC 3.0.4 an all later?
« Reply #3 on: January 08, 2020, 01:44:45 pm »
It hardly makes code simpler in this case, but FPC does have a simple macro facility (not enabled by default) which lets you write this sort of code following, which if you have many instances where a variable definition/implementation of spinedit is needed might be worth the extra typing:
Code: Pascal  [Select][+][-]
  1. unit mainConditional;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. {$Macro on}
  6.  
  7. {$Define DeclareAppropriateSpinEdit1 := {$IF LCL_FullVersion >= 1080000}
  8.     SpinEdit1: TSpinEditEx
  9.   {$ELSE}
  10.     SpinEdit1: TSpinEdit
  11.   {$IFEND} }
  12.  
  13. {$Define CreateAppropriateSpinEdit1 :=  {$IF LCL_FullVersion >= 1080000}
  14.     SpinEdit1 := TSpinEditEx.Create(Self)
  15.   {$ELSE}
  16.     SpinEdit1 := TSpinEdit.Create(Self)
  17.   {$IFEND} }
  18.  
  19. interface
  20.  
  21. uses
  22.   Classes, SysUtils, Forms, Controls,
  23.   Spin, LCLVersion, SpinEx;
  24.  
  25. type
  26.  
  27.   TForm1 = class(TForm)
  28.     procedure FormCreate(Sender: TObject);
  29.   private
  30.     DeclareAppropriateSpinEdit1;
  31.   end;
  32.  
  33. var
  34.   Form1: TForm1;
  35.  
  36. implementation
  37.  
  38. {$R *.lfm}
  39.  
  40. procedure TForm1.FormCreate(Sender: TObject);
  41. begin
  42.   CreateAppropriateSpinEdit1;
  43.   SpinEdit1.Left := 10;
  44.   SpinEdit1.Top := 10;
  45.   SpinEdit1.Parent := Self;
  46. end;
  47.  
  48. end.




Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: [SOLVED] How to compile conditional for FPC 3.0.4 an all later?
« Reply #4 on: January 08, 2020, 02:00:41 pm »
Thank you very much howardpc for that suggestion to simplify my code if I have many TSpinEditEx instances. I didn't know that before.

But I got a problem when using wp's solution inside a uses clause:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
  9.  LCLVersion,
  10. {$IF LCL_FullVersion >= 1080000}
  11.  SpinEx, // this unit does not exist before LCL 1.8.0
  12. {$ENDIF}
  13.  spin;
  14.  
  15. implementation
  16.  
  17. {$R *.lfm}
  18.  
  19. end.  

When I compile this with Lazarus 1.8.4 or 2.0.6 on Windows 7 (32 bit) I get
unit1.pas(10,5) Error: Incompatible types: got "AnsiString" expected "Int64"
unit1.pas(10,5) Error: Compile time expression: Wanted Boolean but got <erroneous type> at IF or ELSEIF

I attached my little demo project. Thanks for any help.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: [REOPENED] How to compile conditional for FPC 3.0.4 and all later?
« Reply #5 on: January 08, 2020, 02:37:13 pm »
Moving LCLVersion to the start of the uses clause solved this for me.
I think this is a minor bug?

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: [REOPENED] How to compile conditional for FPC 3.0.4 and all later?
« Reply #6 on: January 08, 2020, 03:02:01 pm »
Thank you for that hint, but strange to say this does not help at me with Lazarus 1.8.4 and 2.0.6. Which Lazarus version did you try? Did you use my attached project at reply #4 (maybe some compiler setting is "wrong")?

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: [REOPENED] How to compile conditional for FPC 3.0.4 and all later?
« Reply #7 on: January 08, 2020, 03:06:12 pm »
I think the Lazarus version is immaterial.
The problem is with the FPC parser not having evaluated the constants in the LCLVersion unit in time to interpret the $IF correctly (or something of the sort).
I used a recent FPC version 3.3.1 (not a release version).

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: [REOPENED] How to compile conditional for FPC 3.0.4 and all later?
« Reply #8 on: January 08, 2020, 03:20:59 pm »
Thanks. I made a test with Lazarus 2.1.0 r62449 and FPC 3.3.1 r43796 and the error message still occurs although I put unit LCLVersion at 1st position after "uses".
Do you think I should create a bug report or do you think it's already fixed because in your FPC version it does not occur?

Thaddy

  • Hero Member
  • *****
  • Posts: 14205
  • Probably until I exterminate Putin.
Re: [REOPENED] How to compile conditional for FPC 3.0.4 and all later?
« Reply #9 on: January 08, 2020, 03:57:38 pm »
Thanks. I made a test with Lazarus 2.1.0 r62449 and FPC 3.3.1 r43796 and the error message still occurs although I put unit LCLVersion at 1st position after "uses".
Do you think I should create a bug report or do you think it's already fixed because in your FPC version it does not occur?
Remember the {$IF} family (all of them) are unit local, so every other unit would need the same {$IF/$IFDEF}. Maybe that is the case.
I never encountered your issue, probably because I handle them correct by nature. (Which also means I can not explain it without full code)
Specialize a type, not a var.

wp

  • Hero Member
  • *****
  • Posts: 11857
Re: [REOPENED] How to compile conditional for FPC 3.0.4 and all later?
« Reply #10 on: January 08, 2020, 04:02:23 pm »
Sorry for leading you on the wrong track... I remember now that I had the same issue with [$IF inside the "uses" clause in FPSpreadsheet, and it ended in writing an inc file with various manually set defines, e.g.

Code: Pascal  [Select][+][-]
  1. // Deactivate this define when the IDE knows the component TSpinEditEx, i.e. for Laz v1.8 or later
  2. {$DEFINE USE_SPINEDITEX}

Include this inc file, named "test.inc" here, in every unit where it is needed. The code in the unit then would be (following the example of above):

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$I test.inc}
  4.  
  5. uses
  6.   ...,
  7.   {$IFDEF USE_SPINEDITEX} SpinEx, {$ELSE} Spin, {$ENDIF}
  8.   ...
  9.      
  10.     type
  11.       TForm1 = class(TForm)
  12.         procedure FormCreate(Sender: TObject);
  13.       ...
  14.       private
  15.       {$IFDEF USE_SPINEDITEX}
  16.         SpinEdit1: TSpinEditEx;
  17.       {$ELSE}
  18.         SpinEdit1: TSpinEdit;
  19.       {$ENDIF}
  20.       end;
  21.      
  22.     procedure TForm1.FormCreate(Sender: TObject);
  23.     begin
  24.       {$IFDEF USE_SPINEDITEX}
  25.       SpinEdit1 := TSpinEditEx.Create(self);
  26.       {$ELSE}
  27.       SpinEdit1 := TSpinEdit.Create(self);
  28.       {$ENDIF}
  29.       SpinEdit1.Parent := Self; // or whichever control contains the spin edit
  30.       SpinEdit1.Left := ....
  31.       SpinEdit1.Top := ....
  32.       // ... fill in other properties that you had set in the object inspector
  33.  

Then there is the problem that you must change the define whenever you switch from the old to the new IDE and vice versa. A possible way to avoid this is to move the correct include file into a version-dependent directory and specify this in the project options' include-path by using the IDE macro $(LazVer). See attached project - it displays the text "SPINEDITEX is defined" or "SPINEDITEX is NOT defined" in the form caption depending on whether you compile the project from Laz 2.0.6 or Laz 1.6.4.
« Last Edit: January 08, 2020, 04:22:46 pm by wp »

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: [REOPENED] How to compile conditional for FPC 3.0.4 and all later?
« Reply #11 on: January 08, 2020, 04:49:32 pm »
Remember the {$IF} family (all of them) are unit local, so every other unit would need the same {$IF/$IFDEF}. Maybe that is the case.
I never encountered your issue, probably because I handle them correct by nature. (Which also means I can not explain it without full code)
Are you saying that using {$IF ...} inside a uses clause is "incorrect by nature"?
To my mind the incorrectness is in the compiler stumbling over this, hence my suggestion that this is a bug.
However, I am not really sure because I avoid explicit conditional compilation if possible (it makes source code very much harder to read - and in some cases almost impenetrable), so I am not skilled in all the nuances of FPC's extensive conditional dialect. Nevertheless I recognise that for a cross-platform environment conditional compilation is a necessary evil. The FCL/LCL hides it from most of us by clever use of .inc files, but the gory details are still there, of course, if you delve into the sources.

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: [REOPENED] How to compile conditional for FPC 3.0.4 and all later?
« Reply #12 on: January 08, 2020, 06:21:23 pm »
Thanks a lot to all for your new replies.

Remember the {$IF} family (all of them) are unit local, so every other unit would need the same {$IF/$IFDEF}. Maybe that is the case.
Please have a look in my code in reply #4, everything is in the same unit.

Quote
I never encountered your issue, probably because I handle them correct by nature. (Which also means I can not explain it without full code)
Please find the full code attached in reply #4.

I remember now that I had the same issue with [$IF inside the "uses" clause in FPSpreadsheet, and it ended in writing an inc file with various manually set defines, e.g.
Code: Pascal  [Select][+][-]
  1. // Deactivate this define when the IDE knows the component TSpinEditEx, i.e. for Laz v1.8 or later
  2. {$DEFINE USE_SPINEDITEX}
Include this inc file, named "test.inc" here, in every unit where it is needed.
...
Then there is the problem that you must change the define whenever you switch from the old to the new IDE and vice versa. A possible way to avoid this is to move the correct include file into a version-dependent directory and specify this in the project options' include-path by using the IDE macro $(LazVer). See attached project - it displays the text "SPINEDITEX is defined" or "SPINEDITEX is NOT defined" in the form caption depending on whether you compile the project from Laz 2.0.6 or Laz 1.6.4.

That is an interesting approach. And I learned how to use version-dependent directories via IDE macro $(LazVer). Thank you for that including the demo. But in my personal case I found it easier to include "-dNo_Unit_SpinEx" in the fpc.cfg of Lazarus 1.4.4 and 1.6.2 (I don't have more < 1.8.0) and then use it like:

Code: Pascal  [Select][+][-]
  1. uses
  2.  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs,
  3. {$IFNDEF No_Unit_SpinEx}
  4.    SpinEx,
  5. {$ENDIF}
  6.  spin;
  7. ...
  8. type
  9.   TForm1 = class(TForm)
  10.   ...
  11. {$IFNDEF No_Unit_SpinEx}
  12.    SpinEdit1: TSpinEditEx;
  13. {$ELSE}
  14.    SpinEdit1: TSpinEdit;  
  15. {$ENDIF}  
  16.   ...
  17.   private
  18.   public
  19.   end;  

What do others think: should I create a bug report (for the problem in reply #4)? If yes, please howardpc give me the following infos, that I can include them:
 - is it correct, that you got the same error message as me, before you moved unit LCLVersion to the start of the uses clause?
 - which revision# of FPC 3.3.1 did you use?
 - which OS and version?

Thanks again to all for your valuable help.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
Re: [REOPENED] How to compile conditional for FPC 3.0.4 and all later?
« Reply #13 on: January 08, 2020, 06:42:21 pm »
If yes, please howardpc give me the following infos, that I can include them:
 - is it correct, that you got the same error message as me, before you moved unit LCLVersion to the start of the uses clause?
Yes, I got the messages

unit1.pas(9,5) Error: Incompatible types: got "AnsiString" expected "Int64"
unit1.pas(9,5) Error: Compile time expression: Wanted Boolean but got <erroneous type> at IF or ELSEIF
Strangely, moving LCLVersion to the start of the uses clause makes no difference when I go back to recompile the project now. It fails to compile every time, as it does for you.
Looks like the effect of that change I made was an unrepeatable fluke...

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: [REOPENED] How to compile conditional for FPC 3.0.4 and all later?
« Reply #14 on: January 08, 2020, 07:26:56 pm »

 

TinyPortal © 2005-2018