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

Add CGO MakeFile #71

Merged
merged 1 commit into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ main
Tunnel.framework
tunnel.aar
tunnel-sources.jar
*.tar.gz
41 changes: 41 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ BUILD=go build -ldflags "-s -w -X main.Version=$(VERSION)"
BUILD_DIR=build
BIN_NAME=nkn-tunnel
MAIN=bin/main.go
LIB_NAME:=libnkntunnel
LIB_SRC_FILE:=lib/libnkntunnel.go
LIB_BUILD_DIR:=$(BUILD_DIR)/lib

ifdef GOARM
BIN_DIR=$(GOOS)-$(GOARCH)v$(GOARM)
else
Expand Down Expand Up @@ -65,3 +69,40 @@ android:
gomobile bind -target=android -ldflags "-s -w" github.com/nknorg/nkn-tunnel github.com/nknorg/nkn-tuna-session github.com/nknorg/ncp-go github.com/nknorg/tuna github.com/nknorg/nkn-sdk-go github.com/nknorg/nkngomobile
mv tunnel.aar tunnel-sources.jar $(BUILD_DIR)/android/
${MAKE} zip BIN_DIR=android

.PHONY: lib
lib:
rm -rf $(BUILD_DIR)/lib
mkdir -p $(BUILD_DIR)/lib

for target in \
"darwin arm64 darwin-arm64 .dylib clang" \
"windows amd64 win-amd64 .dll x86_64-w64-mingw32-gcc " \
"linux amd64 linux-amd64 .so x86_64-linux-musl-gcc"; \
do \
set -- $$target; \
GOOS=$$1 GOARCH=$$2 PLATFORM=$$3 EXT=$$4 CC=$$5; \
echo "Building for $$GOOS/$$GOARCH..."; \
BUILD_OUTPUT=$(LIB_BUILD_DIR)/$$GOOS_$$PLATFORM/$(LIB_NAME)$$EXT; \
mkdir -p $(dir $$BUILD_OUTPUT); \
CGO_ENABLED=1 GOOS=$$GOOS GOARCH=$$GOARCH CC=$$CC go build -buildmode=c-shared \
-ldflags "-s -w -X main.Version=$(VERSION)" \
-o $$BUILD_OUTPUT $(LIB_SRC_FILE); \
if [ $$? -ne 0 ]; then \
echo "Failed to build $$GOOS/$$GOARCH"; \
exit 1; \
fi; \
echo "Successfully built $$GOOS/$$GOARCH"; \
done

CGO_ENABLED=1 GOOS=darwin GOARCH=arm64 CC=clang go build -buildmode=c-archive -ldflags "-s -w -X main.Version=$(VERSION)" -o $(LIB_BUILD_DIR)/ios-arm64/$(LIB_NAME).a $(LIB_SRC_FILE)
CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 CC=clang go build -buildmode=c-archive -ldflags "-s -w -X main.Version=$(VERSION)" -o $(LIB_BUILD_DIR)/ios-amd64/$(LIB_NAME).a $(LIB_SRC_FILE)
mkdir -p $(LIB_BUILD_DIR)/ios
lipo -create -output $(LIB_BUILD_DIR)/ios/libnkntunnel.a $(LIB_BUILD_DIR)/ios-amd64/libnkntunnel.a $(LIB_BUILD_DIR)/ios-arm64/libnkntunnel.a
cp $(LIB_BUILD_DIR)/ios-arm64/$(LIB_NAME).h $(LIB_BUILD_DIR)/ios/$(LIB_NAME).h
@echo "All platforms built successfully. Output in $(LIB_BUILD_DIR)/"

