2

Apparently my previous question was too broad. So here's the question again, simplified, and with example source code.

I'm trying to compile a Chicken Scheme project containing multiple files:

test-a.scm:

#!/usr/bin/csi -script

(declare (unit test-a))
(declare (uses test-b))

(load "test-b.scm")
(use test-b)

(test-syntax)

test-b.scm:

(declare (unit test-b))

(module test-b *
 (import scheme chicken)
 (define-syntax test-syntax
  (syntax-rules ()
   ((_)
    (print "In test-syntax")))))

According to the official manual, I should do it like this:

csc -c test-b.scm
csc -c test-a.scm
csc test-a.o test-b.o -o test

What I actually get is this:

Syntax error (import): cannot import from undefined module

Things to note:

  • I'm calling a macro.
  • I have a (declare (uses clause, yet csc can't find my sources.
  • csc test-a.scm test-b.o -o test doesn't work either.
  • If I remove load, the program will not work in csi.
  • If I remove use, the program will not work in csi.
  • I need the program to work in csi.

How, without breaking compatibility with csi, can I make this compile?

Community
  • 1
  • 1
Sod Almighty
  • 1,768
  • 1
  • 16
  • 29

1 Answers1

2

There are four(!) problems here:

  • test-a.scm contains a unit declaration. This is incorrect; there's always one file that needs to be compiled to have a main() C function. That's the file without a unit declaration. If you study the manual page you linked more closely, it says "In this case foo.scm is the main module, because it doesn't have a unit declaration".
  • Since you decided to use modules, you'll need to compile test-b.scm as follows: csc -c -j test-b test-b.scm. The -j switch will cause the compiler to emit a module library test-b.import.scm, which is what the compiler is looking for when compiling test-a.scm. When an import library is missing, it will complain that the module is undefined. In the interpreter it's no issue because you load the file before importing the module that it defines.
  • You're using load, even in the compiled version of the program. This means that it will read and evaluate the test-b.scm file (and complain if it's missing) in every situation.
  • You're using use, which will require the library at runtime. This is meant for loading and importing modules defined by dynamically linked libraries.

So, to solve this, you could do it like this:

test-a.scm

#!/usr/bin/csi -script

;; Declare that this uses test-b, so that its toplevel is initialised
(declare (uses test-b))
;; No (declare (unit test-a)) because this file should generate main().

;; Because we tell the compiler what to link together and we want to
;; avoid passing all the .scm files on the csi command line, we can load
;; the test-b.scm file here, but only when interpreting:
(cond-expand
  ((not compiling) (load "test-b.scm"))
  (else))

;; Only import the module; we take care of loading the code above,
;; or in the linking step when compiling.  If we had (use test-b),
;; the library would be searched for at runtime.
;; Alternatively, (use test-b) here, but add (register-feature! 'test-b)
;; to test-b.scm, which prevents the runtime from attempting to load test-b.
(import test-b)

(test-syntax)

test-b.scm (unchanged)

(declare (unit test-b))

(module test-b *
 (import scheme chicken)
 (define-syntax test-syntax
  (syntax-rules ()
   ((_)
    (print "In test-syntax")))))

And, to compile it:

csc -c -j test-b test-b.scm
csc -c test-a.scm
csc test-a.o test-b.o -o test

I realise this is quite a lot of stuff to know, and tricky too and some things like the use plus register-feature! simply don't make much sense. We're attempting to make this less fiddly in CHICKEN 5, and we're also going to add a FAQ to the wiki, because this really isn't obvious and a bit of a FAQ.

The manual page you linked hasn't been changed in a long time: it completely ignores the existence of modules, for example. That's why you couldn't get it to compile, the -j switch was missing because the example files in the manual page don't define modules.

Edit:

This can be cleaned up a bit because declare is only honored by the compiler anyway. So we can move that into the cond-expand as well:

test-a.scm

#!/usr/bin/csi -script
(cond-expand
  (compiling (declare (uses test-b)))
  (else (load "test-b.scm")))

(import test-b)

(test-syntax)
sjamaan
  • 2,282
  • 10
  • 19
  • Thanks, that's very useful. But can I ask a follow-up question? Why am I getting errors like `undefined reference to blah_toplevel`? I've generated import files, and I'm linking the .o files. Admittedly, the files I'm compiling are a little more complicated than the example. – Sod Almighty Aug 17 '16 at 22:14
  • `blah_toplevel` is the "main" entrance point of a unit. It's not the actual C `main()`, but it is called by all the units that use `blah` to initialize that unit. If it's missing, perhaps you have `(declare (uses blah))` in some source file but you forgot to actually link it to `blah`? Another possible explanation is that you have `(declare (unit blah2))` inside the source file for blah, so that it doesn't declare the same unit name that others are expecting. The exact cause is hard to tell without actually seeing the source code. – sjamaan Aug 18 '16 at 06:39
  • I tried to invite you to SO chat, but I don't think it worked. If I give you my project files, could you tell me why I'm getting an odd error, please? – Sod Almighty Aug 19 '16 at 19:05
  • I wasn't online at the time. Maybe you can put them on a pastebin? Alternatively, try sending the files (if they're not too big!) to the `chicken-users` mailinglist and asking for help there. I also read that list, so unless someone beats me to it I'll be able to respond there. – sjamaan Aug 20 '16 at 11:38