ninenines / erlang.mk

A build tool for Erlang that just works.
https://erlang.mk
ISC License
578 stars 241 forks source link

Problem building NIF on Mac M1 #988

Closed jhw closed 1 year ago

jhw commented 1 year ago

Hello

I'm having problems linking Erlang to a shared C library (dylib on Mac) using erlang.mk, and would really like a small amount of help

I think it's a path issue, but I could be wrong

I'm running a Mac M1 with Erlang installed via kerl

The C library is called Sunvox, a small software synth

https://warmplace.ru/soft/sunvox/sunvox_lib.php

My c_src structure is as follows -

jhw@my-MacBook-Air myapp % ls -l -R c_src         
total 16
drwxr-xr-x  4 jhw  staff  128 26 Aug 16:26 sunvox
-rw-r--r--  1 jhw  staff  782 26 Aug 17:04 sunvox_nif.c

c_src/sunvox:
total 0
drwxr-xr-x  3 jhw  staff  96 26 Aug 17:15 include
drwxr-xr-x  3 jhw  staff  96 26 Aug 16:35 lib

c_src/sunvox/include:
total 104
-rw-r--r--@ 1 jhw  staff  49805 26 Aug 16:31 sunvox.h

c_src/sunvox/lib:
total 1504
-rwxr-xr-x@ 1 jhw  staff  767321 26 Aug 16:35 libsunvox.dylib

I have been careful to use the ARM version of the dylib, not the x86 version

c_src/sunvox_nif.c looks as follows -

jhw@my-MacBook-Air myapp % more c_src/sunvox_nif.c
#include "erl_nif.h"
#include "sunvox.h"

static ERL_NIF_TERM nif_sv_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
    int res = sv_init(0, 44100, 2048, 0);
    if (res != 0) {
      return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_int(env, res));
    }    
    return enif_make_atom(env, "ok");
}

static ERL_NIF_TERM nif_sv_deinit(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
    int res = sv_deinit();
    if (res != 0) {
      return enif_make_tuple2(env, enif_make_atom(env, "error"), enif_make_int(env, res));
    }    
    return enif_make_atom(env, "ok");
}

static ErlNifFunc nif_funcs[] = {
    {"sv_init", 0, nif_sv_init},
    {"sv_deinit", 0, nif_sv_deinit}
};

ERL_NIF_INIT(sunvox_nif, nif_funcs, NULL, NULL, NULL, NULL)

My Makefile looks as follows -

PROJECT = myapp
PROJECT_DESCRIPTION = An Erlang server to wrap the Sunvox developer library as a simple DJing service
PROJECT_VERSION = 0.1.0

CFLAGS += -I$(CURDIR)/c_src/sunvox/include

LDFLAGS += -L$(CURDIR)/c_src/sunvox/lib -lsunvox

C_SRC_TYPE = shared
C_SRC_DIR = $(CURDIR)/c_src
C_SRC_OUTPUT = $(CURDIR)/priv/$(PROJECT)

BUILD_DEPS += relx
include erlang.mk

DYLD_LIBRARY_PATH is set as follows -

#!/usr/bin/env bash

export DYLD_LIBRARY_PATH=$HOME/work/myapp/c_src/sunvox/lib

Running make clean && make does the following -

jhw@my-MacBook-Air myapp % make clean && make
erlang.mk:26: Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html
 GEN    clean-app
 GEN    clean-c_src
 GEN    coverdata-clean
erlang.mk:26: Please upgrade to GNU Make 4 or later: https://erlang.mk/guide/installation.html
 DEPEND myapp.d
 ERLC   myapp_app.erl myapp_sunvox_server.erl myapp_sup.erl
 APP    myapp
 C      sunvox_nif.c
 LD     myapp.so
Undefined symbols for architecture arm64:
  "_enif_make_atom", referenced from:
      _nif_sv_init in sunvox_nif.o
      _nif_sv_deinit in sunvox_nif.o
  "_enif_make_int", referenced from:
      _nif_sv_init in sunvox_nif.o
      _nif_sv_deinit in sunvox_nif.o
  "_enif_make_tuple", referenced from:
      _enif_make_tuple2 in sunvox_nif.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [/Users/jhw/work/myapp/priv/myapp.so] Error 1

It feels like compilation works fine but the linker can't find code for the erlang nif functions used in sunvox_nif.c

I notice that erlang.mk generates c_src/env.mk -

jhw@my-MacBook-Air myapp % more c_src/env.mk 
# Generated by Erlang.mk. Edit at your own risk!

ERTS_INCLUDE_DIR ?= /Users/jhw/kerl/26.0.2/erts-14.0.2/include/
ERL_INTERFACE_INCLUDE_DIR ?= /Users/jhw/kerl/26.0.2/lib/erl_interface-5.4/include
ERL_INTERFACE_LIB_DIR ?= /Users/jhw/kerl/26.0.2/lib/erl_interface-5.4/lib
ERTS_DIR ?= /Users/jhw/kerl/26.0.2/lib/erts-14.0.2

These all seem valid; here's what's in each -

jhw@my-MacBook-Air myapp % ls -l /Users/jhw/kerl/26.0.2/erts-14.0.2/include/
total 200
-rw-r--r--   1 jhw  staff    958 28 Jun 11:02 driver_int.h
-rw-r--r--   1 jhw  staff  22003 28 Jun 11:02 erl_driver.h
-rw-r--r--   1 jhw  staff   6685 28 Jun 11:02 erl_drv_nif.h
-rw-r--r--   1 jhw  staff   3940 28 Jun 11:02 erl_fixed_size_int_types.h
-rw-r--r--   1 jhw  staff   1157 26 Aug 11:18 erl_int_sizes_config.h
-rw-r--r--   1 jhw  staff  13623 28 Jun 11:02 erl_nif.h
-rw-r--r--   1 jhw  staff  38361 28 Jun 11:02 erl_nif_api_funcs.h
drwxr-xr-x  25 jhw  staff    800 26 Aug 11:22 internal
jhw@my-MacBook-Air myapp % ls -l /Users/jhw/kerl/26.0.2/lib/erl_interface-5.4/include
total 72
-rw-r--r--  1 jhw  staff  25028 28 Jun 11:02 ei.h
-rw-r--r--  1 jhw  staff    780 28 Jun 11:02 ei_connect.h
-rw-r--r--  1 jhw  staff    768 28 Jun 11:02 eicode.h
jhw@my-MacBook-Air myapp % ls -l /Users/jhw/kerl/26.0.2/lib/erl_interface-5.4/lib
total 2552
-rw-r--r--  1 jhw  staff  661136 26 Aug 11:18 libei.a
-rw-r--r--  1 jhw  staff  642664 26 Aug 11:18 libei_st.a
jhw@my-MacBook-Air myapp % ls -l /Users/jhw/kerl/26.0.2/lib/erts-14.0.2
total 0
drwxr-xr-x  24 jhw  staff  768 26 Aug 11:22 ebin
drwxr-xr-x  24 jhw  staff  768 26 Aug 11:22 src
jhw@my-MacBook-Air myapp % 

Anything important missing from the above?

All help gratefully received. Thank you

essen commented 1 year ago

Hello! It's probably not a wrong path issue, but rather a flag that is missing or incorrect for M1 or arm64. When the library is a shared library it should add the -shared flag to the ld command. Perhaps that's not the correct flag in your environment.

I unfortunately do not have that platform to test.

You can try again adding V=1 at the end of the make commands you run to get more output. V=2 or V=3 will get you even more output.

jhw commented 1 year ago

OK noted. Thank you very much, I will try and resolve elsewhere.