Code Conversion Guide/sk

From Lazarus wiki
Revision as of 20:42, 2 April 2005 by Slavko (talk | contribs)
Jump to navigationJump to search

Konverzia a prenos zdrojového kódu Delphi pre Lazarus

About

Tento článok je o konverzii existujúceho zdrojového kódu Delphi tak, aby pracoval s prekladačom Free Pascal Compiler a IDE Lazarus. Keďže Lazarus a Free Pascal majú spoločné črty s Delphi a Kylix, nejedná sa o klony. Existuje veľa rozdielov vo volaní knižníc a konvenčných rozdielov... a v niektorých oblastiach je Free Pascal rozšírený a môže byť viac náročný na správnu syntax. Pozrite si príručku Lazarus pre užívateľov Delphi, v ktorej nájdete popis niektorých funkčných rozdielov.

Cieľom tejto príručky je zdokumentovať niektoré zo špecifických rozdielov, s ktorými sa často stretnete počas procesu konverzie kódu pri prekladaní existujúceho kódu z Delphi na Lazarus.

Tento dokument bol umiestnený na Wiki aby mohol byť jednoducho rozšírený každým, kto sa stretol s unikátnym problémom a zverejní ho aj pre ostatných.

Výber komponentov alebo knižnice pre konverziu

Kde hľadať kód na konverziu

Na nete je dostupného veľa kódu, ktorý môže byť konvertovaný pre použitie s FreePascal a Lazarus. Tu je Stránka kódov, ktorá je dobrým začiatkom. Prosím vás, pridajte na do nej, ak poznáte nejaký iný dobrý odkaz. Turbo-Power Software uvoľnil ich celú ponuku pod MPL. Zoznam dostupných balíčkov môžete nájsť na Sourceforge.

Aby sme predišli duplicitným snahám, balíčky, ktoré už sú konvertované sú vypísané na stránke Code And Components. Ak ste prekonvertovali balíček alebo na nejakom pracujete, pridajteprosím poznámku na stránku [Current Projects].

Licencovanie

Licencie existujúceho kódu siahajú od freeware/public domain po reštriktívne verzie, ktoré bránia modifikácii, redistribúcii a komerčnému použitiu. Pred konverziou každého balíčka, je dobré preskúmať jeho licenciu a zaistiť, že je kompatibilná s Lazarus a Free Pascal. Výber licencie je veľmi dôležitý pri komponentoch, pretože umiestnenie jedného na formulár môže navodiť nechcenú alebo nekompatibilnú licenciu na celú aplikáciu.

Keď konvertujete komponenty, rešopektujte prosím elanie pôvodného autora a ponechajte všetky copyrighty a licenčné hlavičky spolu s emailovou adresou a url. Je slušné a často i užitočné informovať autora, že jeho komponent je konvertovaný... hlavne ak je komponent pod obmedzujúcou licenciou. Nový záujem o staré alebo zabudnuté komponenty môže často podnietiť autora na revíziu originálu a zmeniť reštriktívnu licenciu.

Vo všeobecnosti, Public Domain (freeware) a LGPL/MPL sú najprispôsobiteľnejšie pre distribúciu komponentov. Ďalšie informácie môžete nájsť na Definícii Open Source a je dobrým začiatkom. Existuje tiež niekoľko porovnaní, ktoré môžu pomôcť objasniť ako rôzne typy licencií pracujú a aký budú mať dosah na kód, ku ktorému patria. Hľadajte "open source license comparison".

Závislosti

Ďalším krokom pred začatím práce na konverzii je overiť si, že kód nie je závislý na na iných balíčkoch, ktoré možno nemáte alebo predstavujú veľkú výzvu pre konverziu. Niektoré ponuky freeware sú obmedzené alebo rozšírené vlastnené balíčky, ktoré často nie sú dostupné alebo prichádzajú s nevhodnou licenciou.

Poznámky k prekladaču

