Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rule for nodejs addon #5938

Open
Freed-Wu opened this issue Dec 5, 2024 · 1 comment
Open

rule for nodejs addon #5938

Freed-Wu opened this issue Dec 5, 2024 · 1 comment

Comments

@Freed-Wu
Copy link
Contributor

Freed-Wu commented Dec 5, 2024

Is your feature request related to a problem? Please describe.

A rule to build nodejs addon.

Describe the solution you'd like

For meson, it like:

project('C++ node example addon', 'cpp')

inc_node = include_directories('/usr/include/node', is_system: true)

# For compatibility with node-gyp we use "shared_module" rather than
# "shared_library". The difference important in the current context is that
# shared_module will happily allow undefined references at link-time. It may or may
# not be what you want, feel free to change to `shared_library`, in which case you
# have to also link it with find_library('node')
shared_module(
  'hello',
  ['hello.cc'],
  include_directories : [inc_node],
  name_prefix: '',     # don't prepend "lib"
  name_suffix: 'node', # required for addon to load properly
)

node = find_program('node')

test('addon',
     node,
     args: ['-e',
            'const addon = require("./hello.node");'
            + 'console.log(addon.hello());'
           ]
    )

https://stackoverflow.com/questions/58633310/how-to-compile-node-js-native-api-extension-via-meson-build-system

Describe alternatives you've considered

No response

Additional context

Related: #5125

An example: https://github.com/tonyfettes/coc-rime/blob/master/xmake.lua

@Freed-Wu
Copy link
Contributor Author

Freed-Wu commented Dec 5, 2024

An experiment:

rule("nodejs.module")
    on_load(function (target)

        -- imports
        import("core.cache.detectcache")
        import("core.project.target", {alias = "project_target"})

        -- set kind
        if target:is_plat("macosx") then
            target:set("kind", "binary")
            target:add("ldflags", "-bundle", "-undefined dynamic_lookup", {force = true})
        else
            target:set("kind", "shared")
        end

        -- set library name
        local modulename = target:name():split('.', {plain = true})
        modulename = modulename[#modulename]
        target:set("filename", modulename .. ".node")

        -- export symbols
        if target:is_plat("windows") then
            local exported_name = target:name():gsub("%.", "_")
            exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name
            target:add("shflags", "/export:napi_register_module_v1", {force = true})
        else
            target:set("symbols", "none")
        end

        -- add node library
        local has_node = false
        local includedirs = get_config("includedirs") -- pass node library from nodejs/xmake.lua
        if includedirs and includedirs:find("node-api-headers", 1, true) then
            has_node = true
        end
        if not has_node then
            -- user use `add_requires/add_packages` to add node package
            for _, pkg in ipairs(target:get("packages")) do
                if pkg == "node-api-headers" then
                    has_node = true
                    break
                end
            end
        end
        if not has_node then
            target:add(find_package("node-api-headers"))
        end
    end)
    on_install(function (target)
        if target:is_plat("macosx") then
            target:set("kind", "shared")
        end
        local moduledir = path.directory((target:name():gsub('%.', '/')))
        import("target.action.install")(target, {
            installdir = "build/Release",
            libdir = moduledir,
            bindir = path.join("lib", moduledir),
            includedir = path.join("include", moduledir)})
    end)

demo for https://github.com/tonyfettes/coc-rime add node-api-headers to devDependencies:

-- luacheck: ignore 113
---@diagnostic disable: undefined-global
add_rules("mode.debug", "mode.release")

add_requires("rime")

rule("nodejs.module")
    on_load(function (target)

        -- imports
        import("core.cache.detectcache")
        import("core.project.target", {alias = "project_target"})

        -- set kind
        if target:is_plat("macosx") then
            target:set("kind", "binary")
            target:add("ldflags", "-bundle", "-undefined dynamic_lookup", {force = true})
        else
            target:set("kind", "shared")
        end

        -- set library name
        local modulename = target:name():split('.', {plain = true})
        modulename = modulename[#modulename]
        target:set("filename", modulename .. ".node")

        -- export symbols
        if target:is_plat("windows") then
            local exported_name = target:name():gsub("%.", "_")
            exported_name = exported_name:match('^[^%-]+%-(.+)$') or exported_name
            target:add("shflags", "/export:napi_register_module_v1", {force = true})
        else
            target:set("symbols", "none")
        end

        -- add node library
        local has_node = false
        local includedirs = get_config("includedirs") -- pass node library from nodejs/xmake.lua
        if includedirs and includedirs:find("node-api-headers", 1, true) then
            has_node = true
        end
        if not has_node then
            -- user use `add_requires/add_packages` to add node package
            for _, pkg in ipairs(target:get("packages")) do
                if pkg == "node-api-headers" then
                    has_node = true
                    break
                end
            end
        end
        if not has_node then
            target:add(find_package("node-api-headers"))
        end
    end)
    on_install(function (target)
        if target:is_plat("macosx") then
            target:set("kind", "shared")
        end
        local moduledir = path.directory((target:name():gsub('%.', '/')))
        import("target.action.install")(target, {
            installdir = "build/Release",
            libdir = moduledir,
            bindir = path.join("lib", moduledir),
            includedir = path.join("include", moduledir)})
    end)

target("rime")
do
    add_rules("nodejs.module")
    add_packages("rime")
    add_includedirs("node_modules/node-addon-api", "node_modules/node-api-headers/include")
    add_files("*.cc")
end

We still miss

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant