Browse Source

Add new-nif target and related tests

Pushing this now so I can figure out Windows.
Loïc Hoguin 9 years ago
parent
commit
9bd5ec4f47
2 changed files with 177 additions and 0 deletions
  1. 95 0
      plugins/c_src.mk
  2. 82 0
      test/plugin_c_src.mk

+ 95 - 0
plugins/c_src.mk

@@ -115,3 +115,98 @@ distclean-c_src-env:
 
 -include $(C_SRC_ENV)
 endif
+
+# Templates.
+
+define bs_c_nif
+#include "erl_nif.h"
+
+static int loads = 0;
+
+static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
+{
+	/* Initialize private data. */
+	*priv_data = NULL;
+
+	loads++;
+
+	return 0;
+}
+
+static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
+{
+	/* Convert the private data to the new version. */
+	*priv_data = *old_priv_data;
+
+	loads++;
+
+	return 0;
+}
+
+static void unload(ErlNifEnv* env, void* priv_data)
+{
+	if (loads == 1) {
+		/* Destroy the private data. */
+	}
+
+	loads--;
+}
+
+static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+	if (enif_is_atom(env, argv[0])) {
+		return enif_make_tuple2(env,
+			enif_make_atom(env, "hello"),
+			argv[0]);
+	}
+
+	return enif_make_tuple2(env,
+		enif_make_atom(env, "error"),
+		enif_make_atom(env, "badarg"));
+}
+
+static ErlNifFunc nif_funcs[] = {
+	{"hello", 1, hello}
+};
+
+ERL_NIF_INIT($n, nif_funcs, load, NULL, upgrade, unload)
+endef
+
+define bs_erl_nif
+-module($n).
+
+-export([hello/1]).
+
+-on_load(on_load/0).
+on_load() ->
+	PrivDir = case code:priv_dir(?MODULE) of
+		{error, _} ->
+			AppPath = filename:dirname(filename:dirname(code:which(?MODULE))),
+			filename:join(AppPath, "priv");
+		Path ->
+			Path
+	end,
+	erlang:load_nif(filename:join(PrivDir, atom_to_list(?MODULE)), 0).
+
+hello(_) ->
+	erlang:nif_error({not_loaded, ?MODULE}).
+endef
+
+$(foreach template,bs_c_nif bs_erl_nif, \
+	$(eval _$(template) = $$(subst $$(tab),$$(WS),$$($(template)))) \
+	$(eval export _$(template)))
+
+new-nif:
+ifneq ($(wildcard $(C_SRC_DIR)/$n.c),)
+	$(error Error: $(C_SRC_DIR)/$n.c already exists)
+endif
+ifneq ($(wildcard src/$n.erl),)
+	$(error Error: src/$n.erl already exists)
+endif
+ifdef in
+	$(verbose) $(MAKE) -C $(APPS_DIR)/$(in)/ new-nif n=$n in=
+else
+	$(verbose) mkdir -p $(C_SRC_DIR) src/
+	$(call render_template,bs_c_nif,$(C_SRC_DIR)/$n.c)
+	$(call render_template,bs_erl_nif,src/$n.erl)
+endif

+ 82 - 0
test/plugin_c_src.mk

@@ -0,0 +1,82 @@
+# C source plugin.
+
+C_SRC_CASES = cpp custom dir env nif port
+C_SRC_TARGETS = $(addprefix c-src-,$(C_SRC_CASES))
+C_SRC_CLEAN_TARGETS = $(addprefix clean-,$(C_SRC_TARGETS))
+
+.PHONY: c-src $(C_SRC_TARGETS) clean-c-src $(C_SRC_CLEAN_TARGETS)
+
+clean-c-src: $(C_SRC_CLEAN_TARGETS)
+
+$(C_SRC_CLEAN_TARGETS):
+	$t rm -rf $(APP_TO_CLEAN)/
+
+c-src: $(C_SRC_TARGETS)
+
+c-src-nif: build clean-c-src-nif
+
+	$i "Bootstrap a new OTP library named $(APP)"
+	$t mkdir $(APP)/
+	$t cp ../erlang.mk $(APP)/
+	$t $(MAKE) -C $(APP) -f erlang.mk bootstrap-lib $v
+
+	$i "Generate a NIF from templates"
+	$t $(MAKE) -C $(APP) new-nif n=$(APP) $v
+
+	$i "Build the application"
+	$t $(MAKE) -C $(APP) $v
+
+	$i "Check that all compiled files exist"
+	$t test -f $(APP)/$(APP).d
+	$t test -f $(APP)/c_src/$(APP).o
+	$t test -f $(APP)/c_src/env.mk
+	$t test -f $(APP)/ebin/$(APP).app
+	$t test -f $(APP)/ebin/$(APP).beam
+	$t test -f $(APP)/priv/$(APP).so
+
+	$i "Check that the application was compiled correctly"
+	$t $(ERL) -pa $(APP)/ebin/ -eval " \
+		ok = application:start($(APP)), \
+		{ok, [$(APP)]} = application:get_key($(APP), modules), \
+		{module, $(APP)} = code:load_file($(APP)), \
+		{hello, joe} = $(APP):hello(joe), \
+		{hello, mike} = $(APP):hello(mike), \
+		{hello, robert} = $(APP):hello(robert), \
+		halt()"
+
+	$i "Re-build the application"
+	$t $(MAKE) -C $(APP) $v
+
+	$i "Check that all compiled files exist"
+	$t test -f $(APP)/$(APP).d
+	$t test -f $(APP)/c_src/$(APP).o
+	$t test -f $(APP)/c_src/env.mk
+	$t test -f $(APP)/ebin/$(APP).app
+	$t test -f $(APP)/ebin/$(APP).beam
+	$t test -f $(APP)/priv/$(APP).so
+
+	$i "Check that the application was compiled correctly"
+	$t $(ERL) -pa $(APP)/ebin/ -eval " \
+		ok = application:start($(APP)), \
+		{ok, [$(APP)]} = application:get_key($(APP), modules), \
+		{module, $(APP)} = code:load_file($(APP)), \
+		{hello, joe} = $(APP):hello(joe), \
+		{hello, mike} = $(APP):hello(mike), \
+		{hello, robert} = $(APP):hello(robert), \
+		halt()"
+
+	$i "Clean the application"
+	$t $(MAKE) -C $(APP) clean $v
+
+	$i "Check that all intermediate files were removed"
+	$t test ! -e $(APP)/$(APP).d
+	$t test ! -e $(APP)/c_src/$(APP).o
+	$t test ! -e $(APP)/ebin/$(APP).app
+	$t test ! -e $(APP)/ebin/$(APP).beam
+	$t test ! -e $(APP)/priv/$(APP).so
+
+	$i "Distclean the application"
+	$t $(MAKE) -C $(APP) distclean $v
+
+	$i "Check that all files were removed"
+	$t test ! -e $(APP)/c_src/env.mk