Code-Server x Dart with ffi x Go x Clang


Dart Advent Calendar 2019 の 記事です。

Dart の Native Interface に ついての記事です。
Go や C言語の機能をDartで利用する方法について解説します。また、すぐに Native Interface を試せるように、 Docker Image を 用意しました。

Native Interface とは : C言語 の ライブラリーを使用することができる機能です

C言語 の ライブラリーを使用することができる機能です。ただし、C言語と言ってもC言語でのライブラリーに閉じたものではないです。

Go言語 や Rust言語などのシステム言語は、Shared Libraryを作成することができます。

この Shared Library 経由すれば、Dartで GoやRustなどの機能も利用できます。

とても便利 : OSの機能をフルに使える

Dartは、まだまだライブラリーが不足している状態です。ですので、C言語で開発されたライブラリーや、Goで開発されたライブラリーを利用する必要があります。
Native Interface によち、 Dart がより便利になります。

開発環境 : Docker 環境を用意しました。

Code-Server という VSCode が オンライン上で動作する エディターを含めました。
このため、 Dockerを起動すれば、即、開発ができます。

FROM ubuntu:20.04

WORKDIR /works
# install dart
RUN apt-get update
RUN apt-get install -y wget gnupg1
RUN apt-get install apt-transport-https
RUN sh -c 'wget -qO- https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add -'
RUN sh -c 'wget -qO- https://storage.googleapis.com/download.dartlang.org/linux/debian/dart_stable.list > /etc/apt/sources.list.d/dart_stable.list'
RUN apt-get update
RUN apt-get -y install dart

# install go
RUN apt-get install software-properties-common -y
#RUN add-apt-repository ppa:longsleep/golang-backports
RUN apt-get update
RUN apt-get install golang-go -y
RUN apt-get install git -y
RUN go get github.com/ramya-rao-a/go-outline
RUN go get github.com/mdempsky/gocode
RUN go get github.com/uudashr/gopkgs/cmd/gopkgs
RUN go get github.com/sqs/goreturns
RUN go get github.com/rogpeppe/godef

# install c
RUN apt-get install musl-dev -y

# code-server
RUN wget https://github.com/cdr/code-server/releases/download/2.1692-vsc1.39.2/code-server2.1692-vsc1.39.2-linux-x86_64.tar.gz
RUN tar -xzf code-server2.1692-vsc1.39.2-linux-x86_64.tar.gz -C ./ --strip-components 1


RUN /works/code-server --install-extension Dart-Code.dart-code
RUN /works/code-server --install-extension ms-vscode.go
RUN /works/code-server --install-extension ms-vscode.cpptools

WORKDIR /app
ENV PATH=${PATH}:/lib/dart/bin
ENV PATH="${PATH}:/root/.pub-cache/bin"
RUN pub global activate webdev
RUN pub global activate stagehand

CMD ["/works/code-server", "--auth","none", "--host","0.0.0.0","--port","8443", "/app"]

docker-compose.yml
version: '3'
services: 
  app:
    build: ./app
    ports:
     - "8080:8080"
     - "8443:8443"
    volumes: 
      - ./app:/app
    # - /var/run/docker.sock:/var/run/docker.sock
    command: /works/code-server --auth none --host 0.0.0.0 --port 8443 /app 

具体的には、github を参照してください。

開発環境を起動する。

$ git clone https://github.com/kyorohiro/advent-2019-code-server.git
$ cd advent-2019-code-server/extra/dart_fmi_and_go_and_c
$ docker-compose build
$ docker-compose up -d

ブラウザーで、http://127.0.0.1:8443/ を開く

Go で Shared Library を 作成する。

VSCode->File(Menu)->/app/wgo に移動する。

hello.go
package main

import "C"
import "fmt"

//export PrintHello
func PrintHello() {
    fmt.Print("Hello,World")
}

func main() {}

Goの PrintHello 関数を、Dart から呼ばれる予定です。

$ go build -o libhello.so  -buildmode=c-shared  hello.go

とすると、libhello.hlibhello.so というファイルができます。
Dart で読み込む前に、C言語から読み込んでみましょう。

main_hello.c
#include <stdio.h>
#include "libhello.h"


int main(int argc, char const *argv[])
{
  PrintHello();
  return 0;
}

Terminal

$ gcc -Wall -o main_hello.exe main_hello.c -L. -lhello
$ LD_LIBRARY_PATH=. ./main_hello.exe -L. -lhello
Hello,World

おっ!! 上手く動作しました。

Dart から読み込んでみる。

VSCode->File(Menu)->/app/wdart に移動する。

bin/main.dart
import 'dart:ffi' as ffi;

typedef PrintHello_func = ffi.Void Function();
typedef PrintHello = void Function();

void main(List<String> arguments) {
  var path = "/app/wgo/libhello.so";
  ffi.DynamicLibrary dylib = ffi.DynamicLibrary.open(path);
  final PrintHello hello = dylib
      .lookup<ffi.NativeFunction<PrintHello_func>>('PrintHello')
      .asFunction();
  hello();
}


Terminal
$ dart ./bin/main.dart
Hello,World

PS

C言語の環境も用意しました。
先ほどの、github レポジトリーから見れます。


Go言語で開発された機能をDartでフルに利用できます。ので、Dart から ほぼなんでも出来る状態になりました。
FMIがサポートされたことで、気軽に Native Interface を書けるようになったと思います。


サーバーサイド向けでしたら、開発には、Docker Image などが便利だと思います。

今回、使用した Code-Server について、下記 Advent にて
20回以上に分けて解説していますので、興味ある方は、ご参照ください。

※ Docker Image として固めてHub に 配置するなどすれば、必ず動作するようになりますが、Image には固めていません。個人利用する際は、 Image に固めて保管しておくことをお勧めします。

Code