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

need help with creating node.js binding for tvision #27

Open
unxed opened this issue Oct 30, 2020 · 15 comments
Open

need help with creating node.js binding for tvision #27

unxed opened this issue Oct 30, 2020 · 15 comments

Comments

@unxed
Copy link
Contributor

unxed commented Oct 30, 2020

Hello! I've just experimenting with node.js binding for tvision. This tool has the possibility to simplify such work:
https://github.com/charto/nbind

I followed this tutorial (replacing vg with tvision):
https://github.com/charto/nbind/blob/master/doc/vg-tutorial.md

Made simple demo js_tvision.cpp:

#include "../tvision/include/tvision/tv.h"
#include "nbind/api.h"

#include "nbind/nbind.h"


namespace tvision {

NBIND_CLASS(TApplication) {

  // Add an empty constructor
  construct<>();

  method(run);
}

}

Unfortunately, npm run -- node-gyp configure build step fails with the following error:

../../tvision/include/tvision/tv.h:634:10: fatal error: tvision/config.h: No such file or directory
  634 | #include <tvision/config.h>
      |          ^~~~~~~~~~~~~~~~~~
compilation terminated.

Please suggest the direction of further work! Thanks!

PS: There is a huge demand in the world of node.js for a powerful TUI framework. Take a look at this module and the number of its forks:
https://www.npmjs.com/package/blessed
https://www.npmjs.com/package/neo-blessed
https://www.npmjs.com/package/post-blessed
But its capabilities are far from those of Turbo Vision, and its usability is far from perfect. My dream is to be able to do "npm install tvision", and use all TV power to create TUI interfaces for cli node.js applications.

@magiblot
Copy link
Owner

Hi unxed.

The build process is clearly missing include directories.

Take a look at this issue: charto/nbind#35. They seem to solve it by providing compilation flags in environment variables or in the .gyp file.

So, for the error above, you would need something like -I../../tvision/include.

My first impression anyway is that porting Turbo Vision this way is going to be painful, because of differences between C++ and JavaScript. JavaScript is a highly dynamic language with extremely useful features such as asynchronism. So, for instance, it does not make sense to sleep the process while waiting for user input, which Turbo Vision in C++ does. Strings in JavaScript are UTF-16 while Turbo Vision expects UTF-8, JavaScript has garbage collection instead of RAII, etc. Therefore I think it would be best to port most of Turbo Vision to native JavaScript, instead of having to keep working around the differences between both languages.

But I'm just being pessimistic, thanks for trying and let's see how your attempt goes :)

@unxed
Copy link
Contributor Author

unxed commented Oct 30, 2020

Thanks for your advice, it helped me to move further. I'll leave here my project/node_modules/nbind/src/nbind.gypi that helped:

{

	"variables": {
		"asmjs%": 0
	},

	"target_name": "nbind",
	"type": "loadable_module",
	"sources": [
		"common.cc",
		"reflect.cc"
	],
	"include_dirs": [
		"../include"
	],

	"conditions": [
		['asmjs==1', {

			"product_name": "nbind.js",
			"type":         "executable",
			"sources":    [ "em/Binding.cc" ],
			"ldflags":    [ "<@(_cflags)" ],

			"copies": [{"destination": "<(INTERMEDIATE_DIR)", "files": ["pre.js", "post.js", "../dist/em-api.js"]}],
			"prejs_path": "<(INTERMEDIATE_DIR)/pre.js",
			"postjs_path": "<(INTERMEDIATE_DIR)/post.js",
			"jslib_path": "<(INTERMEDIATE_DIR)/em-api.js",

			"cflags": [
				"-O3",
				"--pre-js", "<(_prejs_path)",
				"--post-js", "<(_postjs_path)",
				"--js-library", "<(_jslib_path)",
                " -I../../tvision/include",
				"-s", "NO_FILESYSTEM=1",
				"-s", "EXPORTED_FUNCTIONS=[\"_nbind_init\",\"_nbind_value\"]",
				"-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[\"nbind_value\",\"\\$$Browser\"]"
			],

			"cflags_cc": [
				"-std=c++17",
				"-fno-exceptions",
                "-I../../tvision/include"
			],

			"xcode_settings": {
				"GCC_GENERATE_DEBUGGING_SYMBOLS": "NO",
				"OTHER_CFLAGS": [ "<@(_cflags)" ],
				"OTHER_CPLUSPLUSFLAGS": [ "<@(_cflags_cc)" ],
				"OTHER_LDFLAGS": [ "<@(_cflags)" ]
			}

		}, {

			"copies": [{"destination": "<(INTERMEDIATE_DIR)", "files": ["symbols.txt"]}],
			"symbols_path": "<(INTERMEDIATE_DIR)/symbols.txt",

			"sources": [
				"v8/Buffer.cc",
				"v8/Binding.cc"
			],

			"cflags": [
				"-O3",
				"-fPIC",
                " -I../../tvision/include"
			],

			"cflags_cc": [
				"-std=c++17",
				"-fexceptions",
				"-fPIC",
                " -I../../tvision/include"
			],

			"msbuild_settings": {
				"ClCompile": {
					"RuntimeTypeInfo": "false",
					"ExceptionHandling": "Sync", # /EHsc
					"MultiProcessorCompilation": "true"
				}
			},

			"xcode_settings": {
				"GCC_ENABLE_CPP_EXCEPTIONS": "YES",
				"CLANG_CXX_LANGUAGE_STANDARD": "c++11",
				"MACOSX_DEPLOYMENT_TARGET": "10.7",
				"OTHER_CFLAGS": [ "<@(_cflags)" ],
				"OTHER_CPLUSPLUSFLAGS": [
					"<@(_cflags_cc)",
					"-stdlib=libc++"
				],
				"OTHER_LDFLAGS": [
					"-stdlib=libc++",
					"-exported_symbols_list", "<(_symbols_path)"
				]
			}

		}]
	]
}

