Commit 524ccdc1bad49fdfad9216c1a308d43ed4840184

Thomas de Grivel 2022-05-03T17:40:47

try git_nif

diff --git a/Makefile b/Makefile
index 3af7cc2..0cbb1ef 100644
--- a/Makefile
+++ b/Makefile
@@ -1,18 +1,25 @@
 
-gitport = bin/gitport
-gitport_SRC = c_src/gitport.c
-gitport_SRC_O = c_src/gitport.o
-gitport_LIBS = -lgit2
+CFLAGS = -W -Wall -Werror
+CPPFLAGS = -I/usr/local/include -I/usr/local/lib/erlang24/usr/include
+LDFLAGS = -L/usr/local/lib -L/usr/local/lib/erlang24/usr/lib
+
+git_nif = bin/libgit_nif.so
+git_nif_SRC = c_src/git_nif.c
+git_nif_SRC_O = c_src/git_nif.o
+git_nif_LIBS = -lgit2
 
 size = bin/size
 size_SRC = c_src/size.c
 size_SRC_O = c_src/size.o
 size_LIBS =
 
-PROGS = ${gitport} ${size}
+PROGS = ${git_nif} ${size}
 
 all: ${PROGS}
 
+${git_nif}: ${git_nif_SRC_O}
+	${CC} -fPIC -shared ${LDFLAGS} ${git_nif_SRC_O} ${git_nif_LIBS} -o ${git_nif}
+
 ${gitport}: ${gitport_SRC_O}
 	${CC} ${CFLAGS} ${LDFLAGS} ${gitport_SRC_O} ${gitport_LIBS} -o ${gitport}
 
