Lazarus Packages

From Free Pascal wiki
Jump to navigationJump to search

Deutsch (de) English (en) español (es) français (fr) 日本語 (ja) português (pt) русский (ru) slovenčina (sk)

Overview of the Lazarus Package System

For other meanings of Package in the combined FPC/Lazarus projects see Packages(disambiguation).

What is a lazarus package?

A lazarus package is a collection of units and components, containing information how they can be compiled and how they can be used by projects or other packages or the IDE. In contrast to Delphi, packages are not limited to libraries and they can be OS independent. (Library Packages are specially compiled libraries used by applications, the IDE or both. Delphi/Library packages require in-compiler support, which FPC is not capable of at the moment and of course this magic is OS dependent.)

Currently the Free Pascal compiler only supports static packages, not dynamic packages. Therefore you must compile and restart the IDE, each time a package is installed or uninstalled.

A lazarus package is identified/distinguished by the name and its version.

Packages are ideal for sharing code between projects.

Coming from Delphi

If you are familiar with Delphi packages then read the following paragraph, because there are some important differences between a Delphi and a Lazarus package.

  • Lazarus packages are not only a dynamic library. They are used to share units between projects and modularize big projects.
  • Sometimes the Delphi main file of a package is called "Package Project". This term makes no sense under Lazarus.
  • Many Delphi packages provide one "package project" per compiler version. This is hardly needed under Lazarus. But you can do that for Delphi compatibility. Lazarus packages have version information and projects can define that they require at least or at most a specific version of a package. Lazarus will automatically load the right version.
  • If a unit can not be found, under Delphi you are used to add the directory to the global search path. This has many drawbacks when sharing code. That's why this is not supported by Lazarus. Use packages instead.

FAQ

Q: Do I need to install a package?
A: You only need to install a package, if it contains designtime items, like components for the IDE component palette. If you don't use those items, you don't need to install the package. If you only want to use a package in your project, don't install it. Use the project inspector and add a requirement.

Q: I installed a package, but the IDE does not find the units
A: Installing a package means, the package is integrated into the IDE, not your project. These are separate things. To use a package in your project, use Project -> Project Inspector -> Add -> New Requirement. And uninstall the package, if it does not contain any IDE goodies.

Quick Start

To see the packagesystem in action and to get used to it, do the following:

Creating a new package

  • Package->New Package or File->New... -> Package -> Standard Package
  • A package editor opens

Package Maker

  • Use the Save button at top left.
  • Depending on your 'naming' setting in the 'environment options', the IDE will ask you to save the file lowercase. Say yes.

Congratulations: You have just created your first package!

Adding a new component

  • Use the Add button -> New component
  • Choose a component in the ancestor type combobox. For instance: TBevel.
  • Click Ok
  • The file will be added to the package and opened in the editor
  • Install the package by clicking the 'install' button in the top of the package editor.
  • Lazarus will save the package and ask you, if the IDE should be rebuilt. Say yes.
  • The packages are statically linked, so a restart of the IDE is needed.
  • Restart Lazarus and see your new component in the component palette (For example: A TBevel1 will be on the 'Additional' page).
  • If you do not see your new component in the component palette, it is most likely that you are not running the re-compiled version of Lazarus. You can set where Lazarus builds to in: Environment -> Environment options -> Files -> Lazarus directory. Instead of calling lazarus directly, you also can use startlazarus, which starts the newly created lazarus, for example the lazarus executable in the ~/.lazarus directory, if you don't have write access to the directory lazarus was installed into.

Congratulations: You have just installed your first package with your first package component.

Add a component icon

The idea is to create a png, convert that into a lrs include file using the lazres tool and include that. Here are the details:

  • Size of PNG file should be 24x24
  • Name of png must be the class name of the new component, case does not matter (e.g. tmyComponent.png for TMyComponent).

To create the lrs file run (where TMyComponent is the class name of your component and yourunit is the unit name):

~/lazarus/tools/lazres yourunit.lrs TMyComponent.png
  • You may need to compile lazres at first use. Simply open the lazres.lpi in the IDE and at the menu click run > build.

Hint: You can add more than one image to the lrs file by appending the image filename at the end. For example

~/lazarus/tools/lazres yourunit.lrs TMyComponent.png TMyOtherCom.png ...

Following is a sample of the resulting yourunit.lrs file.

