diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..518965b --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +.rebar3 +_* +.eunit +*.o +*.so +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +_rel +_deps +_plugins +_tdeps +logs diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f592795 --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2015, Tristan Sloughter . +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +* The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3abfa27 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +rebar3_run +===== + +A rebar plugin + +Build +----- + + $ rebar3 compile + +Use +--- + +Add the plugin to your rebar config: + + {plugins, [ + {rebar3_run, ".*", {git, "git://github.com/tsloughter/rebar3_run.git", {branch, "master"}}} + ]}. + +Then just call your plugin directly in an existing application: + + + $ rebar3 run + ===> Fetching rebar3_run + ===> Compiling rebar3_run + ===> Starting relx build process ... + ===> Resolving OTP Applications from directories: + ..... + Erlang/OTP 17 [erts-6.4] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false] + + Eshell V6.4 (abort with ^G) + (@127.0.0.1)1> diff --git a/c_src/Makefile b/c_src/Makefile new file mode 100644 index 0000000..d3c3767 --- /dev/null +++ b/c_src/Makefile @@ -0,0 +1,74 @@ +# Based on c_src.mk from erlang.mk by Loic Hoguin + +CURDIR := $(shell pwd) +BASEDIR := $(abspath $(CURDIR)/..) + +PROJECT ?= $(notdir $(BASEDIR)) +PROJECT := $(strip $(PROJECT)) + +ERTS_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s/erts-~s/include/\", [code:root_dir(), erlang:system_info(version)]).") +ERL_INTERFACE_INCLUDE_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s\", [code:lib_dir(erl_interface, include)]).") +ERL_INTERFACE_LIB_DIR ?= $(shell erl -noshell -s init stop -eval "io:format(\"~s\", [code:lib_dir(erl_interface, lib)]).") + +C_SRC_DIR = $(CURDIR) +C_SRC_OUTPUT ?= $(CURDIR)/../priv/$(PROJECT).so + +# System type and C compiler/flags. + +UNAME_SYS := $(shell uname -s) +ifeq ($(UNAME_SYS), Darwin) + CC ?= cc + CFLAGS ?= -O3 -std=c99 -arch x86_64 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -arch x86_64 -finline-functions -Wall + LDFLAGS ?= -arch x86_64 -flat_namespace -undefined suppress +else ifeq ($(UNAME_SYS), FreeBSD) + CC ?= cc + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -finline-functions -Wall +else ifeq ($(UNAME_SYS), Linux) + CC ?= gcc + CFLAGS ?= -O3 -std=c99 -finline-functions -Wall -Wmissing-prototypes + CXXFLAGS ?= -O3 -finline-functions -Wall +endif + +CFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) +CXXFLAGS += -fPIC -I $(ERTS_INCLUDE_DIR) -I $(ERL_INTERFACE_INCLUDE_DIR) + +LDLIBS += -L $(ERL_INTERFACE_LIB_DIR) -lerl_interface -lei +LDFLAGS += -shared + +# Verbosity. + +c_verbose_0 = @echo " C " $(?F); +c_verbose = $(c_verbose_$(V)) + +cpp_verbose_0 = @echo " CPP " $(?F); +cpp_verbose = $(cpp_verbose_$(V)) + +link_verbose_0 = @echo " LD " $(@F); +link_verbose = $(link_verbose_$(V)) + +SOURCES := $(shell find $(C_SRC_DIR) -type f \( -name "*.c" -o -name "*.C" -o -name "*.cc" -o -name "*.cpp" \)) +OBJECTS = $(addsuffix .o, $(basename $(SOURCES))) + +COMPILE_C = $(c_verbose) $(CC) $(CFLAGS) $(CPPFLAGS) -c +COMPILE_CPP = $(cpp_verbose) $(CXX) $(CXXFLAGS) $(CPPFLAGS) -c + +$(C_SRC_OUTPUT): $(OBJECTS) + @mkdir -p $(BASEDIR)/priv/ + $(link_verbose) $(CC) $(OBJECTS) $(LDFLAGS) $(LDLIBS) -o $(C_SRC_OUTPUT) + +%.o: %.c + $(COMPILE_C) $(OUTPUT_OPTION) $< + +%.o: %.cc + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +%.o: %.C + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +%.o: %.cpp + $(COMPILE_CPP) $(OUTPUT_OPTION) $< + +clean: + @rm -f $(C_SRC_OUTPUT) $(OBJECTS) diff --git a/c_src/rebar3_run.c b/c_src/rebar3_run.c new file mode 100644 index 0000000..fa4daeb --- /dev/null +++ b/c_src/rebar3_run.c @@ -0,0 +1,62 @@ +/* The MIT License (MIT) */ + +/* Copyright (c) 2015 Ahmad Sherif */ + +/* Permission is hereby granted, free of charge, to any person obtaining a copy */ +/* of this software and associated documentation files (the "Software"), to deal */ +/* in the Software without restriction, including without limitation the rights */ +/* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell */ +/* copies of the Software, and to permit persons to whom the Software is */ +/* furnished to do so, subject to the following conditions: */ + +/* The above copyright notice and this permission notice shall be included in all */ +/* copies or substantial portions of the Software. */ + +#include + +#include "erl_nif.h" + +static ERL_NIF_TERM exec_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ + unsigned path_length, args_count, arg_length; + ERL_NIF_TERM head, tail; + int i = 0; + + enif_get_list_length(env, argv[0], &path_length); + enif_get_list_length(env, argv[1], &args_count); + + char* exec_argv[args_count + 2]; + char path[path_length + 1]; + + if (!enif_get_string(env, argv[0], path, path_length + 1, ERL_NIF_LATIN1) || !enif_is_list(env, argv[1])) { + return enif_make_badarg(env); + } + + tail = argv[1]; + while(enif_get_list_cell(env, tail, &head, &tail) != 0) { + enif_get_list_length(env, head, &arg_length); + + char* arg = (char*) malloc(sizeof(char) * (arg_length + 1)); + + if (!enif_get_string(env, head, arg, arg_length + 1, ERL_NIF_LATIN1)) { + return enif_make_badarg(env); + } + + exec_argv[i + 1] = arg; + + i++; + } + + exec_argv[0] = path; + exec_argv[args_count + 1] = NULL; + + execv(path, exec_argv); + + return enif_make_atom(env, "ok"); +} + +static ErlNifFunc nif_funcs[] = { + {"exec", 2, exec_nif} +}; + +ERL_NIF_INIT(rebar3_run, nif_funcs, NULL, NULL, NULL, NULL); diff --git a/rebar.config b/rebar.config new file mode 100644 index 0000000..d78fcf1 --- /dev/null +++ b/rebar.config @@ -0,0 +1,4 @@ +{erl_opts, [debug_info]}. +{deps, []}. + +{pre_hooks, [{compile, "make -C c_src/"}]}. diff --git a/rebar.lock b/rebar.lock new file mode 100644 index 0000000..57afcca --- /dev/null +++ b/rebar.lock @@ -0,0 +1 @@ +[]. diff --git a/src/rebar3_run.app.src b/src/rebar3_run.app.src new file mode 100644 index 0000000..839b903 --- /dev/null +++ b/src/rebar3_run.app.src @@ -0,0 +1,9 @@ +{application, rebar3_run, + [{description, "A rebar plugin"} + ,{vsn, "0.1.0"} + ,{registered, []} + ,{applications, + [kernel,stdlib]} + ,{env,[]} + ,{modules, []} + ]}. diff --git a/src/rebar3_run.erl b/src/rebar3_run.erl new file mode 100644 index 0000000..03a6da3 --- /dev/null +++ b/src/rebar3_run.erl @@ -0,0 +1,56 @@ +-module(rebar3_run). + +-export([init/1, + do/1, + format_error/1]). + +-export([exec/2]). + +-on_load(init/0). + +-include_lib("providers/include/providers.hrl"). + +-define(PROVIDER, run). +-define(DEPS, [release]). + +%% =================================================================== +%% Public API +%% =================================================================== + +-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. +init(State) -> + Provider = providers:create([ + {name, ?PROVIDER}, + {module, ?MODULE}, + {bare, false}, + {deps, ?DEPS}, + {example, "rebar3 run"}, + {short_desc, "Run release console."}, + {desc, ""}, + {opts, []} + ]), + State1 = rebar_state:add_provider(State, Provider), + {ok, State1}. + +-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. +do(State) -> + ReleaseDir = filename:join(rebar_dir:base_dir(State), "rel"), + Config = rebar_state:get(State, relx, []), + case lists:keyfind(release, 1, Config) of + {release, {Name, _Vsn}, _} -> + StartScript = filename:join([ReleaseDir, Name, "bin", Name]), + exec(StartScript, ["console"]), + {ok, State}; + false -> + ?PRV_ERROR(no_release) + end. + +format_error(no_release) -> + "No release to run was found.". + +init() -> + PrivDir = code:priv_dir(rebar3_run), + ok = erlang:load_nif(filename:join(PrivDir, "rebar3_run"), 0). + +exec(_Path, _Args) -> + exit(nif_library_not_loaded).