Using Go inside Wren CLI

Original in Wren Wiki

Wren CLI is the official project for a small command line application that embeds Wren. Serves as an example implementation.

If you want to use the exact version of this tutorial. See this commit.

In this simple exercise we wil…

Original in Wren Wiki

Wren CLI is the official project for a small command line application that embeds Wren. Serves as an example implementation.

If you want to use the exact version of this tutorial. See this commit.

In this simple exercise we will export a go function and use it inside the CLI as a new class.

The function will be a simple Http server that returns a message if we go to localhost:8080



Golang

If you need to install go you can download it from here. (go version go1.16.3 darwin/amd64)

Our go code is really simple.
Based on Golang http server

package main

import "C"

import (
    "io"
    "log"
    "net/http"
)

//export Http
func Http() {
    // Set routing rules
    http.HandleFunc("/", Tmp)

    //Use the default DefaultServeMux.
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
            log.Fatal(err)
    }
}

func Tmp(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "Calling Go functions from Wren in static libs")
}

func main() {}

The main requirements for our go code are:

  • import "C" : Imports the cgo runtime
  • //export Http : Tells the compiler to export a function named Http
  • func main() {}: Is required to export the lib.

If you need more examples you can look here.

Now lets create a new directory and files inside the cli project:

Create a new directory named go inside src and inside create two files http.go and Makefile.

Fill http.go with the code above. Then Makefile with:

.PHONY: build
b build:
    go build -buildmode=c-archive -o libhttp.a http.go

Now if we go to the src/go directory and run make build we will have two new files libhttp.a and libhttp.h.

Explendid!

Now we have to configure our C code files and add a new Wren class.

Go to src/module and create server.h, server.c, server.wren and server.wren.inc

server.h

#ifndef server_h
#define server_h

#include "wren.h"

void httpServer(WrenVM* vm);

#endif

server.c

#include "wren.h"

// We import our generated h file from go
#include "libhttp.h"

// And create a simple wrapper to Bind the exported function to the Wren VM
void httpServer(WrenVM* vm) {
  Http();
}

server.wren


class Http {
// foreign is used to tell Wren this will be implemented in C
    foreign static serve()
}

server.wren.inc

// This file can be auto generated too! using 
// python3 util/wren_to_c_string.py src/module/server.wren.inc src/module/server.wren
// the convention is <filename>ModuleSource

static const char* serverModuleSource =
"class Http {\n"
"  foreign static serve()\n"
"}\n"
"\n";

Ok let’s modify our src/cli/modules.c file to include our new class.

// near line 4
#include "modules.h"

#include "io.wren.inc"
#include "os.wren.inc"
#include "repl.wren.inc"
#include "scheduler.wren.inc"
#include "timer.wren.inc"

// We add our generated server.wren.inc file
#include "server.wren.inc"
// near line 51
extern void stdoutFlush(WrenVM* vm);
extern void schedulerCaptureMethods(WrenVM* vm);
extern void timerStartTimer(WrenVM* vm);

// Add our new function as a extern (this will tell the compiler that this function
// is implemented elsewhere (in our server.c file)
extern void httpServer(WrenVM* vm);
  // near line 180
  MODULE(timer)
    CLASS(Timer)
      STATIC_METHOD("startTimer_(_,_)", timerStartTimer)
    END_CLASS
  END_MODULE

  // We add our module mapping
  // import "server" for Http
  // Http.serve()
  MODULE(server)
    CLASS(Http)
      STATIC_METHOD("serve()", httpServer)
    END_CLASS
  END_MODULE

Finally we have to include the new paths in the Makefile so the libs and objects are included in the compilation.

Go to projects/make.mac/wren_cli.make

# Near line 30 prepend -I../../src/go
INCLUDES += -I../../src/go -I../../src/cli
# Near line 34
LIBS += -L../../src/go -lhttp -framework CoreFoundation -framework Security
# Note that we use -lhttp to refer to libhttp.a
# also we include the required frameworks from MacOS that Go http module needs to work
# Near line 160
OBJECTS += $(OBJDIR)/wren_value.o
OBJECTS += $(OBJDIR)/wren_vm.o

OBJECTS += $(OBJDIR)/server.o
# We include the generated object
# Near line 382
$(OBJDIR)/timer1.o: ../../src/module/timer.c
    @echo $(notdir $<)
    $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"

$(OBJDIR)/server.o: ../../src/module/server.c
    @echo $(notdir $<)
    $(SILENT) $(CC) $(ALL_CFLAGS) $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"

# We include the server.c source file for the object.

Now we are almost ready. Just go to the make.mac directory and execute make command.

If all went well you will have a shiny wren_cli binary inside bin/.
And if you execute the REPL you can use the new module and go to localhost:8080 for testing it

bin/wren_cli
\\/"-
 \_/   wren v0.4.0
> import "server" for Http
> Http.serve()
import "server" for Http
Http.serve()



Considerations

  • The generated static library contains lots of functions, even if you just exported one. So it will add weight to the wren_cli. In this case up to 5Mb more.
  • You can automate generating wren.inc files with python3 util/wren_to_c_string.py src/module/server.wren.inc src/module/server.wren
  • You can automate generating projects/make.mac/wren_cli.make files by using Premake.
  • The same procedure can be followed by other languages like Rust and bring all its power to Wren.



Using Premake

The minimum version required is:

  • premake5 (Premake Build Script Generator) 5.0.0-alpha14

Configure projects/premake/premake5.lua

-- near line 58
includedirs {
    "../../src/cli",
    "../../src/module",
    "../../src/go",
  }
-- near line 118
  filter "system:macosx"
    systemversion "10.12"
    links { "http", "/Library/Frameworks/CoreFoundation.framework", "/Library/Frameworks/Security.framework" }
    linkoptions {"-L../../src/go"}

And then execute
python3 utils/generate_projects.py



Conclusion

Wren is marvelous and it’s CLI is easy to hack and extend. If you need Wren to have industry level extensions you can rely
on Go or Rust extensive libraries and create something wonderful.

If you need a complete project you can go here


Print Share Comment Cite Upload Translate
APA
Camilo | Sciencx (2024-03-29T08:16:37+00:00) » Using Go inside Wren CLI. Retrieved from https://www.scien.cx/2021/04/28/using-go-inside-wren-cli/.
MLA
" » Using Go inside Wren CLI." Camilo | Sciencx - Wednesday April 28, 2021, https://www.scien.cx/2021/04/28/using-go-inside-wren-cli/
HARVARD
Camilo | Sciencx Wednesday April 28, 2021 » Using Go inside Wren CLI., viewed 2024-03-29T08:16:37+00:00,<https://www.scien.cx/2021/04/28/using-go-inside-wren-cli/>
VANCOUVER
Camilo | Sciencx - » Using Go inside Wren CLI. [Internet]. [Accessed 2024-03-29T08:16:37+00:00]. Available from: https://www.scien.cx/2021/04/28/using-go-inside-wren-cli/
CHICAGO
" » Using Go inside Wren CLI." Camilo | Sciencx - Accessed 2024-03-29T08:16:37+00:00. https://www.scien.cx/2021/04/28/using-go-inside-wren-cli/
IEEE
" » Using Go inside Wren CLI." Camilo | Sciencx [Online]. Available: https://www.scien.cx/2021/04/28/using-go-inside-wren-cli/. [Accessed: 2024-03-29T08:16:37+00:00]
rf:citation
» Using Go inside Wren CLI | Camilo | Sciencx | https://www.scien.cx/2021/04/28/using-go-inside-wren-cli/ | 2024-03-29T08:16:37+00:00
https://github.com/addpipe/simple-recorderjs-demo