LazarusResources.Add('TMyComponent','PNG',[
  #137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#0#24#0#0#0#24#8#2#0#0#0'o'#21#170#175
  +#0#0#0#4'gAMA'#0#0#177#143#11#252'a'#5#0#0#0'|IDAT8O'#237#212#209#10#192' '#8
  +#5'P'#247#231#251's'#215#138#133#164#166'\'#220#195'`'#209'c'#157'L'#173#131
  +#153#169'd4'#168'dP'#137'r_'#235'5'#136'@Zmk'#16'd9'#144#176#232#164'1'#247
  +'I'#8#160'IL'#206'C'#179#144#12#199#140'.'#134#244#141'~'#168#247#209'S~;'#29
  +'V+'#196#201'^'#10#15#150'?'#255#18#227#206'NZ>42'#181#159#226#144#15'@'#201
  +#148#168'e'#224'7f<@4'#130'u_YD'#23#213#131#134'Q]'#158#188#135#0#0#0#0'IEND'
  +#174'B`'#130
]);
  • Click the Add button again, goto the Add File tab and browse to the yourunit.lrs file and click Ok. This is needed, so that the IDE checks this file for changes and will recompile your package.
  • Add an include directive to yourunit:

<Delphi> unit YourUnit; ... implementation ... initialization

 {$I yourunit.lrs}

end. </Delphi>

Note: You can include more than one lrs file, but each only once.

  • That's all. Compile or install your package

Creating a package for your common units

Let's say you have some units, that you want to use in your projects. Put the units in a separate directory, for instance C:\MySharedUnits.

  • Package->New Package or File->New... -> Package -> Standard Package
  • A package editor opens
  • Use the Save button at top left. Choose C:\MySharedUnits\MySharedUnits.lpk. The package name must not conflict with any existing unit.
  • Depending on your 'naming' setting in the 'environment options', the IDE will ask you to save the file lowercase. Say yes.
  • Now you have a package with the name MySharedUnits stored as C:\MySharedUnits\mysharedunits.lpk.
  • Adding the units: Add->Add Files->Add directory.
  • A dialog will appear with the directory C:\MySharedUnits and some include and exclude filters. Click Ok to add all units.
  • The units will now be listed. Click Add files to package.
  • The files are now added to the package. Click Save.
  • If your units only require the standard FPC units you can now simply press Compile. Otherwise see below.

Adding the LCL as dependency

If your package contains forms or any other LCL unit, then it requires the LCL package.

  • In the package editor of your package click Add -> New Requirement
  • Choose as package name LCL
  • Click Ok.
  • The LCL is now added under Required Packages. Click Save.

You can see the what the LCL does, by clicking in the package editor of your package Compiler Options / Inherited.

Use your package in your project

You need to add a dependency from the project to your new package. There are several ways to do this:

  • Open your package. For example via Package / Open recent package. On the package editor of your package click More ... / Add to project.
  • Or you can use the project inspector:
    • Project / Project Inspector.
    • Click on the Add button with the plus.
    • Select the page New Requirement
    • Choose as package name your package
    • Click Ok

The IDE menu items for packages:

  • Package -> New Package or File->New... -> Package -> Standard Package
    • Creates a new package.
  • Project -> Project Inspector
    • Here you can see, what packages are required by the currently open project.
    • You can add new dependencies and remove unneeded ones.
  • Run -> Compiler options -> Inherited
    • Here you can see what compiler options are inherited from which package.
  • Components
    • 'Open package': A dialog shows all open packages with their state.
    • 'Open package file': Open a .lpk file
    • 'Open package of current unit': Open the .lpk file, that belongs to the file in the source editor
    • 'Open recent package': Open a recently open package file (lpk file)
    • 'Add active unit to a package': Adds the unit in the source editor to a package
    • 'Package Graph': The package graph shows all open packages and their dependencies.
    • 'Configure installed packages': Edit the list of packages installed in the IDE. Install or uninstall several packages at once.
  • Project -> Project Inspector
    • Here you can see and edit all packages used by the project.

Removing Packages

  • To remove installed components, on the IDE menu, click Package > Configure installed packages. The following image shows the Installed Packages tool.

Installed Components

  • Select the package you want to uninstall and click Uninstall selection.