Momentálne stabilná verzia 1.0.x Free Pascalnepodporuje rozhrania, vlákna a variantné záznamy. Ak vami konvertovaný kód vyžaduje tieto konštrukcie, budete musieť použiť 1.1 vývojovú vetvu prekladača... ktorý nie vždy garantuje spoluprácu s Lazarus.

Poznámky k platforme a OS

Lazarus a Free Pascal sú nadplatformové a nadarchitektúrne vývojové nástroje. V kontraste, väčšina existujúceho kódu Delphi bola špecificky navrhnutá pre beh na rpocesore Intel a s Win32. Ak váš komponent má eľa špecifického kódu Win32, bude možno múdrejšie skúsiť nájsť alternatívu, ktorá je menej závislá na platforme. Ale nenechajte sa tým zastaviť... to je pravé prekvapenie, čo Lazaruspodporuje!

Konverzia

Nastavenie IDE Lazarus pre konverziu projektu

Vytvorenie skúšobného projektu

  • Umiestnite konvertovaný kód do podadresára (napr: convertdir)
  • Vyvolajte Lazarus
  • File->Save All do adresára convertdir. Významné mená pre Project a predvolenú jednotku sú voliteľné.
  • Otvorte "základnú" konvertovanú jednotku v convertdir
  • Pridajte ju do projektu: Project->Add Active Unit to Project
  • Spusťte Tools->Quick Syntax Check alebo Run->Build All na začatie.

Na čo treba dať pozor

  • Mená súborov sú citlivé na veľkosť pri prekladačoch série 1.0.x. Ak pracujete s touto verziou, premenujte všetky súbory malými písmenami. Ak to neurobíte, ľahko dostanete chybové hlásenie File not found.

Zdrojové kódy Delphi VCL a Kylix CLX v Lazarus

Pri konverzii zdrojových kódov Delphi/Kylix, je často užitočné použiť Find declaration, aby ste sapresvedčili, či daná funkcia existuje. IDE Lazarus môže analyzovať zdrojové kódy Delphi/Kylix. Na to potrebujete nastaviť niektoré cesty a nastaveniaprekladača. Jednoducho to môžete nastaviť pomocou Environment->CodeTools Defines Editor->Insert Template.

Problémy a riešenia konverzie

Konvertovanie formulárov a jednotiek Delphi do Lazarus

Mnoho existujúcich formulárov Delphi môžete konvertovať pomocou IDE Lazarus pomocou zabudovaného konvertora formulárov DFM na LFM. Nájdete ho v menu Tools->Convert DFM file to LFM. Vyvolajte súborový dialóg, vyberte *.dfm a konvertor urobí zvyšok.

Ak potrebujete konvertovať celú jednotku (s alebo bez formulára), Lazarus má tiež zabudovaný nástroj: Convert Delphi unit to Lazarus unit, ktorý urobí nasledujúce:

  1. premenuje súbory .pas a .dfm malými písmenami.
  2. prekonvertuje súbor .dfm na súbor .lfm (aktuálne bez kontroly obsahu, len formát)
  3. vytvorí prázdny súbor .lrs (obsah bude pridaný neskôr)
  4. pridá direktívu
    {$mode delphi}
  5. nahradí jednotku Windows jednotkou LCLIntf
  6. ak je to potrebné, pridá jednotku LResources
  7. odoberie jednotku Variants
  8. odoberie direktívu
    {$R *.dfm}
  9. pridá sekciu
    initialization
    a direktívu
    {$i unit.lrs}

Toto umožní rýchlu a ľahkú konverziu väčšiny jednotiek z formátu Delphi do formátu Lazarus. Tento nástroj nevykonáva žiadnu kontrolu platnosti alebo automatické zmeny syntaxe, tak všetky potrebné zmeny syntaxe, dodatočné zmeny jednotiek, alebo zmeny dfm/pas pre rozdiely v prvkoch/komponentoch, musíte urobiť manuálne, pomocou niekoľkých sprievodcov, ktorý vám pomôžu niektoré kroky, hlavne opravovanie konvertovaných formulárov (lfm).

Výber správneho módu prekladača

