Background
This exercise was prompted by the need for Scripted Testing to be supported by – as far as possible – code generation. The need is for the public or interfacing view of a supporting part (domain) of a system to be able to log calls made to it and to provide values on calls to it, using a scripting mechanism.
A previous project, ColdFrame, made extensive provision for this. An example of the scripting support code generated for
function Get (S : Input_Signal) return Boolean;
was
function Get
(S : Input_Signal)
return Boolean is
Lock : ColdFrame.Stubs.Lock (ColdFrame.Stubs.Mutex'Access);
pragma Unreferenced (Lock);
Call : constant Positive := ColdFrame.Stubs.Note_Entry
("Digital_IO.Get");
begin
Input_Signal'Output
(ColdFrame.Stubs.Get_Input_Value_Stream
("Digital_IO.Get", "S", Call, S'Size),
S);
ColdFrame.Stubs.Check_For_Exception
("Digital_IO.Get", Call);
return Boolean'Input
(ColdFrame.Stubs.Get_Output_Value_Stream
("Digital_IO.Get", "return", Call));
end Get;
In that case, the framework was based on code generation from UML models, so generating this code wasn’t a stretch. In more typical circumstances, you’d want to generate these bodies from package specs.
Back in the day, one could have extracted the Ada spec’s structure using ASIS (e.g. ASIS2XML), but now that FSF GCC later than 10.3 doesn’t support ASIS some other approach is needed.
The obvious solution is to use Libadalang.
Libadalang
Libadalang is available in Alire, and the documentation includes code to traverse the parsed structure (that’s the Ada interface; there’s a Python interface too).
This traversal appears to flatten the tree, which of course makes it hard to see where one construct ends and the next begins; for example, the return type of a function is distinguished by appearing after all the parameters. Also, it wasn’t easy to work out from the documentation which subprograms were useful for what (note, the same was true for ASIS!) So, the alternative approach I adopted was an explict tree navigation, dumping the node types (and text content, where relevant); see libadalang2xml.
At this point, some of the issues with the actual build process became apparent.
An important thing to note is that most of the trouble came from my (dogged? foolish? pigheaded?) insistance on using Macs (a Macbook Pro with Intel silicon, x86-apple-darwin
, and a Mac Mini with Apple silicon, aarch64-apple-darwin
). Linux? no worries.
Building
I initiated an Alire binary crate,
alr init --bin libadalang2xml
alr with libadalang
using the Alire-provided x86_64
compiler, gnat_native 12.2.1
. There were several stumbling blocks along the way, mainly to do with gnatcoll_gmp
and libgmp
.
gnatcoll_gmp
needsgmp
’s header files. One could of course go off and buildgmp
, but that seems rather contrary to the spirit of Alire, so get it via Homebrew.- On Intel silicon, Homebrew installs in
/usr/local
, while on Apple it’s in/opt/homebrew
(don’t ask me why), and in any case the way we’ve set up GCC to accept either of the possible SDK options has had the unfortunate side-effect of removing/usr/local/include
,/usr/local/lib
from the compiler’s standard paths, so we have to setCFLAGS
andLDFLAGS
appropriately.- Some day, Alire may understand
pkg-config
.
- Some day, Alire may understand
- On Intel silicon, Homebrew installs in
- Still on Intel silicon, all appears to go well until the final link, at which point the Libadalang and GNATCOLL libraries get a lot of missing symbols to do with tasking, protected types and semaphores.
- This is because of gprbuild issue 97; gprbuild has the concept of a “static stand-alone library”, which it implements using a
kludgetechnique which isn’t possible on Darwin.
- This is because of gprbuild issue 97; gprbuild has the concept of a “static stand-alone library”, which it implements using a
- The only reasonable solution seen for this was to build the libraries as relocatables (
LIBRARY_TYPE=relocatable
). Unfortunately, althoughgnatcoll_gmp
builds just fine,langkit_support
doesn’t, because it can’t findlibgmp
; this turns out to be becauselangkit_support.gpr
doesn’t readLDFLAGS
(nor, for that matter, doeslibadalang
).- If I build an executable against a dynamic library, gprbuild includes the library’s
Library_Options
in the link; why not if it’s building one dynamic library against another? but in this case we havelibadalang
>langkit_support
>gnatcoll_gmp
, so perhaps it’s expecting a bit much).
- If I build an executable against a dynamic library, gprbuild includes the library’s
This saga means that the only way to get a build is to use a full compiler suite, like that obtainable for GCC 12.2.0 (x86_64) or GCC 12.2.0 (aarch64).
Apple silicon
Let’s try building with the full x86_64
compiler under Rosetta on Apple silicon. Fails, because even though we have an x86_64
libgmp.dylib
with the compiler, the linker sees the aarch64
(a.k.a. arm64
) libgmp.dylib
provisioned via Homebrew:
ld: warning: ignoring file /opt/homebrew/lib/libgmp.dylib, building for macOS-x86_64 but attempting to link with file built for macOS-arm64
Nothing for it by to use the full compiler appropriate for the architecture.
At least once the tool’s been built, you only have to use it!
No comments:
Post a Comment