Skip to content
/ gonacli Public

🖖 This is a development tool that can quickly use Golang to develop and build NodeJS Addon.

License

Notifications You must be signed in to change notification settings

wenlng/gonacli

Repository files navigation

GonaCli

This is a development tool that can quickly use Golang to develop NodeJS Addon.


English | 中文

GONACLI is a development tool that quickly uses Golang to develop NodeJS Addon. You only need to concentrate on the development of Golang, and you don't need to care about the implementation of the bridge layer. It supports JavaScript sync calls and async callbacks.


⭐️ If it helps you, please give a star.


Compatible Support Of Gonacli

  • Linux
  • Mac OS
  • Windows

Compatible Support Of NodeJS Addon

  • Linux / Mac OS / Windows
  • NodeJS(12.0+)
  • Npm(6.0+)
  • Node-gyp(9.0+)
  • Go(1.14+)

Use Golang Install

Ensure that the system is configured with GOPATH environment variables before installation

Linux or Mac OS

# .bash_profile
export GOPATH="/Users/awen/go"
# set bin dir
export PATH="$PATH:$GOPATH:$GOPATH/bin"

Window

# set system path
GOPATH: C:\awen\go
# set bin dir
PATH: %GOPATH%\bin

Install

$ go install github.com/wenlng/gonacli@latest

$ gonacli version

Compilation Of Windows OS Environment

In the Windows OS environment, you need to install the gcc/g++ compiler support required by Go CGO, download the MinGW installation, configure the PATH environment variable of MinGW/bin, and execute gcc normally on the command line.

$ gcc -v

When compiling Node Addon in the Windows OS environment, you also need to install the build tool that node-gyp and depends on.

$ npm install --global node-gyp

$ npm install --global --production windows-build-tools

Gonacli Command

1. generate

Generate bridge code related to NodeJS Addon according to the configuration of goaddon

# By default, it reads the goaddon in the current directory Json configuration file
$ gonacli generate

# --config: Specify Profile
$ gonacli generate --config demoaddon.json

2. build

Same as the go build - buildmode=c-archive command, compile the library

# Compile to generate library
$ gonacli build

# --args: Specify the args of go build
# --config: Specify Profile
$ gonacli build --args '-ldflags "-s -w"'

3. install

Same as the npm install, Install NodeJS dependencies.

# Install dependencies
gonacli install

# --config: Specify Profile
$ gonacli install --config demoaddon.json

4. msvc

Select dlltool.exe in the Windows OS environment or lib.exe

1.dlltool.exe of MinGW

2.lib.exe of Microsoft Visual c++ Build tools or Visual Studio

$ gonacli msvc

# --vs: use VS "lib.exe", default is "dlltool.exe" of MinGW
# --32x: Supports 32-bit OS,default 64.
# --config: Specify Profile
$ gonacli msvc --config demoaddon.json

5. make

Same as the node-gyp configure && node-gyp build command,Compile NodeJS Addon

Please ensure that the node gyp compiler has been installed on the system before using the "make" command

Before using the "--npm-i" arg, ensure that the system has installed the npm package dependency management tool
# --args: Specify the parameters of node-gyp build,for example "--debug"
$ gonacli make --args '--debug'

Use Golang to develop an Demo of NodeJS Addon

Tip:Ensure that relevant commands can be used normally, This is an demo under Linux/OS environment.

# go
$ go version

# node
$ node -v

# npm
$ npm -v

# node-gyp
$ node-gyp -v

1. Create Goaddon Configure File

/goaddon.json

{
  "name": "demoaddon",
  "sources": [
    "demoaddon.go"
  ],
  "output": "./demoaddon/",
  "exports": [
    {
      "name": "Hello",
      "args": [
        {
          "name": "name",
          "type": "string"
        }
      ],
      "returntype": "string",
      "jscallname": "hello",
      "jscallmode": "sync"
    }
  ]
}

2. Write Golang Code

/demoaddon.go

package main
import "C"

// notice://export xxxx is necessary

//export Hello
func Hello(_name *C.char) *C.char {
	// args string type,return string type
	name := C.GoString(_name)
	
	res := "hello"
	if len(name) > 0 {
	    res += "," + name
	}
	
	return C.CString(res)
}

3. Generate Rridging Napi C/C++ Code

# Save to the "./demoaddon/" directory
$ gonacli generate --config ./goaddon.json

4.Compile Libraries

# Save to the "./demoaddon/" directory
$ gonacli build

5. Install Dependencies

# Save to the "./demoaddon/build" directory
$ gonacli install

6. Compile Nodejs Adddon