Prekladač Free Pascal podporuje 5 rôznych módov Pascalu. Napríklad TP pre Turbo Pascal, vám dovolí prekladať jednotky Turbo Pascal. Má i mód pre kompatibilitu s DELPHI, ktorý môžete nastaviť na uľahčenie konverzie kódu. Lazarus preferuje mód OBJFPC, ktorý je skoro ako mód DELPHI, ale bez mnohoznačnosti syntaxe Delphi. Tu sú dôležité body:

Mód môže byť zvolený na príkazovom riadku alebo na začiatku zdrojového kódu. Použitie príkazového riadku má výhody, ktoré nevyužijete na zmenu zdrojového kódu, ale i nevýhody, ktoré musia byť povedané tiež.

Väčšinu jednotiek Delphi môžete jednoducho preložiť pomocou Free Pascal pridaním

{$IFDEF FPC}
  {$MODE DELPHI}
{$ENDIF}

rovno za meno jednotky.

Ďalšie podrobnosti o módoch Free Pascal môôžete nájsť v Dokumentácii Free Pascal.

Cross-Platform considerations

  • Inline assembler is always a problem because it will bind the code to the Intel architecture. Some developers do algorithm prototypes in Pascal and ifdef the their optimized assembler. Fortunately Turbo-Power did this in numerous places with their code. If this is the case with the package you're converting, throw the switch back to Pascal.
  • Don't reference specific memory location like the BIOS data area. Find out what the code needs and try to find a cross platform alternative.
  • Don't do processor specific tricks (like using the Intel TSC) without enclosing your code in an ifdef for the platform the code needs... and providing an alternative for environments that don't have the hardware capability.
  • If you need some OS specific code, than you can use IFDEFs. See below for a list of macros.

Useful compiler variables

To write code, that behaves on different systems differently, you can use the

{$IFDEF Name}

directives.

  • {$IfDef LCL}

This variable is defined, when using the LCL package. Useful to write code, that works with the LCL and Delphi.

  • {$IfDef FPC}

This variable is defined, when using the FPC compiler. Useful to write code, that works with FPC and Delphi.

  • {$IfDef Unix}
    ,
    {$IfDef Win32}
    , ...

Defined by FPC for the current Target OS. Delphi defines "Linux", "Win32" and "MSWindows". Free Pascal runs on much more platforms and so it is recommended to use the more general items. For example "Unix" is defined for Linux, FreeBSD, NetBSD and OpenBSD, where Lazarus already runs. Use

{$IfDef Linux}
  {$Define Unix}
{$EndIf}

to work around this for Kylix.

For more details see the Free Pascal Documentation.

Finding a missing identifier

There are differences in how the LCL is organized when compared to the Delphi VCL. If you get a "not found" compiler error about a major class or identifier, the chances are good that it's in a different unit. A complete cross reference can be found by grep'ing lazarus/docs/xml or the lcl subdirectory.

