Dynamische Paketten

Dynamische pakketten stellen gebruikers in staat om in enkele seconden componenten, controls en Lazarus-extensies te maken en te installeren, zonder dat de gehele IDE opnieuw gebouwd hoeft te worden. Ze kunnen ook in eigen programma's worden gebruikt om plugins te ontwikkelen en te implementeren die native Pascal-klassen en -typen ontsluiten via dynamische pakketbibliotheken. Om deze functionaliteit te ondersteunen, heb ik talloze verbeteringen aangebracht in de compiler, de runtime en de Lazarus IDE. Daarnaast heb ik extra IDE-extensies gemaakt om de ervaring bij het werken met Lazarus-pakketten te verbeteren.

Implementatie en werking

Alle installatieprogramma's bevatten ondersteuning voor dynamische pakketten. Deze zijn geïmplementeerd als standaard shared library-bestanden voor Linux, Windows en macOS. Ze bevatten aanvullende metadata die door de compiler is gegenereerd en die kan worden geïnspecteerd en opgevraagd met behulp van nieuwe RTL-units. De runtime bevat ook mechanismen voor het veilig behouden van thread-variabelen en het beheren van dynamische typen, zoals strings en dynamische arrays, tussen programma's en pakketten of tussen meerdere pakketten.

Video: Voorbeeld van Dynamische Paketten

Bij het implementeren van uw programma kunt u nog steeds alles bundelen in één enkel uitvoerbaar bestand. Als alternatief kunt u het verdelen in meerdere dynamische pakketten die kunnen worden vervangen terwijl uw programma draait, wat een plugin-systeem mogelijk maakt.

Beperkingen

Dynamische pakketten kennen enkele beperkingen. Ten eerste mag een unit in een pakket niet dezelfde naam hebben als het pakket zelf. Ik heb het standaard pakketsysteem verbeterd, zodat het gegenereerde Pascal-bronbestand automatisch het woord Package aan de pakketnaam toevoegt.

Voorbeeld: Als u een pakket maakt genaamd ElectroTools.lpk, dan wordt het bronbestand waarin alle units van het pakket worden vermeld ElectroToolsPackage.pas genoemd in plaats van ElectroTools.pas.

Een andere beperking is dat unit-namen uniek moeten zijn over verschillende pakketten heen. Als iemand anders een unit heeft geschreven met de naam Widgets, kunt u geen ander pakket installeren dat een unit met dezelfde naam bevat. U moet dan kiezen welk pakket u installeert of een van de conflicterende units hernoemen.

Dynamische pakketten zijn bovendien groter dan gewone shared libraries omdat ze een kaart (map) van alles bevatten wat erin zit. Het strippen (stripping) van een dynamisch pakket verwijdert de metadata die nodig is om als dynamisch pakket te functioneren.

RTL-verbeteringen

De runtime library is uitgebreid om dynamische pakketten te ondersteunen, inclusief de metadata die nodig is om hun inhoud te inspecteren en de mechanismen die nodig zijn om dynamische typen en thread-variabelen veilig over pakketgrenzen heen te beheren.

type
TPackageType = record
  { De unit die het type declareert }
  UnitName: string;
  { De naam van het type }
  Name: string;
  { Het soort type }
  Kind: TTypeKind;
  { De RTTI van het type }
  Info: PTypeInfo;
  { De klasse als Kind tkClass is, anders nil }
  ClassType: TClass;
end;

Er is een nieuwe unit, DynPkgTypes.pas, toegevoegd aan de RTL om u te helpen informatie uit dynamische pakketten te lezen. De beproefde RTTI-informatie kan uit een pakket worden verzameld om met de inhoud ervan te werken. In de RTL zijn voldoende functies aanwezig om alles te doen wat nodig is met dynamische pakketten, waaronder het laden, opsommen, zoeken en ontladen van pakketten tijdens runtime.

Lazarus-verbeteringen

Ik heb verschillende verbeteringen aangebracht in de Lazarus IDE om beter gebruik te maken van dynamische pakketten.

Progressievenster en pakketbeheer

Het eerste dat u zult opmerken bij het openen van Lazarus is een nieuw voortgangsvenster dat buiten de IDE kan verschijnen. Wanneer u broncode wijzigt die behoort tot een pakket dat bij de IDE is geregistreerd, detecteert het systeem de wijzigingen en bouwt het pakket automatisch opnieuw voordat Lazarus start. Tijdens dit proces meldt een voortgangsvenster dat er wijzigingen zijn gedetecteerd en dat het pakket opnieuw wordt gebouwd.

Hetzelfde venster verschijnt wanneer u ervoor kiest om de volledige IDE opnieuw te bouwen. Het blijft zichtbaar en toont informatie over de huidige operatie, inclusief de geschatte resterende tijd voordat de IDE herstart. Het voortgangsvenster verschijnt ook bij het toevoegen of verwijderen van pakketten als de operatie langer dan vijf seconden duurt. Dit kan gebeuren bij het gelijktijdig installeren van meerdere pakketten, wat overigens ook een nieuwe functie is.

Pakkettenvenster en compilatie

Een andere verbetering is een nieuw 'Packages'-venster, dat standaard aan de rechterkant is vastgezet (docked). Hierin worden de pakketten getoond die u onlangs heeft geopend; deze lijst blijft bewaard tussen Lazarus-sessies. U kunt door de bestanden in een pakket bladeren en dubbelklikken op een item om het te openen.

Wanneer u een geïnstalleerd pakket opent en op 'Compile' klikt in het pakketvenster, worden alle bijbehorende controls, componenten of extensies binnen één of twee seconden bijgewerkt. Dit versnelt de ontwikkeling aanzienlijk, omdat wijzigingen die voorheen een volledige herbouw van de IDE vereisten, nu bijna onmiddellijk kunnen worden toegepast.

Het pakketmenu, waarin de beschikbare pakketten staan, is eveneens verbeterd. Het toevoegen of verwijderen van pakketten werkt nu de IDE bij zonder dat een herbouw nodig is.

Geavanceerde pakketopties

Ook de pakketopties zijn uitgebreid. Onder 'IDE Integrations' kunt u een pakket nu beperken tot een specifiek platform of een specifieke widgetset. Als een pakket niet voldoet aan de huidige platform- of widgetset-vereisten, verschijnt het niet in de pakketlijst. De IDE zal het pakket ook negeren wanneer het wijzigingen detecteert of anderszins zou proberen het te openen.

Voorbeeld: Stel dat u een TWinControl-implementatie ontwikkelt voor de Windows IShellView COM-interface. U kunt het pakket zo beperken dat het alleen beschikbaar is wanneer Lazarus op Windows draait.

Form- en Frame-conversie

Een andere verbetering die ik heb gemaakt om de mogelijkheden van dynamische pakketten te demonstreren, is een nieuwe conversiefunctie voor forms of frames voor Lazarus-extensies. Deze converteert elk form of frame in een installeerbare control die automatisch wordt toegevoegd aan het componentpalet. Alle controls, events, methoden en eigenschappen worden gekopieerd naar de nieuwe control. Hierdoor is het mogelijk om nieuwe controls visueel te ontwerpen en in te richten, of om bestaande forms en frames eenvoudig in andere projecten te hergebruiken.

Let op: Als u probeert een pakket te bouwen dat momenteel wordt gebruikt door een open form, zal de IDE u vragen het form op te slaan of te sluiten voordat het pakket wordt gebouwd.

Beschikbaarheid en ondersteuning

Deze functies en meer zijn nu beschikbaar voor download via de onderstaande links. De volledige wijzigingen in FPC en Lazarus zijn gecommit naar GitLab in mijn geforkte branch genaamd dynamic packages.

  • [FPC dynamic package branch]
  • [Lazarus dynamic package branch]

Mocht u problemen tegenkomen, meld deze dan via de GitLab issue tracker die gekoppeld is aan de bovenstaande branches. Als u dynamische pakketten wilt bespreken of vragen heeft, kunt u het onderstaande forum gebruiken. Let op dat een moderator uw account moet goedkeuren voordat u nieuwe berichten kunt plaatsen.

Bijdragen

Als u wilt helpen met dit project: de site heeft moderators nodig. U kunt mij (sysrpl) een privébericht sturen als u geïnteresseerd bent in een bijdrage.

Daarnaast heb ik moeite met het werken op macOS en het produceren van de nodige builds. Mocht iemand een M1 Macintosh hebben die hij/zij wil doneren, stuur mij dan een privébericht. Ik zou graag werken aan de compiler-code die nodig is om dynamische pakketten op de M1-architectuur te ondersteunen.