# Save to the "./demoaddon/build" directory
$ gonacli make

7. Create JS Test File

/demoaddon/test.js

const demoaddon = require('.')

const name = "awen"
const res = demoaddon.hello(name)
console.log('>>> ', res)
$ node ./test.js
# >>> hello, awen

Configure File Description

{
  "name": "demoaddon",      // Name of Nodejs Addon
  "sources": [              // File list of go build,Cannot have path
    "demoaddon.go"
  ],
  "output": "./demoaddon/", // Output directory path
  "exports": [              // Exported interface, generating the Napi and C/C++ code of Addon
    {
      "name": "Hello",      // The name of the "//export Hello" interface corresponding to Golang must be consistent
      "args": [             // The parameter type of the passed parameter list must be consistent with the type table
        {                  
          "name": "name",
          "type": "string"
        }
      ],
      "returntype": "string",   // The type returned to JavaScript,has no callback type
      "jscallname": "hello",    // JavaScript call name
      "jscallmode": "sync"      // Sync is synchronous execution, and Async is asynchronous execution
    },
    {
        name: "xxx",
        ....
    }
  ]
}

Type Table

Type Golang Args Golang Return JS / TS
int int32 C.int number
int32 int32 C.int number
int64 int64 C.longlong number
uint32 uint32 C.uint number
float float32 C.float number
double float64 C.double number
boolean bool bool boolean
string *C.char *C.char string
array *C.char *C.char Array
object *C.char *C.char Object
callback *C.char - Function

The returntype field type of the configuration file

The returntype field has no callback type

array type

When there are multiple levels when returning, it is not recommended to use in the returntype

  1. The array type received in Golang is a string *C.Char type, which needs to be use []interface{} and json.Unmarshal

  2. The array type is when Golang returns *C.Char type, use json.Marshal

  3. The array type is an Array type when JavaScript is passed, but currently only supports one layer when receiving. Please use string method to return multiple layers in Golang, and then use JavaScript's JSON.parse

object type

When there are multiple levels when returning, it is not recommended to use in the returntype

  1. The object type received in Golang is a string type. You need to use [string]interface{} and json.Unmarshal

  2. The object type is when Golang returns *C.Char type, use json.Marshal

  3. The object type is an Object type when JavaScript is passed, but currently only supports one layer when receiving. Please use string method to return multiple layers in Golang, and then use JavaScript's JSON.parse


JavaScript Sync Call

/goaddon.json

{
  "name": "demoaddon",
  "sources": [
    "demoaddon.go"
  ],
  "output": "./demoaddon/",
  "exports": [
    {
      "name": "Hello",
      "args": [
        {
          "name": "name",
          "type": "string"
        }
      ],
      "returntype": "string",
      "jscallname": "hello",
      "jscallmode": "sync"
    }
  ]
}

2. Golang Code

/demoaddon.go

package main
import "C"

//export Hello
func Hello(_name *C.char) *C.char {
	// args is string type,return string type
	name := C.GoString(_name)
	
	res := "hello"
	ch := make(chan bool)

	go func() {
	    // Time consuming task processing
	    time.Sleep(time.Duration(2) * time.Second)
		if len(name) > 0 {
	        res += "," + name
	    }   
		ch <- true
	}()

	<-ch
	
	return C.CString(res)
}

3. Test

/test.js

const demoaddon = require('./demoaddon')

const name = "awen"
const res = demoaddon.hello(name)
console.log('>>> ', res)

JavaScript Async Call

/goaddon.json

{
  "name": "demoaddon",
  "sources": [
    "demoaddon.go"
  ],
  "output": "./demoaddon/",
  "exports": [
    {
      "name": "Hello",
      "args": [
        {
          "name": "name",
          "type": "string"
        },
        {
          "name": "cbs",
          "type": "callback"
        }
      ],
      "returntype": "string",
      "jscallname": "hello",
      "jscallmode": "async"
    }
  ]
}

2. Golang Code

/demoaddon.go

package main
import "C"

//export Hello
func Hello(_name *C.char, cbsFnName *C.char) *C.char {
	// args is string type,return string type
	name := C.GoString(_name)
	
	res := "hello"
	ch := make(chan bool)

    go func() {
	    // Time consuming task processing
		time.Sleep(time.Duration(2) * time.Second)
		if len(name) > 0 {
	        res += "," + name
	    }   
		ch <- true
	}()

	<-ch
	
	return C.CString(res)
}

3. Test

/test.js

const demoaddon = require('./demoaddon')

const name = "awen"
demoaddon.hello(name, funciton(res){
    console.log('>>> ', res)
})

LICENSE

MIT