-
Notifications
You must be signed in to change notification settings - Fork 461
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
Missing module in Init #449
Comments
If I remember correctly there was a discussion around including the module or not and the the decision was to drop the module from the parameters in order to avoid tying N-API to the Node.js module implementation. It sounds like the main thing you want to figure out is the path as the name itself you probably can already figure out. Right? What are you using the path for? |
@mhdawson Thank you Michael. But module can give me access to the require function as well as other symbols. The problem in dropping support for Module is the inability to easily migrate the addon. I substantially stopped at the very beginning, when writing the Initialization function.... it is so frustrating... |
@raffaeler sorry to hear its been a bad start. I wish I better remembered the discussion as I know the concern was raised about not having access to the module and in the end there was an answer as to why it was thought it would not be an issue. @digitalinfinity any chance you remember? What other info do you need from the module for the addon? |
My codebase currently retrieves FYI I need a solution that works both in a plain |
@raffaeler another reason we dropped support for const addon = require('./build/Release/addon');
...
module.exports = addon; So, if your addon also uses the const path = require('path');
const addonPath = path.join('.', 'build', 'Release', 'addon.node');
const addon = require(addonPath);
addon.initialize(addon, path.resolve(path.join(__dirname, addonPath)));
// Now `addon` is ready for use. On the C++ side, you would make the following change: From: namespace {
Napi::Value Init(Napi::Env env, Napi::Object exports) {
exports["doSomething"] = Napi::Function::New(env, DoSomething);
exports["doSomethingElse"] = Napi::Function::New(env, DoSomethingElse);
return exports;
}
} // end of anonymous namespace
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init) To: namespace {
Napi::Value Initialize(const Napi::CallbackInfo& info) {
Napi::Object exports = info[0].As<Napi::Object>();
Napi::String configPath = info[1].As<Napi::String>();
// Do something with `configPath`.
exports["doSomething"] = Napi::Function::New(env, DoSomething);
exports["doSomethingElse"] = Napi::Function::New(env, DoSomethingElse);
}
Napi::Value Init(Napi::Env env, Napi::Object exports) {
exports["initialize"] = Napi::Function::New(env, Initialize);
return exports;
}
} // end of anonymous namespace
NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init) |
@gabrielschulhof I don't get your solution.
Thanks to the legacy API (and using I don't want to give for granted the positions on the hard disks as I found that they directories are treated differently from Electron and this can be true also for other apps/frameworks. FYI Once I will get over this problem, I am still not sure to be able to migrate to the new API as I need to enqueue calls to the main node thread with libuv. I am not sure I will find all the API I need. |
If I understand your question correctly, I'd believe Thread Safe Function is what you expected. |
@raffaeler can you embed the configuration file into the native addon as part of the build process? You can use the N-API thread-safe function to call a JavaScript function from any thread. Although the documentation marks it as experimental, we have PRs on the way to mark it as stable. There's an example that illustrates its use. |
@legendecas @gabrielschulhof Thank you. I currently manage my own functions queue. When an event handler of any asynchronous function on the CLR side completes, I marshal the results on the Node/C++ side and enqueue the results in my queue. Then, on the main libuv thread, I get the bucket, map it to a function and invoke it. @gabrielschulhof With regards to the configuration, I cannot embed it for two reasons:
|
@raffaeler from your description of the "functions queue" it sounds like exactly what the thread-safe function does. May I ask how you provide the native addon? Do you provide it as an npm package? Given that you said you did not control the JavaScript that loads the addon it sounds like you're providing the binary only, and instructing your users to place the configuration file into the same directory as the binary. If this is the case, would it be possible to instruct your users to henceforth place the configuration file in a different location? The reason I ask is that with libuv you are able to retrieve the user's home directory using size_t os_homedir_length = 1;
char one_byte;
char* os_homedir = nullptr;
int result = uv_os_homedir(&one_byte, &os_homedir_length);
os_homedir = new char[os_homedir_length];
result = uv_os_homedir(os_homedir, &os_homedir_length);
assert(result == 0);
std::string platform = env.Global()
.Get("process").As<Napi::Object>()
.Get("platform").As<Napi::String>();
char path_sep = platform.compare("win32") == 0 ? '\\' : '/';
std::string config_file_path =
std::string(os_homedir) + path_sep + ".my-module.conf";
delete[] os_homedir;
// Now you can use config_file_path |
@gabrielschulhof thanks for the answer. I will investigate the threading function as soon as I solve the more basic problems. Yes, the addon is provided via npm but since it can be used in multiple apps, it would not be wise to use the user's home directory. The configuration file must be kept app-specific. It looks very strange not to have the ability to know where the addon binary is currently stored. I really don't get the reason for this decision. |
@raffaeler if the addon is provided via npm and consumed by the various applications as Each app that uses the addon would then install its own copy with its own config file. If the package is installed globally using |
@gabrielschulhof If the user decides to install it globally, I (and the user) expect the settings are shared too (which is fine if this is what the user wants). With regards to the index.js, it is an additional step just to receive the module information, which is something that existed before N-API ... isn't this a clear sign of a missing/forgot feature? Instead of adding a js file, I will probably end up in taking the project root folder (process.mainModule) and scan all the directories but this is a terrible and slow solution. |
I wanted to understand what was needed from the module directory and why they are needed. From what I parse from the discussion the main gap is the ability to get the path for the module. @gabrielschulhof we should be able to grab the path as part of the module creation and potentially stash in the env, then expose through something like napi_get_addon_path. @gabrielschulhof does that sound reasonable to you or are there problems/concerns that you can think of? @raffaeler even if we do that it may only be for the latest node versions so might only be a longer term solution. |
@mhdawson thanks for the answer Michael, I was afraid to receive such an answer.
With regards to my project: what are the paths in Env I can assume are absolutely and always available? (even when Electron is playing)
Do you have any other suggestions? P.S. I currently wrote a ton of C++ code probing all the folders and it works, but I am not sure it can work in any environment. |
@mhdawson adding stuff to
@raffaeler we intentionally didn't create a one-to-one mapping of pre-existing API to N-API. Another consequence, had we sent the I acknowledge that this means that, in the case of your package, you would have no choice but to change the way it initializes itself. To that end I created https://gist.github.com/gabrielschulhof/df079c9a89fe05dfa740b7171e0aa760 to illustrate the way in which I would convey the addon's absolute path into the addon, formulating the whole thing as an npm package that provides the addon to its dependents. Can you please go over the code and point out the ways in which it would fail under the circumstances in which your addon is being used? |
@gabrielschulhof Thanks for the example, but I want to exclude any requirement in terms of a javascript initializer as the initial require can also be made from another native addon. I really don't understand why I can 'expand' I am not asking to restore the Module object but just an addon specific object containing the information about the module itself. Going back to my previous comment, the documentation doesn't help in telling me what are the paths that:
For example:
|
An additional consideration. I remember to have seen different results (even in javascript) when the addon is packed from
my 2 cents |
@raffaeler Node.js wraps CommonJS modules in the following: (function (exports, require, module, __filename, __dirname) {
// original contents of JS file
}); and then passes AFAIK it does no such thing for native modules, and also AFAIK there is no way to retrieve values of variables that are in scope. I guess it's setting I have to say that I'm amazed that there is code out there that loads one native addon from another native addon! Thanks for bringing up this use case! How exactly is that accomplished? Is it done by acquiring and then calling an instance of the We should definitely consider formalizing some values we make available to native modules, but we should do so while keeping in mind the diverse nature of the environments in which these modules might be loaded. |
We also need to keep in mind that |
@gabrielschulhof got it, now I get why
I expect my addon can be loaded from other native addons. On my side I did it in other addons by getting Said that, my addon do load other dlls shipped as part of the addon package (no need for
That would be great. Glad to have contributed to that. |
Hey! Can you unmute me? I joined by phone. |
@gabrielschulhof I can't ... you only can do it via web interface. Sorry |
@raffaeler @gabrielschulhof we've discussed in the recent N-API team meetings and @gabrielschulhof is trying out some different options and will post an update once he's done that. |
@gabrielschulhof The fact that the addon initializer returns a single |
@argv-minus-one the intention with N-API was to get bindings into JS somehow and let JS impose its abstractions, like CommonJS or ESM, on top. The simplest way to achieve that was to transmit one The problem in this case arises from the fact that the native addon is the package entry point, rather than a script. |
We've discussed this and don't see any good options that fit with the abstractions we want to preserve in the API. Since there are alternatives which covers most of the use case, at this point unless there are more requests/cases where it blocks adoption its not in the work we are planning. Alternatives include:
|
Hi @mhdawson Let's see the issue from another perspective. Thanks |
@raffaeler I have an idea how you can get your add-on's path from the address of a symbol inside your add-on, however, it's platform-dependent. What kinds of platforms do you need to support? |
@gabrielschulhof frankly at this stage I can't predict them. For sure Windows and Linux, but since my addon is a kind of "glue" between nodejs and .NET, it can potentially be used anywhere. BTW by spying file activities I see a lot of addons making deep search through various folder to find themselves. This means that many others have the same need but just didn't ask for it. Making this search leads to poor performance and can potentially break very easily. |
@raffaeler do you have that list of other modules that you can share? |
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. Fixes: nodejs/node-addon-api#449
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. Fixes: nodejs/node-addon-api#449
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. Fixes: nodejs/node-addon-api#449
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. Fixes: nodejs/node-addon-api#449 PR-URL: nodejs#37195
I didn't save a list @mhdawson. I installed (and later removed) a bunch of addons to try understanding how could they get the correct folders. Using Windows utility procmon I discovered they were just trying probing entire trees. BTW if I remember well, not directly related, but node-gyp itself probes debug, release and other folders to search for the native addon just because there is no info about the directory structure |
@raffaeler it's not node-gyp that does that but bindings. However, since But check out nodejs/node#37195 – let's see how it goes 🤷 |
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. Fixes: nodejs/node-addon-api#449 PR-URL: nodejs#37195
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. Fixes: nodejs/node-addon-api#449 PR-URL: nodejs#37195
Great work @gabrielschulhof |
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. As an attempt at future-proofing, the file name is provided as a URL, i.e. prefixed with the `file://` protocol. Fixes: nodejs/node-addon-api#449 PR-URL: nodejs#37195 Co-authored-by: Michael Dawson <mdawson@devrus.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. As an attempt at future-proofing, the file name is provided as a URL, i.e. prefixed with the `file://` protocol. Fixes: nodejs/node-addon-api#449 PR-URL: nodejs#37195 Co-authored-by: Michael Dawson <mdawson@devrus.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. As an attempt at future-proofing, the file name is provided as a URL, i.e. prefixed with the `file://` protocol. Fixes: nodejs/node-addon-api#449 PR-URL: nodejs#37195 Co-authored-by: Michael Dawson <mdawson@devrus.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. As an attempt at future-proofing, the file name is provided as a URL, i.e. prefixed with the `file://` protocol. Fixes: nodejs/node-addon-api#449 PR-URL: #37195 Co-authored-by: Michael Dawson <mdawson@devrus.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. As an attempt at future-proofing, the file name is provided as a URL, i.e. prefixed with the `file://` protocol. Fixes: nodejs/node-addon-api#449 PR-URL: nodejs#37195 Backport-PR-URL: nodejs#37328 Co-authored-by: Michael Dawson <mdawson@devrus.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. As an attempt at future-proofing, the file name is provided as a URL, i.e. prefixed with the `file://` protocol. Fixes: nodejs/node-addon-api#449 PR-URL: nodejs#37195 Co-authored-by: Michael Dawson <mdawson@devrus.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. As an attempt at future-proofing, the file name is provided as a URL, i.e. prefixed with the `file://` protocol. Fixes: nodejs/node-addon-api#449 PR-URL: nodejs#37195 Co-authored-by: Michael Dawson <mdawson@devrus.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. As an attempt at future-proofing, the file name is provided as a URL, i.e. prefixed with the `file://` protocol. Fixes: nodejs/node-addon-api#449 PR-URL: #37195 Backport-PR-URL: #37327 Co-authored-by: Michael Dawson <mdawson@devrus.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. As an attempt at future-proofing, the file name is provided as a URL, i.e. prefixed with the `file://` protocol. Fixes: nodejs/node-addon-api#449 PR-URL: #37195 Backport-PR-URL: #37327 Co-authored-by: Michael Dawson <mdawson@devrus.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. As an attempt at future-proofing, the file name is provided as a URL, i.e. prefixed with the `file://` protocol. Fixes: nodejs/node-addon-api#449 PR-URL: #37195 Backport-PR-URL: #37327 Co-authored-by: Michael Dawson <mdawson@devrus.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
Unlike JS-only modules, native add-ons are always associated with a dynamic shared object from which they are loaded. Being able to retrieve its absolute path is important to native-only add-ons, i.e. add-ons that are not themselves being loaded from a JS-only module located in the same package as the native add-on itself. Currently, the file name is obtained at environment construction time from the JS `module.filename`. Nevertheless, the presence of `module` is not required, because the file name could also be passed in via a private property added onto `exports` from the `process.dlopen` binding. As an attempt at future-proofing, the file name is provided as a URL, i.e. prefixed with the `file://` protocol. Fixes: nodejs/node-addon-api#449 PR-URL: nodejs#37195 Backport-PR-URL: nodejs#37327 Co-authored-by: Michael Dawson <mdawson@devrus.com> Reviewed-By: Michael Dawson <midawson@redhat.com>
With the legacy api, I could obtain the addon module using this code:
I need the module to obtain the addon filename (and folder) using this code:
How can I get the module and the filename at initialization time now?
The text was updated successfully, but these errors were encountered: