Is your feature request related to a problem? Please describe.
What problem does this solve?
I'd like to be able to provide Xmake modules from sources other than normal .lua files in module directories, while keeping the caller experience the same:
Some examples:
- generated Lua modules
- virtual/test modules
- transpiled modules from another Lua-targeting language
- project-local module providers that do not map cleanly to static files
Right now, import(...) is effectively bound to module files and module directories. That works well for normal cases, but it makes generated or dynamic modules awkward because they need to exist as physical files before project/script code can import them.
Why this seems useful
This keeps import(...) as the single user-facing module API while allowing advanced projects to provide generated or virtual modules cleanly.
The feature is intentionally generic and is not tied to any specific language or generator.
Describe the solution you'd like
Proposed API
Add a project API:
add_moduleresolver(function (name, ctx)
if name == "virtual.hello" then
return ctx.module({
hello = function ()
return "hello"
end
})
end
end)
Then normal imports continue to work:
local hello = import("virtual.hello", {anonymous = true})
print(hello.hello())
For generated file-backed modules:
add_moduleresolver(function (name, ctx)
local generated = try_generate_module(name)
if generated then
return ctx.file(generated)
end
return ctx.miss()
end)
Resolution order
Resolvers should be fallback providers:
- normal module lookup
- registered module resolvers, in registration order
- normal import failure
This preserves existing behavior and avoids accidentally shadowing built-in or project-local modules.
Expected behavior
- resolvers are only called after normal module lookup fails
- resolver results are cached by logical module name
nocache bypasses resolver result caching
ctx.file(path) loads through the existing module loader
ctx.module(table) provides an in-memory module export table
ctx.miss() explicitly skips the resolver
Describe alternatives you've considered
Alternatives considered
I looked at a few existing approaches before implementing this:
1. Pre-generating modules before running Xmake
This works for some cases, but it pushes the responsibility outside of Xmake itself. The generated modules must already exist before any project logic can import them, which makes workflows involving generated or dynamic modules awkward.
It also complicates caching/invalidation because generation becomes disconnected from the import system.
2. Generating modules through custom targets/hooks
I considered using existing hooks such as before_build, before_load, etc. to generate Lua modules into module directories.
The main issue is lifecycle timing. import(...) is used very early during project/script evaluation, before most build hooks run. This means generated modules still need to exist ahead of time or require awkward bootstrap logic.
This also tends to pollute projects with helper targets/scripts whose only purpose is preparing importable modules.
3. Extending add_moduledirs()
I considered whether this could be modeled entirely as additional module search directories.
That works for static/generated files, but it still assumes modules exist as filesystem-backed Lua files and does not support:
- virtual modules
- dynamically synthesized modules
- generated modules that do not naturally map to static files
- alternate module providers
Why a resolver API seemed cleaner
A resolver API keeps all of this behind the existing import(...) interface while preserving normal module semantics and precedence.
The caller still just writes:
and the project can decide how that module is provided.
The implementation is intentionally fallback-only so existing behavior remains unchanged unless a module cannot already be resolved normally.
Additional context
Implementation PR with Tests: #7569
Documentation PR: xmake-io/xmake-docs#279
Is your feature request related to a problem? Please describe.
What problem does this solve?
I'd like to be able to provide Xmake modules from sources other than normal
.luafiles in module directories, while keeping the caller experience the same:Some examples:
Right now,
import(...)is effectively bound to module files and module directories. That works well for normal cases, but it makes generated or dynamic modules awkward because they need to exist as physical files before project/script code can import them.Why this seems useful
This keeps
import(...)as the single user-facing module API while allowing advanced projects to provide generated or virtual modules cleanly.The feature is intentionally generic and is not tied to any specific language or generator.
Describe the solution you'd like
Proposed API
Add a project API:
Then normal imports continue to work:
For generated file-backed modules:
Resolution order
Resolvers should be fallback providers:
This preserves existing behavior and avoids accidentally shadowing built-in or project-local modules.
Expected behavior
nocachebypasses resolver result cachingctx.file(path)loads through the existing module loaderctx.module(table)provides an in-memory module export tablectx.miss()explicitly skips the resolverDescribe alternatives you've considered
Alternatives considered
I looked at a few existing approaches before implementing this:
1. Pre-generating modules before running Xmake
This works for some cases, but it pushes the responsibility outside of Xmake itself. The generated modules must already exist before any project logic can import them, which makes workflows involving generated or dynamic modules awkward.
It also complicates caching/invalidation because generation becomes disconnected from the import system.
2. Generating modules through custom targets/hooks
I considered using existing hooks such as
before_build,before_load, etc. to generate Lua modules into module directories.The main issue is lifecycle timing.
import(...)is used very early during project/script evaluation, before most build hooks run. This means generated modules still need to exist ahead of time or require awkward bootstrap logic.This also tends to pollute projects with helper targets/scripts whose only purpose is preparing importable modules.
3. Extending
add_moduledirs()I considered whether this could be modeled entirely as additional module search directories.
That works for static/generated files, but it still assumes modules exist as filesystem-backed Lua files and does not support:
Why a resolver API seemed cleaner
A resolver API keeps all of this behind the existing
import(...)interface while preserving normal module semantics and precedence.The caller still just writes:
and the project can decide how that module is provided.
The implementation is intentionally fallback-only so existing behavior remains unchanged unless a module cannot already be resolved normally.
Additional context
Implementation PR with Tests: #7569
Documentation PR: xmake-io/xmake-docs#279