For example the commonly used tbutton typically throws an error in Delphi code because it's located in a unit named buttons.pp. The following command finds the correct unit very quickly (in the lazarus source directory):

 grep -in ' tbutton =' lcl/*


Major unit differences between Lazarus and Delphi

    • Please add to this topic! **
  • Windows->LCLIntf, LCLType, LCLProc, VCLGlobals, ...)

As the LCL is not windows specific, the code that is in the Delphi Windows unit for directly accessing the Win32 API is abstracted into seperate interfaces, which can be accessed from the LCLIntf unit. Keep in mind, that Lazarus does not emulate win32, so many functions are missing and some do not work as their win32 counterparts. These functions only exist for Delphi compatibility and should only be used for quick & dirty porting. LCL also breaks out many of the types, so often LCLType, and sometimes VCLGlobals are required. LCLProc also contains a few functions which can be usefull for lower level handling such as "FreeThenNil" as is in Delphi 5 and higher, "DeleteAmpersands" to remove additional ampersands from a string for controls(& vs && etc).

  • Messages->LMessages

TControl Messages for win32 event callbacks of the format WM_CALLBACK and the structs associated with them are often found in the Messages unit in Delphi. In the LCL these types of messages and there structs are usually found in LMessages, usually with name changes of WM to LM, so for instance WM_MOUSEENTER becomes LM_MOUSEENTER, and TWMMouse becomes TLMMouse.

  • Graphics, Controls->GraphTypes, GraphMath, Graphics, Controls

To simplify some things and break complexity of circles between units, a few types have been abstracted into a shared unit called GraphType, which includes things, which in Delphi are located in Graphics or Controls, for instance the bvNone etc of panels. So sometimes you have to include it. Also a unit which, although incompatible with Delphi, adds other usefull functionality is GraphMath, which adds a TFloatPoint for precision, misc routines for dealing with beziers, lines, and arcs, as well as some operator overloading for use with TPoints and TRect, such as for instance Point1 := Point2 + Point3, and comparing two rects like if (rect1 = rect2) then ...

  • Mask->MaskEdit

For more intelligent naming considerations, the unit for TMaskEdit is called [MaskEdit|] instead of the slightly more nebulous Mask as in many versions of Delphi.

  • StdCtrls->StdCtrls,Buttons

In many version of Delphi TButton is located in StdCtrls, while TSpeedButton and TBitBtn are in Buttons. For consistency and simplicity the LCL puts all button types in Buttons, which can occasionally break code conversion, so it is always a good idea to include.


Property and method differences Delphi -> FPC/LCL

  • TBitmap contains a canvas in the LCL

Syntax differences

Please add to this topic!

Because of the inherent strictness in FPC, some syntax changes are necessary, even though !!;dir {$Mode Delphi}!!;! does allow more lazyness like Delphi does. For this reason complying as much with the syntax rules of !!;dir {$Mode ObjFPC}!!;! as possible is highly recommended, even when the codebase is still going to be shared between Delphi and the LCL. Some of these are simply better coding practises, and sometimes because occasionally Delphi mode is not entirely accurate, or in a few instances Delphi acceptible code does not function as expected with FPC, even though it might compile. To that end even though not all such are strictly required, the following list of changes should be considered mandatory :

Variables initialization in FPC 1.0.x

With Delphi it is possible (and quite common) to initialize a variable in its declaration, however this is not possible in FPC 1.0.x, const must be used instead, or prefereably in many situations, initialized elsewhere at some point before the variable is used (like unit initialization).

For example -

Delphi FPC
var
MyObject: TSomeObject=
nil
;
//More code...
implementation
//More code...
initialization
//More code...
end
.
var
MyObject
:
TSomeObject;
//More code...
implementation
//More code...
initialization
MyObject:=
nil
;
//More code...
end
.
When assigning an event handling entry point, prefix it with an "@"

For instance, you might assign a button callback manually

Delphi FPC
begin
if not
Assigned(MyButton.OnClick)
then
MyButton.OnClick:= SomeFunction;
//@ not required
//more code...
end
;
begin
if not
Assigned(MyButton.OnClick)
then
MyButton.OnClick:= @SomeFunction;
//@ IS required
//more code...
end
;
When calling a procedure variable use this syntax
theprocname()

In Delphi there is no difference between a procedure and a procedure variable, however there is in FPC, so to call a procedure, even if it has no paramaters, you must append parenthesis. For Example -

Delphi FPC
With
(SomeObject)
do begin
If
Assigned(OnMyCallback)
then
OnMyCallback;
//parenthesis not required
end
;
With
(SomeObject)
do begin
If
Assigned(OnMyCallback)
then
OnMyCallback();
//parenthesis required
end
;
When accessing values in a pointer to a record you must dereference first

In Delphi it is not required to dereference a pointer to a record to acces values within it, it can in fact be treated just like the record itself, or any other object. In FPC it must be first dereferenced. As an example,

Delphi FPC
Function
GetSomeValue(ARecord: PMyRecord)
:
Integer;
begin
If
Assigned(ARecord)
then
Result
:=
ARecord.SomeValue
else
Result:=
0
;
end
;
Function
GetSomeValue(ARecord: PMyRecord)
:
Integer;
begin
If
Assigned(ARecord)
then
Result
:=
ARecord^.SomeValue
else
Result:=
0
;
end
;
When accessing chars of an indexed string Property of an object, it must be enclosed in parenthesis

With Delphi it is possible to treat a Property exactly like some other const or var, even to accessing for instance individual chars of a string directly, while this is not always possible in FPC, specifically for indexed properties. Instead it must be enclosed in parenthesis, to make distinct. While this may not always hold true it is probably a good practice to consider anyway. For example

Delphi FPC
Type
TSomeComponent=
class
(TComponent)
//More code...
Published
Property
MyString:
String
index
3
read
GetMyString;
//More code...
End
;
var
MyChar
:
char;
begin
If
Length(MyString)>
2
then
//no parenthesis needed
MyChar:= MyString[
3
];
//More code...
end
;
Type
TSomeComponent=
class
(TComponent)
//More code...
Published
Property
MyString:
String
index
3
read
GetMyString;
//More code...
End
;
var
MyChar
:
char;
begin
If
Length(MyString)>
2
then
//parenthesis sometimes needed
MyChar:= (MyString)[
3
];
//More code...
end
;


You must typecast pointers to actual type when using with var or function of that type

Sometimes in Delphi you will have a null pointer variable representing an object. While it might seem a complex situation, it is oddly quite common especially in large component packs as a method of preventing too many circular includes between objects in different units. In Delphi it is then possible to send this null pointer to a function expecting that object, without bothering to typecast to actual type, in fpc you must typecast.

For example -

Delphi FPC
Unit 1
Type
TSomeObject=
class
(TComponent)
//More code...
End
;
Procedure
DoSomething(Value: TSomeObject);
Function
GetSomeObject: TSomeObject;
Unit 2
Type
TSomeComponent=
class
(TComponent)
//More code...
Published
SomeObject: Pointer
;
//More code...
End
;
Application
var
MyComponent: TSomeComponent
;
begin
MyComponent.SomeObject
:=
GetSomeObject;
//More code...
DoSomething(MyComponent.SomeObject)
;
end
;
Unit 1
Type
TSomeObject=
class
(TComponent)
//More code...
End
;
Procedure
DoSomething(Value: TSomeObject);
Function
GetSomeObject: TSomeObject;
Unit 2
Type
TSomeComponent=
class
(TComponent)
//More code...
Published
SomeObject: Pointer
;
//More code...
End
;
Application
var
MyComponent: TSomeComponent
;
begin
MyComponent.SomeObject
:=
Pointer(GetSomeObject);
//More code...
DoSomething(TSomeObject(MyComponent.SomeObject))
;
end
;

Resources

Delphi resource files are win32 specific and not compatible with Lazarus, so you'll have to recreate and compile them using the lazres. Lazres can be found in the lazarus/tools subdirectory. If you've downloaded the Lazarus sources, you'll need to compile it first.

  • cd lazarus/tools
  • make install

To add a resource to your application:

  • lazres myresource.lrs mypix.xpm anotherpix.xpm
  • Add the LResources unit to your Uses clause
  • Include the .lrs file you created under the initialization block

Example:

function
TForm1.LoadGlyph(
const
GlyphName:
String
): TBitMap;
begin
Result:= TPixmap.Create
;
Result.LoadFromLazarusResource(GlyphName)
;
end
;
//More code...
begin
Speedbutton1.glyph:= LoadGlyph('mypix')
;
//More code...
end
;
initialization
{$I unit1.lrs}
{$I myresource.lrs}
end
.

Getting Help

If you encounter a problem during conversion that you just can't solve, there are a wide variety of places to get help. For pure Object Pascal and FPC issues, the best place to start is the Free Pascal Documentation by Michaël Van Canneyt and Florian Klämpfl. For more Lazarus oriented problems, the Lazarus Project Documentation in the Lazarus-CCR Knowledgebase [Main Page] is the next place to look. Finally you can post a question on any of the mailing lists for the Free Pascal Compiler or the FPC forums where a lot of experts are subscribed.

There are some outstanding search and knowledge bases online that can also be a great help for learning new techniques and solving problems. Tamarack Associates operates a fast search engine specifically for the Borland usenet archives. Mer Systems Inc. provides a similar search engine. Another outstanding source of information along with a sitewide search capability is Earl F. Glynn's Computer Lab and Reference Library.

Packaging and Releasing your component

Creating a Lazarus package for your component(s)

Creating a package makes installing the code you've converted a much easier process... especially if you're providing more then one component. Mattias Gärtner has written an overview of Lazarus Packages that should be read before beginning this process.

Documentation

The purpose of this site and the wiki format is to make the generation of professional documentation an easy and quick process. The wiki also makes it possible to see the results of your posting immediately and make any changes you'd like in real time.

Using the Lazarus-CCR wiki to create nice looking documentation is very easy. If you've never used wiki markup before, you can get familiar with it in the Sand Box practice area.

Creating a Code Release Page

The Code Release Page contains vital information about your component that a potential downloader will need to know, such as license, intended platform, status (alpha, beta, stable...), where to download it, who wrote it, is support available... etc.

The following procedure will let you create a Code Release Page with your browser:

  • Edit the Code And Components page and add a project name wiki link entry for your component in the "Released Components" section. Save the modified page.
  • Go to the Component Release Template, select all and copy. Hit the back button on your browser to return to the Code And Components page.
  • Click on your new wiki component name entry and paste the release template into the blank edit box.
  • Edit the template accordingly and hit save.
  • Do edit-saves until your document looks the way you want it to.

Creating a Comment Page for your component

When your component is posted to the Sourceforge File Release System, the Lazarus-CCR admins will lock your code release page to prevent the unlikely event of someone changing your license, name or other release info.

While you're building your code release page, you have the option to create another wiki link to a "news and comments" page for your component that will always be writable. This is called a ComponentCommentTemplate. The link to it should be added to the bottom of the code release template where there's a topic heading and a brief howto. The wiki name link should be your wiki component name with a Comments suffix like EpikTimer Comments or JpegLazarus Comments . The comments page will remain writable to collect feedback from users and for you to post updated information.

A comment page can be added like this:

  • Edit your component release page and add the wiki comment link (in the format described above). Save the modified page.
  • Go to the Component Comment Template , select all and copy. Hit the back button on your browser to return to your component release page.
  • Click on the comment entry entry you created and paste the comment template into the edit box.
  • Edit the template accordingly and hit save.
  • Do edit-saves until your comment page looks the way you want it to.

While the Lazarus-CCR admins initially lock code release pages, any member of the project can use their shell acount to unlock, edit and re-lock their pages.

Submitting the component

If you're a release technician on the project, upload your component to the SourceForge File Release System and add it to the list of release packages. Otherwise send it to one of the project administrators and we'll add it to the repository. We'll also put it into CVS so you'll continue to have access to it.

Contributors and Changes

This page has been converted from the epikwiki version.

  • Initial version by Tom Lisjac and Mattias Gärtner - 9/22/2003 VlxAdmin
  • Moved Getting help from the main page. T. Lisjac - 9/24/2003 VlxAdmin
  • Added documentation templates, procedure and links. 9/25/2003 VlxAdmin
  • LCLLinux was renamed to LCLIntf, Jesus Reyes, 9/27/2003
  • added more information on Unit changes, AndrewJohnson 9/27/2003
  • Updated Syntax differences, including some examples, AndrewJohnson 9/27/2003
  • FPC 1.0.x doesn't support interfaces, Vincent Snijders 9/28/2003
  • Fixed some of the examples per new WikiWord definition, 9/28/2003 VlxAdmin
  • Made code more consistant to remove last accidental Pascal WikiWord definitions, AndrewJohnson 9/27/2003
  • Use tables for code examples for nice blocks, and easy side by side view of Delphi->FPC differences, AndrewJohnson 10/17/2003
  • Use pascal stylesheet to make example code more readable, AndrewJohnson 10/18/2003