Added "-I"s and changed c++ version from 11 to 17.

@magiblot
Copy link
Owner

magiblot commented Oct 30, 2020

It's a C++11 error. Try to set -std=c++17 or -std=c++14.

EDIT: Oh but, you already tried. Although I can still see C++11 in one of the configuration variables. Does changing that one solve the issue?

@unxed
Copy link
Contributor Author

unxed commented Oct 30, 2020

It's a C++11 error. Try to set -std=c++17 or -std=c++14.

Thanks, already guessed. It helps!
How I have:

../node_modules/nbind/include/nbind/noconflict.h:56:33: error: no matching function for call to ‘nbind::BindDefiner<TApplication>::metho
d(const char [4], void (TProgram::*)())’
   56 | #define NBIND_METHOD(name, ...) definer.method(#name, &Bound::name, ## __VA_ARGS__)
      |                                 ^~~~~~~
../node_modules/nbind/include/nbind/noconflict.h:15:28: note: in definition of macro ‘NBIND_EXPAND’
   15 | #define NBIND_EXPAND(args) args
      |                            ^~~~
../node_modules/nbind/include/nbind/nbind.h:12:34: note: in expansion of macro ‘NBIND_METHOD’
   12 | #define method(...) NBIND_EXPAND(NBIND_METHOD(__VA_ARGS__))
      |                                  ^~~~~~~~~~~~
../js_tvision.cpp:29:3: note: in expansion of macro ‘method’
   29 |   method(run);
      |   ^~~~~~

../node_modules/nbind/include/nbind/ArgStorage.h:43:3: error: ‘TApplication::TApplication()’ is protected within this context
   43 |   ::new(&data) Bound(std::forward<Args>(args)...);
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/usr/include/c++/9/ext/new_allocator.h:145:20: error: ‘TApplication::TApplication()’ is protected within this context
  145 |  noexcept(noexcept(::new((void *)__p)
      |                    ^~~~~~~~~~~~~~~~~~
  146 |        _Up(std::forward<_Args>(__args)...)))
      |        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/usr/include/c++/9/ext/new_allocator.h:151:2: error: ‘virtual TApplication::~TApplication()’ is protected within this context
  151 |  destroy(_Up* __p)
      |  ^~~~~~~

@unxed
Copy link
Contributor Author

unxed commented Oct 30, 2020

EDIT: Oh but, you already tried. Although I can still see C++11 in one of the configuration variables. Does changing that one solve the issue?

xcode block is for MacOS AFAIK, and I'm building from Linux Mint

@magiblot
Copy link
Owner

TApplication is expected to be inherited instead of being used directly, so its constructors are protected. You will have to create a subclass of TApplication class with public constructors. You can see an example of this in the hello.cpp program.

@magiblot
Copy link
Owner

Now excuse me, I have to go and I won't be able to answer you immediately. Good luck!

@unxed
Copy link
Contributor Author

unxed commented Nov 1, 2020

The last recommendation helped, it finally builds, thanks!

Still fails to run with

$ ./test.js                                                                       ↑
internal/modules/cjs/loader.js:807
  return process.dlopen(module, path.toNamespacedPath(filename));
                 ^

