Skip to content

Added Option to create an lpk for libraries#7

Open
andrewd207 wants to merge 4 commits intograemeg:masterfrom
andrewd207:laz_pkg
Open

Added Option to create an lpk for libraries#7
andrewd207 wants to merge 4 commits intograemeg:masterfrom
andrewd207:laz_pkg

Conversation

@andrewd207
Copy link
Copy Markdown
Contributor

First I changed the TArgumentParser.GoalToString/ToGoal code to not need updating for new goals. This was not really needed so, sorry for that.

Added an option 'lazarus-package' as a goal. It builds an lpk if a project is a 'library' type. + 2 new units to implement that.
I based then on the Compile command and removed what I didn't need.

Specifically I did this for the fpgui_framework.lpk file. which I later found in _template. But this could be useful too for anything. Lazarus is more than happy to generate the pas file that goes with the lpk.

Let me know if this fits or there's something you'd like changed.

… need less work.

Improved Unit name detection for BootstrapGenerator. More units are found. Added Param to DiscoverUnits to allow returning whole filename/path or only unit name as before. And protected instead of private so inherited class could reuse code.
…ound. Added Param to DiscoverUnits to allow returning whole filename/path or only unit name as before. And protected instead of private so inherited class could reuse code.
@andrewd207
Copy link
Copy Markdown
Contributor Author

andrewd207 commented Mar 23, 2026

Also, it's worth noting that pasbuild will find more .pas files with the improvement to unit name searching and for fpgui it's agg units. Several of them don't compile, because of comments or being windows/gdi or using units not in the source tree scope.

@graemeg graemeg self-assigned this Mar 27, 2026
@graemeg
Copy link
Copy Markdown
Owner

graemeg commented Mar 28, 2026

Apologies for the delay! I somehow didn't get any notifications about this PR—I'll double-check my repository settings to see what went wrong.

First off, thank you so much for the contribution and for showing interest in PasBuild. It’s great to see others poking at the codebase.

You actually jumped ahead of my future plans! 😃 From the start, I’ve had the idea of auto-generating Lazarus Packages, but it has been a lower priority. I’m hesitant to bake the LPK generation directly into the core PasBuild binary, as it slightly conflicts with my goal of keeping the tool project-independent, and avoiding hard-coded paths in projects or tying to external projects file formats that could change (given how much LPK layouts have varied over time).

My goal was to handle this via a plug-in system, loosely inspired by how Git handles extensions. I’ve recently implemented the resolve command [links below], which is a key building block for this. This "loose coupling" means plugins can be implemented in any language without the version-matching headaches of traditional DLL/SO files. You can read more about the plug-in system and two example implementations here: https://github.com/graemeg/PasBuild/tree/master/extras/plugins/hello-world

Even though I'm not merging the LPK logic directly yet, your PR was incredibly helpful for two reasons:

  1. CLI Refactoring: I appreciate you calling out the need for refactoring in the CLI unit. I’ve gone ahead and cleaned that up (see commit dc4e742). The build goals and argument support are now clearly separated, as they should have been!
  2. Unit Discovery Bug: Good spot on the missing units in the bootstrap program. Your solution pointed me in the right direction, though it was a bit susceptible to false matches (like unitfoo or the word units in comments). I’ve added unit tests to confirm the bug and implemented a tweaked version of your fix to handle those edge cases.

Thanks again for the input—it definitely made the project better. Let me know if you have any thoughts on the plugin system!

@andrewd207
Copy link
Copy Markdown
Contributor Author

That’s cool — I admit it felt a little alien to have a Lazarus target in the somewhat generic build system.

I did have one thought as I was writing the plugin: if each build goal has a corresponding class, why not just have a registration with a class, like:

RegisterGoal(TMyGoal); or RegisterGoal(IMyGoal/ 'my-goal', @ConstructorFunc)

Then it wouldn't need an enum or to/from, just PerformGoal(FoundGoal) But I looked a little closer and saw that when the goals are called, they have different parameters — so it’s not quite that simple. FWIW freepascal can use typeinfo to call procedures now in trunk.

But assuming you could register them, you could use interfaces and even load dynamic libraries that implement your plugins, etc.

In that case, a package could probably be made for Lazarus that implements a plugin for PasBuild.


uses
  typinfo, rtti;

function CallObjectMethod(AObject: TObject; AToolRequest: TJsonObject): String;
var
  lFun: TJSONObject;
  lObject: TObject;
  lParams: TMethodParamInfoArray;
  Method: TMethod;
  ArgValues: specialize TArray<TValue>;
  i, ArgIdx: integer;
  ArgValue, ResValue: TValue;
  ArgJson: TJSONData;
  ParamTypeInfo: PTypeInfo;
  tmpStr: TJSONStringType;
  tmpDouble: TJSONFloat;
  tmpBool: Boolean;
  lMethodInfo: TMethodSignatureInfo;
