Namespace-require can't find module in executable

Hello Racket community!
I'm trying to use namespace-require, but the executable seems to fail to look up the module, while commandline racket can run it normally. This is the minimal example:

test-pkg
├── lib.rkt
└── test.rkt
; test.rkt
#lang racket/base
(module+ main
  (parameterize ([current-namespace (make-base-namespace)])
    (namespace-require 'test-pkg/lib)
    (eval '(greet))))
; lib.rkt
#lang racket/base
(provide greet)

(define (greet)
  (display "Hello\n"))

First install the test-pkg package by running raco pkg install in the test-pkg directory.
Then running racket test.rkt normally outputs Hello. But running an executable generated by raco exe results in an error:

$ raco exe test.rkt && ./test
standard-module-name-resolver: collection not found
  for module path: test-pkg/lib
  collection: "test-pkg"
  in collection directories:
  context...:
   .../test-pkg/test.rkt:3:2
   body of '|#%mzc:test(main)|
   body of top-level

I have found the last paragraph of 6.1.5 Module References Within a Collection mentioned a similar case, but I think I correctly used collection path (test-pkg/lib) instead of relative-path. What can I do?

Perhaps it might be helpful to use the module path index instead.

test.rkt:

#lang racket/base
(require racket/runtime-path (for-syntax racket/base))

(module test racket/base
  (provide square anchor)
  (define (square x)
    (* x x))
  (define-namespace-anchor anchor))

(require (only-in 'test anchor))

(define-runtime-module-path-index test '(submod "." test))
(define test/resolved (module-path-index-resolve test))

(parameterize ([current-namespace (make-base-namespace)])
  (namespace-attach-module (namespace-anchor->empty-namespace anchor)
                           test/resolved)
  (namespace-require test/resolved)
  (displayln (eval '(square 3))))
$ raco exe test.rkt
$ ./test
9

Thanks for your reply! module-path-index-resolve really solved the problem.
I have tried resolve-module-path before, but it failed. I'm feeling that the module system is really intricate, and there are so many variants (and different phase levels maybe). I still need to figure out what all that means. Again thank you very much!

Did you mean make-resolved-module-path?

I think it is racket/runtime-path that matters. This module is used to access files and directories at run time.

As well as define-runtime-module-path-index, you could try define-runtime-module-path.

test.rkt:

#lang racket/base
(require racket/runtime-path)

(module test racket/base
  (provide square anchor)
  (define (square x)
    (* x x))
  (define-namespace-anchor anchor))

(require (only-in 'test anchor))

(define-runtime-module-path test/resolved (submod "." test))

(parameterize ([current-namespace (make-base-namespace)])
  (namespace-attach-module (namespace-anchor->empty-namespace anchor)
                           test/resolved)
  (namespace-require test/resolved)
  (displayln (eval '(square 3))))

Although it automatically resolves the module path, it is not recommended by the documentation.

The define-runtime-module-path-index form is usually preferred, because it creates a weaker link to the referenced module. Unlike define-runtime-module-path-index, the define-runtime-module-path form creates a for-label dependency from an enclosing module to module-path.

In the version without define-runtime-module-path-index, have you tried using ++lib when building the executable?

Using namespace-require seems like it would have the same kinds of issues that dynamic-require does in terms of needing ++lib/++lang to make sure raco exe embeds references to modules that otherwise appear unused.

IOW: raco exe test.rkt has no idea that it should bundle up lib.rkt; the racket/runtime-path collection cooperates with the executable and distribution builders to signal that, but you can also pass ++lib/++lang.

See https://docs.racket-lang.org/raco/exe.html

(Apologies for any imprecise terms! I'm sure someone can show a more precise way to say "embeds references" or "bundle up.")

1 Like