Error: /home/unxed/far2l/!!next/!!node-cli/nbind/git/project/build/Release/nbind.node: undefined symbol: _ZN8TProgram7deskTopE
    at Object.Module._extensions..node (internal/modules/cjs/loader.js:807:18)
    at Module.load (internal/modules/cjs/loader.js:653:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
    at Function.Module._load (internal/modules/cjs/loader.js:585:3)
    at Module.require (internal/modules/cjs/loader.js:692:17)
    at require (internal/modules/cjs/helpers.js:25:18)
    at initNode (/home/unxed/far2l/!!next/!!node-cli/nbind/git/project/node_modules/nbind/dist/nbind.js:141:15)
    at /home/unxed/far2l/!!next/!!node-cli/nbind/git/project/node_modules/nbind/dist/nbind.js:115:13
    at findCompiledModule (/home/unxed/far2l/!!next/!!node-cli/nbind/git/project/node_modules/nbind/dist/nbind.js:79:13)
    at find (/home/unxed/far2l/!!next/!!node-cli/nbind/git/project/node_modules/nbind/dist/nbind.js:93:13)

Possibly I should ask nbind guys about this?

@magiblot
Copy link
Owner

magiblot commented Nov 1, 2020

My guess is that the issue is Turbo Vision being a static library, so the compiler optimizes out unused symbols. Try replacing STATIC with SHARED in CMakeLists.txt, link the module against libtvision.so and add the path containing libtvision.so to LD_LIBRARY_PATH so that the dynamic linker can find it.

@unxed
Copy link
Contributor Author

unxed commented Nov 1, 2020

nbind builds it's own custom version of libtvision.so with it's own additional routines included. I guess if I give it .so build by myself without all that stuff it is hardly to work correctly.

@magiblot
Copy link
Owner

magiblot commented Nov 1, 2020

Could it be that the libtvision.so generated by nbind only contains the code of your entry .cpp file and has not been linked against libtvision.a?

@unxed
Copy link
Contributor Author

unxed commented Nov 1, 2020

Sorry to deceive you, its js_tvision.o that nbind creates, not libtvision.o.
And its for sure contains not all required code.

It looks like, in addition to specifying the headers path, it is necessary to somehow explain to nbind how to build the turbo vision itself. AFAIK, node modules should be built using just the same compiler flags as node, so its enough just to provide node with my own libtvision.so.

@magiblot
Copy link
Owner

magiblot commented Nov 1, 2020

nbind cannot be asked to build Turbo Vision, because it doesn't know how to. Only CMake knows how to build Turbo Vision.
So what has to be done is build Turbo Vision with CMake, and then ask nbind to link against the libtvision.a generated by CMake (ideally, the same compiler and C++ version should be used for both Turbo Vision and js_tvision).

When compiling from the command line, libtvision.a is linked in just by placing it in the arguments list next to the source code files:

g++ -std=c++17 -o hello hello.cpp ./build/lib/libtvision.a -Iinclude -lncursesw -lgpm

So maybe for nbind you only have to list it as another source file.

@sirlordt
Copy link

Hello! I've just experimenting with node.js binding for tvision. This tool has the possibility to simplify such work:
https://github.com/charto/nbind

I followed this tutorial (replacing vg with tvision):
https://github.com/charto/nbind/blob/master/doc/vg-tutorial.md

Made simple demo js_tvision.cpp:

#include "../tvision/include/tvision/tv.h"
#include "nbind/api.h"

#include "nbind/nbind.h"


namespace tvision {

NBIND_CLASS(TApplication) {

  // Add an empty constructor
  construct<>();

  method(run);
}

}

Unfortunately, npm run -- node-gyp configure build step fails with the following error:

../../tvision/include/tvision/tv.h:634:10: fatal error: tvision/config.h: No such file or directory
  634 | #include <tvision/config.h>
      |          ^~~~~~~~~~~~~~~~~~
compilation terminated.

Please suggest the direction of further work! Thanks!

PS: There is a huge demand in the world of node.js for a powerful TUI framework. Take a look at this module and the number of its forks:
https://www.npmjs.com/package/blessed
https://www.npmjs.com/package/neo-blessed
https://www.npmjs.com/package/post-blessed
But its capabilities are far from those of Turbo Vision, and its usability is far from perfect. My dream is to be able to do "npm install tvision", and use all TV power to create TUI interfaces for cli node.js applications.

Hello. Any progress in this issue?

@magiblot
Copy link
Owner

Hola Tomás,

There's probably not been much progress, given that unxed did not even get an aswer to charto/nbind#139.

I do not have high expectations about it, though. Considering that the Turbo Vision API requires the programmer to use inheritance, I suspect a port based on native bindings won't be very usable, or at least it will be harder to get working than just writing the library directly in JavaScript.

Additionally, JavaScript has first-class functions and coroutines, asynchronous I/O, prototype-based inheritance... It would be very hard for native bindings to take advantage of any of these.

Saludos.

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

No branches or pull requests

3 participants