[gambit-list] Status of compiling R7RS programs

Marc Feeley feeley at iro.umontreal.ca
Mon Nov 11 17:58:57 EST 2019


> On Nov 11, 2019, at 11:03 AM, Lassi Kortela <lassi at lassi.io> wrote:
> 
> Is there a way to compile a R7RS program that uses a R7RS library into a standalone executable using the latest Gambit from git?
> 
> The sample implementation for draft SRFI 175 works fine with gsi. gsc produces executables without complaining, but when trying to run those executables, they exit with code 70 and don't write anything to stdout or stderr. Using -:debug= flags to redirect the output does not cause any messages to be shown either.
> 
> Here's how to reproduce the problem:
> 
> $ git clone -q https://github.com/lassik/srfi-175.git ~/tmp/srfi-175 --branch gambit
> $ cd ~/tmp/srfi-175
> $ srfi/compile-gambit-r7rs.sh
> Entering directory '/Users/lassi/tmp/srfi-175'
> + gsc-script -:r7rs -exe . srfi/examples.scm
> + gsc-script -:r7rs -exe . srfi/tests.scm
> $ git ls-files --others
> srfi/examples
> srfi/tests
> $ srfi/examples; echo "exited with code $?"
> exited with code 70
> $ srfi/tests; echo "exited with code $?"
> exited with code 70
> 
> The two programs are srfi/examples.scm and srfi/tests.scm; both depend on the R7RS library srfi/175.sld. Note that the programs (.scm files) are not wrapped within `define-library` forms.

The issue you are encountering is related to Gambit’s module loader and the fact that it hasn’t yet been upgraded to seamlessly support multi module programs.  In the not too distant future your usage of gsc-script should just work as-is, but for now you need to help the “old-style” module loader locate the modules.  Here are some explanations.

The Gambit compiler currently supports linking multiple modules into a single executable.  The gsi and gsc executables are in fact composed of multiple modules.  You can see this by looking at the table of registered modules:

% gsi
Gambit v4.9.3

> ##registered-modules
(#(_gsi 2 #(#(_gsi) #() () 1 #<procedure #2 _gsi#> #<foreign #3 0x10172eda0>))
 #(_gsilib 2 #(#(_gsilib) #() () 1 #<procedure #4 _gsilib#> #<foreign #5 0x10199f600>))
 #(_repl 2 #(#(_repl) #() () 1 #<procedure #6 _repl#> #<foreign #7 0x10188f200>))
 #(_thread 2 #(#(_thread) #() () 1 #<procedure #8 _thread#> #<foreign #9 0x10187f920>))
 #(_nonstd 2 #(#(_nonstd) #() () 1 #<procedure #10 _nonstd#> #<foreign #11 0x10186aa00>))
 #(_io 2 #(#(_io) #() () 1 #<procedure #12 _io#> #<foreign #13 0x101830de0>))
 #(_module 2 #(#(_module) #() () 1 #<procedure #14 _module#> #<foreign #15 0x10180bfc0>))
 #(_eval 2 #(#(_eval) #() () 1 #<procedure #16 _eval#> #<foreign #17 0x1017ed560>))
 #(_std 2 #(#(_std) #() () 1 #<procedure #18 _std#> #<foreign #19 0x101795880>))
 #(_num 2 #(#(_num) #() () 1 #<procedure #20 _num#> #<foreign #21 0x1017589c0>))
 #(_system 2 #(#(_system) #() () 1 #<procedure #22 _system#> #<foreign #23 0x1017448c0>))
 #(_kernel 2 #(#(_kernel) #() () 1 #<procedure #24 _kernel#> #<foreign #25 0x1017367c0>)))

You can see that gsi is a collection of 12 modules. The _gsi module is gsi's “main” module, _module is the filesystem module getter/loader, _eval is the Scheme interpreter, _num is the numerical library, etc

The order in which these modules are “loaded” is in the reverse order, i.e. _kernel is loaded first, then _system, etc and finally _gsi which (usually) starts gsi's REPL.  “loading” a module essentially means executing its top-level forms (expressions and definitions).

The link file (in this case gsi/_gsi_.c generated by the Gambit compiler) contains an ordered list of the modules to be initialized and each module has a descriptor to store some meta-information about the module, in particular:

- a “preload” flag
- the list of modules this module depends on, as indicated in the (##demand-module <module-ref>) forms contained in the source file

The preload flag is “on” by default, and indicates that this module must be loaded even if no other module depends on it.  This flag can be controlled with the -preload/-nopreload Gambit compiler options.  The main module is always loaded, even if its preload flag is “off”.

When the preload flag is off, the module is linked into the executable but it is not loaded.  If at some point during the execution a (##demand-module <the-module>) is evaluated, then the module will be loaded (once) by executing its top-level forms. There is no need to search the filesystem for the module, so this is quick and it avoids problems with the module search order being incorrectly set for the modules installed in the filesystem.

Up until recently, the module descriptors did not specify the module dependencies, and the order of the modules on the command-line of the Gambit compiler’s linking invocation (combined with the -preload/-nopreload options) was the only way to specify the load order.

But now, the primitive module loader in the _kernel module has been extended to also take the module dependencies into account to determine the module load order.  Now the topological sort of the module dependencies determines the module load order.

It is the primitive module loader in the _kernel module that determines the module load order, after it has loaded (in a sense the Gambit runtime system only loads the _kernel module, and it is then the responsability of the _kernel module to load the other modules).

In the case of your:

% gsc-script -:r7rs -exe . srfi/examples.scm

The module srfi/examples.scm contains a dependency on the module srfi/175.  However, the executable does not contain that module and when the executable is run it needs to get srfi/175 from the filesystem to know the dependencies of srfi/175 (which could require getting other modules from the filesystem before any of the modules are actually “loaded”).  But the filesystem module getter/loader is in module _module, that hasn’t been loaded yet!

This can be solved by turning off the preload flag of the main program.  In that case, the modules of Gambit's runtime system (including _module) will be loaded before the main program and its dependencies are loaded.  So all you need to do is:

% gsc-script -:r7rs -exe . -nopreload srfi/examples.scm

If you then try to run your program, you will get this:

% srfi/examples 
*** ERROR IN ##collect-modules -- Module not found
(##get-module 'srfi/175)

That error is because _module is trying to fing the module srfi/175 but the search order does not contain the current directory. So you need to explicitly add that to the runtime system’s search order:

% srfi/examples -:search=.
(hex-digit #\a) => 10
(hex-digit #\0) => 0
(hex-digit #\9) => 9
(hex-digit #\A) => 10
(hex-digit #\F) => 15
(hex-digit #\G) => #f
(ascii-nth-upper-case 0) => #\A
(ascii-nth-upper-case -1) => #\Z
(ascii-nth-lower-case 15) => #\p
(caesar -55 (caesar 55 "hello world")) => "hello world"
(call-with-port (open-binary-input-file "/bin/ls") strings) => __PAGEZERO
__TEXT
...

Sorry for the long explanation.  The situation will improve when the compiler can analyze the module dependencies at compile time and include in the executable all of the required modules.

Marc





More information about the Gambit-list mailing list