Forum > Packages and Libraries

Conditional compilation of units in the Lazarus package

(1/2) > >>

I'm creating a package that I want to use with Raspberry Pi and Up Squared (and maybe others in the future). The package is to contain a set of library files related to GPIO, PWM, I2C, SPI support. The classes included in the package are non-visual, they will not be installed in Lazarus (each one comes from TObject). The library is to be used with the operating systems: Linux and Windows. In the case of the Raspberry Pi, it would only be Linux. For Up Squared, however, it would be Linux and Windows. In the case of Windows, the library of Aaeon company must be installed in the system, which allows access to the Up Squared board.

Some of the units of the package would always be used. Another would be used depending on the operating system and the board selected. The compiler would be informed about the type of the selected board by specifying symbols in the project's custom options (I set it like this: main menu: Project → Project Options…, in dialog window: Compiler Options → Custom Options, button: Defines ...). This of course allows you to conveniently use conditional compilation commands ($IFDEF and $ENDIF). In common modules of the package, I can easily use these symbols in the "uses" sections and also to denote fragments of OS-specific modules or a board. An example of the package contents (modules) is as follows:

IoT.pas <- package file
IoT.lpk <- package file
IoT.Common.Classes <- common file for all systems and boards
IoT.Common.Types <- common file for all systems and boards
IoT.Common.Constants <- common file for all systems and boards
IoT.Common.Exceptions <- common file for all systems and boards
IoT.Linux.DeviceGPIO <- Linux-specific file
IoT.Linux.DevicePWM <- Linux-specific file
IoT.Linux.DeviceI2C <- Linux-specific file
IoT.Linux.DeviceSPI <- Linux-specific file
IoT.Windows.DeviceGPIO <- Windows-specific file
IoT.Windows.DevicePWM <- Windows-specific file
IoT.Windows.DeviceI2C <- Windows-specific file
IoT.Windows.DeviceSPI <- Windows-specific file
IoT.Platform.RaspberrPi <- Raspberry Pi board specific file
IoT.Platform.UpSquared <- Up Squared board specific file

The problem arises when the module is OS or board specific. For example, when compiling under Windows, Linux modules are not used. In the "uses" sections of these modules there are references to Linux-specific FCL modules (eg "BaseUnix"). All PAS modules of the library I create are placed on the package list (section: "Files" visible in the package window). This causes the compiler to be unable to compile the package files. Sample compiler message:

"Iot.Linux.DeviceI2C.pas(222,3) Fatal: Cannot find BaseUnix used by Iot.Linux.DeviceI2C of package Iot."

I tried to add conditional compilation symbols to the LPK file, but after each compilation attempt, this file is overwritten with new content (symbols are removed by the IDE).

I can see the following way out of this situation: placing the code of specific modules (ie: OS, board) in the INC files. However, this is quite a bothersome solution.

There is another option - create a library as a set of source files (not package). When a new project using this library is created, the programmer in the project options will have to add the path to the directory of this library (i.e. main menu: Project → Project Options ..., in dialog window: Compiler Options → Paths, text edit: Other Unit Files). However, then you will not be able to use the package manager. You have to set everything up in the IDE yourself.

So my question is: is it possible to conditional compile entire files in the Lazarus package?

Well it should not matter if they get compiled.
As long, as any target specific code in each unit is properly IFDEFed.
=> That is, you could IFDEF the entire unit, so it is empty (except for unit name; end;)

But if you want to exclude them. (not tested / from memory)

- In the package Window select each such unit.
- Uncheck the box "use unit" for each of them
Now they should no longer be in the package.pas file

However that means no PPU are created for them at all. So they will never be compiled, even if you want them too.

Create your own new unit package_myuses.pas. Make sure this has "use unit" checked, so it is compiled when the package is compiled.
In this file, you can write your own "uses" clause, with IFDEF, so the correct files are compiled.


Units in your package are not compiled when they get used in the project.

They must be compiled, when the IDE compiles the package (which it will do once for your project, if anything affecting the package changed).
At that time ppu are created for all units.
Only those ppu are available, when your project (or other packages, using this package) are compiled.

So you must ensure all units that you may need are compiled into ppu when the package compiles.

The package does not normally see settings for your project (such as defines (-dENABLE_FEATURE) for you project.
So you can not relay on that.
But it will see defines that are globally set, like the architecture.

You can also use "Additions and overwrites" in the project settings, to force defines to the package (useful for buildmodes).

Thanks for the clarification. Inspired by your advice, I acted a bit differently. I have selected the platform specific files in the package window. Then I displayed their context menu (popup menu). Of course, you had to do it for each file separately, because you can't select several files at once. From this menu I selected the "File type" command, a sub-menu was displayed in which I changed the setting of the selected file to "Include file". After changing the settings for the selected files, the package compiles.

I'm usually inquisitive and generally always check the presence of different options. However, in this case it not occurred to me that each item (file) in the package window has a context menu. Perhaps because never before I developed the package in Lazarus (from scratch). In Delphi I create packages classes (including components and controls) for many years. Lazarus, however, is slightly different from Delphi.


--- Quote from: VisualLab on May 16, 2021, 03:47:08 pm ---The problem arises when the module is OS or board specific.
--- End quote ---
You should take a look at PXL for inspiration:

Yes, the library PXL is known to me. Immediately after buying a Raspberry Pi board in May 2017 I started looking for libraries. To make use of Raspberry Pi, I also gradually bought: HAT boards (Waveshare), mini-modules (Grove - Seedstudio, Gravity - DFRobot) and various trinkets (stepper motors, servos, photovoltaic cells, etc.). A little of this accumulated over the last 4 years.

First of all, I started looking for libraries that could be used in Lazarus. First I looked at the Lazarus on Raspberry Pi, in particular the External Links section. From there I found my way to the PXL library website. I also looked at other libraries written in Object Pascal. But right now, PXL has perhaps the greatest capabilities (plus carefully written code and some pretty good documentation).

For inspiration, I also searched for libraries created in other programming languages, for example:

- Wiring Pi,
- libgpiod,
- The Pi4J Project,
- Rpi.GPIO.

Unfortunately, the Wiring Pi library was of no use to me. It can be useful for people who need minimalism. But creating more complex C applications is a "painful journey." Moving this library to Object Pascal or creating wrappers seems to me pointless (it uses, among others, the sysfs GPIO interface and specific references to Broadcom SoC).

I looked at the libgpiod library just to get to know GPIO support using the chardev character interface in Linux. I looked at the MRAA library files only cursorily, to look at I2C and SPI support in Linux. I have to find something about support for the 1-wire bus in Linux. I found something in Linux source files, but it's probably for kernel use: w1.h).

The Pi4J library is quite confusing. Using it resembles dealing with some complicated matters in state offices. The following description comes to my mind: to get a specific object, call the factory, then ask it to return a specific builder to you, when you get it, ask him to build a specific object according to a set of guidelines, the builder will create this object on their basis and will hand it over to you - this is bureaucracy. Well, Java is known for that.

I haven't looked at Rpi.GPIO yet (I haven't had enough patience with Python).

What I need (or what I'm trying to strive for) are two groups of classes:

1. base classes that support GPIO, PWM and serial buses: I2C, SPI, 1-wire,
2. target classes that includes a handling code of specific integrated circuits (e.g.: MCP23017, PCA9698, MAX14830, etc.).

To achieve the first goal, I am trying to use operating system interfaces (Linux for now). In this way, I plan to extend this library to support other SBCs (Up Squared, etc.) without changing the rest of the classes (i.e. target ones). For the second goal, I want to create a set using the details from datasheets provided by chip manufacturers (chip address, register numbers in the chip, etc.) Of course, this is easy to write on the forum ("speech is cheap"), but it takes time to implement. Also, if the library had to be easily extensible, it requires some thought, so the internal structure of the base classes will probably change a few more times (mostly changes inside the main base classes).

The difficulty at this time is in identifying SBC and Soc. I need this to determine the details of the GPIO connector (how many pins, what signals are available, what modes the GPIO line works, etc.). I put the details in arrays of constants (lookup tables). Probably almost all libraries have uses this approach.

In the case of SBC based on Intel processors (e.g. Up Squared) under Linux it is not too bad (Linux plug-and-play). There is a bigger problem with ARM-based SBCs. It turned out that there is no plug-and-play mechanism on the ARM platforms (which I didn't know before). Information about the hardware and its configuration depends on the presence of the DTB (device tree) file loaded at system startup. If some Linux distribution does not contain this information, there may be a hardware identification problem. Since this information may not be in the system, of course you can tell the programmer to add conditional compilation symbols to the project options. But this slightly reduces the convenience of creating a program. So far, I don't see any other option.


[0] Message Index

[#] Next page

Go to full version