begin
  lFun := AToolRequest.Objects['function'];
  lObject := AObject;

  // add self param first.
  SetLength(ArgValues, 1);
  TValue.Make(@lObject, lObject.ClassInfo, ArgValue);
  ArgValues[High(ArgValues)] := ArgValue;

  // Assume GetSignature populates Method and lParams correctly
  GetSignature(lObject, lFun.Strings['name'], Method, lParams, lMethodInfo);

  // Build TValue array for non-self args

  ArgIdx := 0;
  for i := 0 to High(lParams) do
  begin
    if (lParams[i].ParamName = '$self') or // handled
      (lParams[i].ParamName = '$result') then // not a parameter
      Continue;

    ParamTypeInfo := lParams[i].ParamTypeRef^; // PTypeInfo for param
    ArgJson := lFun.Objects['arguments'].Find(lParams[i].ParamName);

    if not Assigned(ArgJson) then
      raise Exception.CreateFmt('Missing arg: %s', [lParams[i].ParamName]);

    // Support allowed Ollama types: strings, numbers (int/float), bool, null
    if ArgJson.JSONType = jtString then
    begin
      tmpStr := ArgJson.AsString;
      TValue.Make(@tmpStr, ParamTypeInfo, ArgValue)
    end
    // Assume string
    else if ArgJson.JSONType = jtNumber then
    begin
      if ParamTypeInfo^.Kind in [tkInteger, tkInt64, tkQWord] then
        ArgValue := TValue.FromOrdinal(ParamTypeInfo, Round(ArgJson.AsInt64))
      else // float types
      begin
        tmpDouble := ArgJson.AsFloat;
        TValue.Make(@tmpDouble, ParamTypeInfo, ArgValue);
      end;
    end
    else if ArgJson.JSONType = jtBoolean then
    begin
      tmpBool := ArgJson.AsBoolean;
      ArgValue := TValue.From(ParamTypeInfo, @tmpBool)
    end
    else if ArgJson.IsNull then
      ArgValue := TValue.Empty
    else
      raise Exception.CreateFmt('Unsupported arg type for %s',
        [lParams[i].ParamName]);

    SetLength(ArgValues, Length(ArgValues) + 1);
    ArgValues[High(ArgValues)] := ArgValue;
  end;

  ResValue := Invoke(Method.Code, ArgValues, lMethodInfo.CC, TypeInfo(String), False, False);
  // Adjust cc, assume instance method

  // Assume string result for Ollama tool
  if ResValue.TypeInfo^.Kind = tkString then
    Result := ResValue.AsString
  else if ResValue.IsEmpty then
    Result := ''
  else
    Result := ResValue.ToString;
end;            

I was using this to call tools from an ollama tool request. I limited myself to basic types without const/out/var but I imagine passing classes shouldn't be too bad either.

@andrewd207
Copy link
Copy Markdown
Contributor Author

Ahh you already have a working plugin system 😀 I had read it as it was in your future plans.

I'm very curious what are you using to write freepascal files as your IDE?

@graemeg
Copy link
Copy Markdown
Owner

graemeg commented Mar 28, 2026

Ahh you already have a working plugin system 😀 I had read it as it was in your future plans.

👍

I'm very curious what are you using to write freepascal files as your IDE?

TL;DR: maximus

The back story...
As of about 1 month ago, I solely use Maximus (fpGUI's own IDE), so it forces me to see what's missing. I created it years ago, as a more elaborate "demo app", but recently I started going down a rabbit hole. 🙈 I wanted an IDE to test integration of OPDF (the new FPC debug format I created and reference implementation CLI debugger). I designed it so the "debug engine" is completely separate from the CLI.

MSEide (which was normally my go-to IDE) had too tight integration with GDB. FP IDE (that FreeVision IDE) also had tight integration with GDB, but that is actually a lot easier to refactor to support multiple debuggers than MSEide. Lazarus - well yes that has better design, but there is so much about Lazarus I'm not a fan of, and I didn't want to step on any toes.

So I thought I would use fpGUI's IDE instead. But there starts the problem. It was far from being an "ide". My recent years with Java and Eclipse/IntelliJ has taught me how well IDEs can work. Moving back to MSEide and Lazurus felt like the stone age. 😁

So, I created my own debug format, I already created my own debugger, I created my own GUI toolkit, I created a Maven (java) inspired build system. I have all this awesome experience with IntelliJ.... It just seemed logical to bring this all together - in the form of an IDE. 😆 So that's what I'm working on. Still got tons to do. The editor was a good start, but it missed a lot of features. So I started there. Then tokenised highlighting (and I threw in a few editor themes for good measure). Then PasBuild integration (using the same project.xml files - lets keep going with the "auto discover everything" instead of "Edit Compiler Settings" dialog boxes). Then code navigation (currently busy with). After that, I'll finally get to debugger integration stage! Yup, down the rabbit hole... 🙄

Feel free to give it a try and let me know your thoughts. ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants