Recent

Author Topic: Conditional compilation of units in the Lazarus package  (Read 3815 times)

VisualLab

  • Sr. Member
  • ****
  • Posts: 290
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?

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 9791
  • Debugger - SynEdit - and more
    • wiki
Re: Conditional compilation of units in the Lazarus package
« Reply #1 on: May 16, 2021, 04:03:52 pm »
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.


Background.

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).
« Last Edit: May 16, 2021, 04:07:21 pm by Martin_fr »

VisualLab

  • Sr. Member
  • ****
  • Posts: 290
Re: Conditional compilation of units in the Lazarus package
« Reply #2 on: May 18, 2021, 10:48:35 pm »
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.

avra

  • Hero Member
  • *****
  • Posts: 2514
    • Additional info
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

VisualLab

  • Sr. Member
  • ****
  • Posts: 290
Re: Conditional compilation of units in the Lazarus package
« Reply #4 on: May 21, 2021, 06:42:33 pm »
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,
- MRAA,
- 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.

DonAlfredo

  • Hero Member
  • *****
  • Posts: 1739
Re: Conditional compilation of units in the Lazarus package
« Reply #5 on: May 22, 2021, 08:42:54 am »
You might want to consider FreeRTOS. It is supported by FPC trunk.
Library needed: https://github.com/michael-ring/mbf-freertos
(based on PXL)
I am now using FPC+FreeRTOS and the above library for production.
Systems:
https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html
https://www.seeedstudio.com/Wio-Terminal-p-4509.html
Both systems support uf2. And FPC trunk can (will) output uf2 !!

avra

  • Hero Member
  • *****
  • Posts: 2514
    • Additional info
Re: Conditional compilation of units in the Lazarus package
« Reply #6 on: May 22, 2021, 10:28:29 am »
I think forking PXL is the way to go (it seams that author has moved his focus elsewhere).

1. base classes that support GPIO, PWM and serial buses: I2C, SPI, 1-wire,
With PXL you would only need to implement 1-wire.

2. target classes that includes a handling code of specific integrated circuits (e.g.: MCP23017, PCA9698, MAX14830, etc.).
MCP23017 and PCA9698 could derive from PXL TCustomGPIO, and MAX14830 from TCustomPortUART. For some specific sensors use TCustomSensor as a starting base. These PXL.Sensors are already implemented: BMP180, DHT22, L3GD20, LSM303, SHT10. Code has good architecture, is quite readable, and waiting for your adaptation.  ;)

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.
I know. You could take a look at how Balena Blocks implement sensors plug and play using Linux Industrial I/O Subsystem:
https://www.balena.io/blog/balenablocks-in-depth-sensor-and-pulse/
https://wiki.analog.com/software/linux/docs/iio/iio

I would not bother with SBC/OS detection. I would leave that as a compile time decision.
« Last Edit: May 22, 2021, 10:58:11 am by avra »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

VisualLab

  • Sr. Member
  • ****
  • Posts: 290
Re: Conditional compilation of units in the Lazarus package
« Reply #7 on: May 23, 2021, 01:00:57 am »
I think forking PXL is the way to go (it seams that author has moved his focus elsewhere).

With PXL you would only need to implement 1-wire.

I agree on those Pascal modules, that include the translation of the C headers with the Linux code, that handles the I2C and SPI buses. I have already done it - by verifying the relevant files from the website:

https://elixir.bootlin.com/linux/latest/source/include/uapi/linux

Object-oriented implementation of the classes supporting I2C and SPI buses seems to be an easier job than translating C headers into Pascal modules.

MCP23017 and PCA9698 could derive from PXL TCustomGPIO, and MAX14830 from TCustomPortUART. For some specific sensors use TCustomSensor as a starting base. These PXL.Sensors are already implemented: BMP180, DHT22, L3GD20, LSM303, SHT10. Code has good architecture, is quite readable, and waiting for your adaptation.  ;)

There seems to be a trap here. These integrated circuits communicate via the I2C or SPI bus, depending on their variant. For example, the MCP23017 communicates over I2C, but its MCP23S17 variant communicates over SPI. The MAX14830, on the other hand, supports both buses, which is set using specific pins of the chip. Only PCA9698 communicates via I2C (like almost all Philips/NXP chips). The trap is that MAX14830 is a UART, so I have to check how Linux "sees" it - whether as a typical UART or as an I2C/SPI chip.

As for the handling of GPIO pins working as binary IN/OUT ports, something else has to be used. In the PXL library, GPIO lines are handled by the sysfs interface. This is an older solution. Ultimately, it is to be removed in the future (but when?). I found information about this from the GPIO maintainer in Linux:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=a0910d72afc69b25703f7be9bf7d13f18937a478

I want to use the chardev interface:

https://elinux.org/images/c/cb/Linux_GPIO-Evolution_and_Current_State_of_the_User_API.pdf

But I'm not going to do libgpiod wrapper library. I want to get direct access to the interface: /dev/gpiochip.

I know. You could take a look at how Balena Blocks implement sensors plug and play using Linux Industrial I/O Subsystem:
https://www.balena.io/blog/balenablocks-in-depth-sensor-and-pulse/
https://wiki.analog.com/software/linux/docs/iio/iio

I have looked briefly for now. I'll take a closer look tomorrow. It seems interesting.

I would not bother with SBC/OS detection. I would leave that as a compile time decision.

You're right. In the end, easier to read the description directly from the board (or its manufacturer's Web site). When someone will create a program that will be using this library, in the project options will need to add conditional compilation symbols defined in the library. Alternatively, a list of predefined values could be prepared (as an enumeration type) representing specific types of boards (e.g. cpRaspberryPi1, cpRaspberryPi2, cpUpSquare, cpNanoPi, cpAsusTinker, etc.). Such a value could be passed to the class constructor which would hold the minimum necessary board configuration in the project.

 

TinyPortal © 2005-2018