.PHONY: package_lib
package_lib: lib
cd $(BUILD_DIR) && rm -f lib.tar.gz && tar -czf lib.tar.gz lib
@echo "Library package created: $(BUILD_DIR)/lib.tar.gz"
95 changes: 95 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,98 @@ git commit -s
- [Telegram](https://t.me/nknorg)
- [Reddit](https://www.reddit.com/r/nknblockchain/)
- [Twitter](https://twitter.com/NKN_ORG)

## Building Dynamic and Static Libraries

```shell
make lib
```

### Build Targets

The `make lib` target builds shared libraries (dynamic libraries) and static libraries for the following platforms:

* macOS: `.dylib` and `.a`
* Windows: `.dll`
* Linux: `.so`
* iOS: `.a`

All generated files are stored in the `build/lib` directory.

### Prerequisites

1. Required Tools
Ensure the following tools are installed on your system:

* go (version >= 1.20)
* clang (for macOS and iOS builds)
* x86_64-w64-mingw32-gcc (for Windows builds)
* x86_64-linux-musl-gcc (for Linux builds)
* lipo (for merging iOS static libraries)

2. Environment Setup

* Ensure make and related tools are in your PATH.
* Set GOPATH and GOROOT environment variables appropriately.

> Builds shared libraries (c-shared) for the following platforms:

* macOS (arm64): .dylib
* Windows (amd64): .dll
* Linux (amd64): .so

> Builds static libraries (c-archive) for the following platforms:

* iOS (arm64 and amd64): .a

### Generated File Structure

After a successful build, the output files are organized as follows:

```
build/lib/
├── darwin-arm64/
│ ├── libnkntunnel.dylib
│ └── libnkntunnel.h
├── ios/
│ ├── libnkntunnel.a
│ └── libnkntunnel.h
├── ios-arm64/
│ ├── libnkntunnel.a
│ └── libnkntunnel.h
├── ios-amd64/
│ ├── libnkntunnel.a
│ └── libnkntunnel.h
├── linux-amd64/
│ ├── libnkntunnel.so
│ └── libnkntunnel.h
├── win-amd64/
│ ├── libnkntunnel.dll
│ └── libnkntunnel.h
└── ...
```

## Common Issues and Solutions

1. Build Fails: Missing Compiler

* Ensure the following compilers are installed:
* clang (for macOS and iOS builds)
* x86_64-w64-mingw32-gcc (for Windows builds)
* x86_64-linux-musl-gcc (for Linux builds)

2. Error: library not found

* Ensure all Go module dependencies are installed:

```shell
go mod tidy
```

3. `lipo` Command Not Found

* On macOS, ensure Xcode is installed, and the correct developer tools are selected:

```shell
xcode-select --install
```
196 changes: 196 additions & 0 deletions lib/libnkntunnel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package main

/*
#include <stdlib.h>
*/
import "C"
import (
"encoding/hex"
"github.com/nknorg/ncp-go"
"github.com/nknorg/nkn-sdk-go"
ts "github.com/nknorg/nkn-tuna-session"
tunnel "github.com/nknorg/nkn-tunnel"
"github.com/nknorg/nkngomobile"
"log"
"os"
"strings"
"sync"
)

var (
instanceTunnel *tunnel.Tunnel
tunnelMutex sync.Mutex
logMutex sync.Mutex

logFilePath string
logFile *os.File
logToFile bool

DefaultTunaMaxPrice = "0.01"
DefaultTunaMinFee = "0.00001"
DefaultTunaFeeRatio = 0.1
)

func initLogger() error {
if logFilePath == "" {
logToFile = false
log.SetOutput(os.Stdout)
return nil
}

var err error
logFile, err = os.OpenFile(logFilePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
if err != nil {
logToFile = false
log.SetOutput(os.Stdout)
log.Println("Failed to open log file, defaulting to stdout:", err)
return err
}

log.SetOutput(logFile)
logToFile = true
log.Println("Log initialized, writing to file:", logFilePath)
return nil
}

func closeLogger() {
logMutex.Lock()
defer logMutex.Unlock()

if logFile != nil {
log.Println("Closing log file")
logFile.Close()
logFile = nil
logToFile = false
}
}

//export SetLogFilePath
func SetLogFilePath(path *C.char) {
logMutex.Lock()
defer logMutex.Unlock()

logFilePath = C.GoString(path)
initLogger()
}

//export StartNknTunnel
func StartNknTunnel(numClients C.int, seedRpcServers *C.char, seedHex *C.char, identifier *C.char, from *C.char, to *C.char, udp C.int, useTuna C.int, tunaMaxPrice *C.char, tunaMinFee *C.char, tunaFeeRatio C.float, verbose C.int) C.int {
tunnelMutex.Lock()
defer tunnelMutex.Unlock()

if instanceTunnel != nil {
log.Println("Closing existing tunnel before starting a new one...")
instanceTunnel.Close()
instanceTunnel = nil
}

numClientsGo := int(numClients)
seedRpcServersGo := C.GoString(seedRpcServers)
seedHexGo := C.GoString(seedHex)
identifierGo := C.GoString(identifier)
fromGo := C.GoString(from)
toGo := C.GoString(to)
udpGo := udp != 0
useTunaGo := useTuna != 0
tunaMaxPriceGo := C.GoString(tunaMaxPrice)
tunaMinFeeGo := C.GoString(tunaMinFee)
tunaFeeRatioGo := float64(tunaFeeRatio)
verboseGo := verbose != 0

if seedHexGo == "" {
log.Println("Seed hex cannot be empty")
return 1
}

if tunaMaxPriceGo == "" {
tunaMaxPriceGo = DefaultTunaMaxPrice
}
if tunaMinFeeGo == "" {
tunaMinFeeGo = DefaultTunaMinFee
}
if tunaFeeRatioGo == 0 {
tunaFeeRatioGo = DefaultTunaFeeRatio
}

seedRpcServerList := strings.Split(seedRpcServersGo, ",")
seedRpcServerAddr := nkngomobile.NewStringArray(seedRpcServerList...)

var seed []byte
var err error

seed, err = hex.DecodeString(seedHexGo)
if err != nil {
log.Println("Invalid seed hex: ", err)
return 2
}
account, err := nkn.NewAccount(seed)
if err != nil {
log.Println("Failed to create account:", err)
return 3
}
clientConfig := &nkn.ClientConfig{
SeedRPCServerAddr: seedRpcServerAddr,
}
walletConfig := &nkn.WalletConfig{
SeedRPCServerAddr: seedRpcServerAddr,
}
sessionConfig := &ncp.Config{
MTU: int32(0),
}

var tsConfig *ts.Config
if useTunaGo {
tsConfig = &ts.Config{
NumTunaListeners: numClientsGo,
SessionConfig: sessionConfig,
TunaMaxPrice: tunaMaxPriceGo,
TunaMinNanoPayFee: tunaMinFeeGo,
TunaNanoPayFeeRatio: tunaFeeRatioGo,
}
}

config := &tunnel.Config{
NumSubClients: numClientsGo,
ClientConfig: clientConfig,
WalletConfig: walletConfig,
TunaSessionConfig: tsConfig,
UDP: udpGo,
Verbose: verboseGo,
}
t, err := tunnel.NewTunnel(account, identifierGo, fromGo, toGo, useTunaGo, config, nil)
if err != nil {
log.Println("Failed to create tunnel:", err)
return 4
}

instanceTunnel = t

go func() {
if err := t.Start(); err != nil {
log.Println("Tunnel failed to start:", err)
}
}()
log.Println("Tunnel started successfully")
return 0
}

//export CloseNknTunnel
func CloseNknTunnel() C.int {
tunnelMutex.Lock()
defer tunnelMutex.Unlock()

if instanceTunnel == nil {
log.Println("No tunnel to close")
return -1
}

instanceTunnel.Close()
instanceTunnel = nil
log.Println("Tunnel closed successfully")
return 0
}

func main() {
defer closeLogger()
}
Loading