diff --git a/c_src/git_nif.c b/c_src/git_nif.c
new file mode 100644
index 0000000..e556e8a
--- /dev/null
+++ b/c_src/git_nif.c
@@ -0,0 +1,104 @@
+#include <erl_nif.h>
+#include <git2.h>
+#include <stdlib.h>
+#include <string.h>
+
+char * enif_term_to_string (ErlNifEnv *env, const ERL_NIF_TERM term)
+{
+  ErlNifBinary bin;
+  unsigned len;
+  char *str;
+  switch (enif_term_type(env, term)) {
+  case ERL_NIF_TERM_TYPE_BITSTRING:
+    enif_inspect_binary(env, term, &bin);
+    len = bin.size;
+    str = malloc(len + 1);
+    memcpy(str, bin.data, len);
+    str[len] = 0;
+    return str;
+  case ERL_NIF_TERM_TYPE_LIST:
+    enif_get_list_length(env, term, &len);
+    str = malloc(len + 1);
+    enif_get_string(env, term, str, len + 1, ERL_NIF_LATIN1);
+    return str;
+  default:
+    return NULL;
+  }
+}
+
+ERL_NIF_TERM enif_string_to_term (ErlNifEnv *env, const char *str)
+{
+  size_t len = strlen(str);
+  ErlNifBinary bin;
+  enif_alloc_binary(len, &bin);
+  memcpy(bin.data, str, len);
+  return enif_make_binary(env, &bin);
+}
+
+ERL_NIF_TERM push_string (ErlNifEnv *env, const char *str, const ERL_NIF_TERM acc)
+{
+  ERL_NIF_TERM term = enif_string_to_term(env, str);
+  return enif_make_list_cell(env, term, acc);
+}
+
+static ERL_NIF_TERM branches (ErlNifEnv *env, int argc,
+                              const ERL_NIF_TERM argv[])
+{
+  git_repository *r = NULL;
+  git_branch_iterator *i = NULL;
+  git_reference *ref = NULL;
+  git_branch_t ref_type = 0;
+  char *branch = NULL;
+  char *path = NULL;
+  ERL_NIF_TERM branches;
+  fprintf(stderr, "branches %d\n", argc);
+  if (argc != 1 || !argv || !argv[0])
+    goto error;
+  path = enif_term_to_string(env, argv[0]);
+  fprintf(stderr, "path: '%s'\n", path);
+  if (!path || !path[0])
+    goto error;
+  if (git_repository_open(&r, path))
+    goto error;
+  git_branch_iterator_new(&i, r, GIT_BRANCH_ALL);
+  branches = enif_make_list(env, 0);
+  while (!git_branch_next(&ref, &ref_type, i)) {
+    git_branch_name((const char **) &branch, ref);
+    fprintf(stderr, " %s", branch);
+    branches = push_string(env, branch, branches);
+  }
+  git_branch_iterator_free(i);
+  free(path);
+  //free(branch);
+  fprintf(stderr, "\n");
+  return branches;
+ error:
+  fprintf(stderr, "error\n");
+  free(path);
+  //free(branch);
+  return enif_make_atom(env, "error");
+}
+
+static ErlNifFunc funcs[] = {
+  {"branches_nif", 1, branches, 0},
+};
+
+int load (ErlNifEnv *env, void **a, ERL_NIF_TERM b)
+{
+  (void) env;
+  (void) a;
+  (void) b;
+  fprintf(stderr, "git_nif load\n");
+  git_libgit2_init();
+  return 0;
+}
+
+void unload (ErlNifEnv *env, void *a)
+{
+  (void) env;
+  (void) a;
+  git_libgit2_shutdown();
+  fprintf(stderr, "git_nif unload\n");
+}
+
+ERL_NIF_INIT(Elixir.Kmxgit.Git, funcs, load, NULL, NULL, unload);
diff --git a/c_src/gitport.c b/c_src/gitport.c
deleted file mode 100644
index 7f2a8b3..0000000
--- a/c_src/gitport.c
+++ /dev/null
@@ -1,109 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <git2.h>
-
-typedef int (*f_fun) (int argc, const char **argv);
-
-typedef struct fun {
-  const char *name;
-  f_fun fun;
-} s_fun;
-
-int branches (int argc, const char **argv)
-{
-  const char *path;
-  git_repository **r = malloc(sizeof(void*));
-  git_branch_iterator **i = malloc(sizeof(void*));
-  git_reference **ref = malloc(sizeof(void*));
-  git_branch_t *ref_type = malloc(sizeof(git_branch_t));
-  const char **branch = malloc(sizeof(void*));
-  fprintf(stderr, "branches %d\n", argc);
-  if (argc != 1 || !argv || !argv[1])
-    goto error;
-  path = argv[1];
-  if (git_repository_open(r, path))
-    goto error;
-  git_branch_iterator_new(i, *r, GIT_BRANCH_ALL);
-  while (!git_branch_next(ref, ref_type, *i)) {
-    git_branch_name(branch, *ref);
-    fprintf(stderr, " %s", *branch);
-    printf(" %s", *branch);
-  }
-  git_branch_iterator_free(*i);
-  free(i);
-  free(r);
-  free(ref);
-  free(ref_type);
-  free(branch);
-  fprintf(stderr, "\n");
-  printf("\n");
-  fflush(stdout);
-  return 0;
- error:
-  fprintf(stderr, "error\n");
-  printf("error\n");
-  free(i);
-  free(r);
-  free(ref);
-  free(ref_type);
-  free(branch);
-  return 1;
-}
-
-s_fun g_fun[] = {
-  {"branches", branches},
-  {NULL, NULL}
-};
-
-f_fun find_fun (const char *name)
-{
-  s_fun *f = g_fun;
-  while (f->name && strcmp(name, f->name))
-    f++;
-  if (f->name)
-    return f->fun;
-  return NULL;
-}
-
-int repl (FILE *in)
-{
-  int ret = 0;
-  int run = 1;
-  ssize_t size = 1024;
-  char *line = malloc(size);
-  int argc;
-  char **argv = malloc(sizeof(char*) * 16);
-  char *argvp;
-  f_fun f = NULL;
-  if (!line)
-    return -1;
-  while (1) {
-    size = getline(&line, &size, in);
-    if (size < 0)
-      break;
-    if (size == 0)
-      continue;
-    line[size - 1] = 0;
-    argvp = line;
-    argc = 0;
-    while ((argv[argc] = strsep(&argvp, " ")))
-      argc++;
-    argc--;
-    if ((f = find_fun(argv[0]))) {
-      f(argc, (const char **) argv);
-    }
-  }
-  free(line);
-  return ret;
-}
-
-int main (int argc, char **argv)
-{
-  fprintf(stderr, "gitport started\n");
-  git_libgit2_init();
-  repl(stdin);
-  git_libgit2_shutdown();
-  fprintf(stderr, "gitport exiting\n");
-  return 0;
-}
diff --git a/lib/kmxgit/application.ex b/lib/kmxgit/application.ex
index d906375..79c4264 100644
--- a/lib/kmxgit/application.ex
+++ b/lib/kmxgit/application.ex
@@ -10,7 +10,6 @@ defmodule Kmxgit.Application do
     children = [
       # Start the Ecto repository
       Kmxgit.Repo,
-      Kmxgit.Git,
       # Start the Telemetry supervisor
       KmxgitWeb.Telemetry,
       # Start the PubSub system
diff --git a/lib/kmxgit/git.ex b/lib/kmxgit/git.ex
index c506f15..baf3f7a 100644
--- a/lib/kmxgit/git.ex
+++ b/lib/kmxgit/git.ex
@@ -1,46 +1,25 @@
 defmodule Kmxgit.Git do
-  use GenServer
+  @on_load :init
 
   @git_root "priv/git"
 
-  def start_link(opts) do
-    GenServer.start_link(__MODULE__, opts, name: __MODULE__)
+  def init do
+    path = "bin/libgit_nif"
+    |> String.to_charlist()
+    :ok = :erlang.load_nif(path, 0)
   end
 
   # Functions
 
   def branches(repo) do
-    GenServer.call(__MODULE__, {:branches, repo})
-    |> String.trim()
-    |> String.split(" ")
+    repo
+    |> git_dir()
+    |> IO.inspect()
+    |> branches_nif()
   end
 
-  # Callbacks
-
-  @impl true
-  def init(_) do
-    cmd = {:spawn_executable, "bin/gitport"}
-    IO.inspect(cmd)
-    port = Port.open(cmd, [:binary])
-    {:ok, %{from: [], port: port}}
-  end
-  
-  @impl true
-  def handle_call(arg = {:branches, repo}, from, state) do
-    IO.inspect({:handle_cast, arg})
-    dir = git_dir(repo)
-    cmd = "branches #{dir}\n"
-    send(state.port, {self(), {:command, cmd}})
-    {:noreply, %{state | from: state.from ++ [from]}}
-  end
-
-  @impl true
-  def handle_info(arg = {port, {:data, data}}, state = %{from: [from | rest], port: port}) do
-    GenServer.reply(from, data)
-    {:noreply, %{state | from: rest}}
-  end
-  def handle_info(arg, state) do
-    {:noreply, %{state | from: tl(state.from)}}
+  def branches_nif(_path) do
+    exit(:nif_not_loaded)
   end
 
   # common functions