If something goes wrong with a package (for instance a package directory is deleted without first uninstalling it), Lazarus may not allow you to uninstall packages. To fix the problem, at the IDE menu click Tools > Build Lazarus. Lazarus will rebuild all packages and restart. You should now be able to uninstall problematic packages.

The theory

Each Lazarus package has a .lpk file. A package is identified by its name and its version. The name must correspond to the lpk filename. For example:

Name: Package1, Version: 1.0, Filename: /home/.../package1.lpk.

  • The IDE automatically creates the main source file (package1.pas). See below. The lpk file contains information about the required packages, the files it uses, how to compile them, and what is needed to use the package by other packages/projects. The directory where the lpk file is, is called the "package directory".
  • The IDE maintains a list of all package files (<config directory>/packagelinks.xml). Everytime a package is opened in the IDE it will be added to this list. When a package is opened, the IDE automatically opens all required packages via this list.
  • There are three base packages: FCL, LCL and SynEdit. These are parts of the IDE and so they are autocreated, readonly and have no lpk file.
  • Normally a package has a source directory with some pascal units. And normally the lpk file will be there too. A package has also an output directory. Default is the subdirectory 'lib/$(TargetCPU)-$(TargetOS)/' in the package directory.
  • Before a package is compiled the IDE checks all required packages and if they need update and have the auto update flag, they are compiled first. Then the IDE creates the package main source file. If the lpk file was package1.lpk, then the main source file is package1.pas. This file contains all units in the uses section plus a 'Register' procedure, which is called in the intialization section.

For example:

<Delphi> { This file was automatically created by Lazarus. Do not edit!

  This source is only used to compile and install
  the package GTKOpenGL 1.0.

} unit GTKOpenGL;

interface

uses GTKGLArea, GTKGLArea_Int, NVGL, NVGLX, LazarusPackageIntf;

implementation

procedure Register; begin

 RegisterUnit('GTKGLArea', @GTKGLArea.Register);

end;

initialization

 RegisterPackage('GTKOpenGL', @Register);

end. </Delphi>

  • Then the compiler is called and the package is compiled to the output directory.
  • After successful compilation the state file is created. The state file is put into the output directory. It has the name <packagename>.compiled and contains the information, how the package was compiled. This state file is used by the IDE to check if update is needed.

For example: gtkopengl.compiled:

<?xml version="1.0"?>
<CONFIG>
  <Compiler Value="/usr/bin/ppc386" Date="781388725"/>
  <Params Value=" -Rintel -S2cgi -CD -Ch8000000 -OG1p1
    -Tlinux -gl -vewnhi -l -Fu../../../lcl/units
    -Fu../../../lcl/units/gtk -Fu../../../packager/units
    -Fu. -FElib/ gtkopengl.pas"/>
</CONFIG>
  • The IDE opens all needed packages automatically. This means, it opens all installed packages, all packages marked for installation (auto install), all packages with an open Editor, all packages required by the project and all packages required by one of the other packages. Unneeded packages are automatically unloaded, when the IDE becomes idle.
  • The IDE never opens two packages with the same name at the same time. When the user opens another package file with the same name the IDE will ask if it should replace the old one.
  • The IDE maintains two extra sets of packages: The 'installed' packages and the 'auto install' packages. The auto install packages will be linked into the IDE on next compile. It creates two new files in the config directory: staticpackages.inc and idemake.cfg. Then it calls 'make ide OPT=@/path/to/your/config/idemake.cfg' to compile itself.

Hints and Tips

Please add any hints, tips or gotchas here.

  • To rename a package, use 'save as'.
  • You can add to every dependency the default filename. For instance you provide an example project for your package. When another programmer opens your example for the first time, the IDE does not know where the lpk file is. Simply right click on the dependency in the project inspector and 'Store file name as default for this dependency'. The file name is stored relative to the project. You can do the same for package dependencies.

Adding existing components to a Package

If you want to add a file that contains a component to an existing package, you can do the following:

  • Open the package file
  • Click on the 'Add' file to add a new item to the package.
  • Select the unit tab in the 'Add to package' dialog.
  • Choose the file to add.
  • Check the 'Has register procedure' if the unit contains a Register procedure. If you do not do this, the component(s) will not be shown on the component palette.
  • Click 'Add unit'.
  • Recompile and install the package.

That's it. The component should now show on the component palette.

Create a package with a unit that has the same name as the package

Normally the IDE auto creates the registration code into a unit with the same name as the package. If you want to name your package the same as an existing unit, for example because the package has only one unit, then you need at least Lazarus 0.9.29 and use this:

  • Make a backup of your unit mypackage.pas.
  • Create the package mypackage.lpk normally and do not add the unit yet.
  • Add a new unit, for example mypackageall.pas.
  • Select the unit mypackageall.pas in the package editor and right click for the popup menu.
  • Change File type to Main Unit.
  • Now restore your unit mypackage.pas and add it to the package.

The Register procedure

To show a component on the component palette, it must be registered in Lazarus. This is done in the 'Register' procedure. This is a procedure that must appear in the interface section of the unit it is in, and must issue one or more RegisterComponent calls, as well as install property and component editors for the components.

One or more Register procedures can be present in a package: in the package editor, you must indicate the units that have a 'register' procedure, so Lazarus knows which procedures it must call when the package is installed.

There are 2 ways to mark a unit as having a register procedure:

  • You can do this while adding the units to the package (see the 'Adding existing components to a package' section),
  • or you can select the unit in the package dialog, and set the 'Register' checkbox in the details panel.

Do not forget to recompile and install the package after changing the properties.

Search paths

All search paths are stored relative to the directory of the .lpk file. If you use an absolute path you have either not understood packages or you don't care.

Each package has two types of search paths:

  • Paths in the Compiler Options are used to compile the package itself.
  • Paths in the Options / Usage are used by packages and projects that require this package. In other words these paths are added to the search paths of the project (or requiring package).

Design Time vs Run Time package

  • A package can be a design time package, run time package or both.
  • A design time package can be installed in the IDE and provides IDE extensions like new components, new file or project types, new menu items, new component or property editors, etc.
  • A run time package can be used by projects.
  • A run time only package should not be installed in the IDE, for example because it is incompatible with the LCL. The IDE will warn if a developer tries to install it.
  • A design time only package should not be used by projects. For example when it only contains IDE extensions. The IDE will warn if a developer to tries to add it.
  • These restrictions are not hard. For example to test a design time package you may want to use it in a test project.

Typical scenario for a run time and design time package

You have a component library called Awesome and you want to write some IDE editors for some special properties. For example a property editor for the property Datafile which shows a TOpenDialog to let the user select a file. You can put the components into one run time package named AwesomeLaz.lpk' and create another directory design for the design time package AwesomeIDELaz.lpk. Move all units containing the IDE editors and registering stuff into the design directory. Add a dependency in AwesomeIDELaz to AwesomeLaz. Make sure they do not share units or search paths. For an example see components/turbopower_ipro/: The run time package is turbopoweripro.lpk and the design time package is design/turbopoweriprodsgn.lpk.

For Delphians: I can not install a run time package, why not?

Installing a package means compiling the whole code into the IDE. A run time package does not contain code for the IDE or maybe even bites it. In order to use a package in your project, simply open the package, right click on the package editor for the popup menu and click Add to project. Alternatively you can use View / Project Inspector. The project inspector shows what packages are used by your project, you can add dependencies or delete them.

Examples

Example

I will use the tiOPF framework as an example The tiOPF has the following directory layout, due to the fact that it compiles for FPC, Delphi 5-7, D2005 and D2006.

Source                <= full path \Programming\3rdParty\tiOPF\Source
 \Compilers
    \Delphi7          <= Delphi 7 package files live here
    \D2005
    \FPC              <= the tiOPF.lpk lived here
 \Core                <= core unit files
 \Options             <= optional unit file for extra features
 \GUI

Using this example, I included in the "Options - Usage - Units" editbox the following paths:

"$(PkgOutDir);..\..\Core;..\..\Options;..\..\GUI" 

which will be added to whatever project uses this package.

Different versions of a package

You can specify the minimum, maximum version required. When the project is opened and there are multiple .lpk files the IDE will automatically open the one with the right version.

If you work with different versions of a package, you can open at any time the other package file. The old package will be closed and the new one will be opened. Registered items will be transfered to the new package.

If you work with different projects that require different versions of a package (for example with different branches of the same package), you can define the preferred one in the dependencies. In the project inspector right click on the dependency and click on Set file name as preferred for this dependency. When the project is opened, the preferred package will be opened. If there is already a package with the same name it will be replaced. This feature exists since 0.9.29.

Links

How To Write Lazarus Component - a tutorial about creating a new component.