diff options
-rw-r--r-- | priv/templates/Makefile.dtl | 74 | ||||
-rw-r--r-- | priv/templates/cmake.template | 2 | ||||
-rw-r--r-- | src/rebar3.erl | 12 | ||||
-rw-r--r-- | src/rebar_config.erl | 37 | ||||
-rw-r--r-- | src/rebar_otp_app.erl | 1 | ||||
-rw-r--r-- | src/rebar_plugins.erl | 16 | ||||
-rw-r--r-- | src/rebar_prv_compile.erl | 44 | ||||
-rw-r--r-- | test/rebar_deps_SUITE.erl | 44 |
8 files changed, 185 insertions, 45 deletions
diff --git a/priv/templates/Makefile.dtl b/priv/templates/Makefile.dtl new file mode 100644 index 0000000..88357d1 --- /dev/null +++ b/priv/templates/Makefile.dtl @@ -0,0 +1,74 @@ +# Based on c_src.mk from erlang.mk by Loïc Hoguin <essen@ninenines.eu> + +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 $(C_SRC_OUTPUT) $(OBJECTS) diff --git a/priv/templates/cmake.template b/priv/templates/cmake.template new file mode 100644 index 0000000..b3f23e8 --- /dev/null +++ b/priv/templates/cmake.template @@ -0,0 +1,2 @@ +{description, "Makefile for building C/C++ in c_src"}. +{template, "Makefile.dtl", "c_src/Makefile"}. diff --git a/src/rebar3.erl b/src/rebar3.erl index 5a49ca8..e8a9983 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -142,15 +142,7 @@ init_config() -> rebar_config:consult_file(ConfigFile) end, - Config1 = case rebar_config:consult_file(?LOCK_FILE) of - [D] -> - %% We want the top level deps only from the lock file. - %% This ensures deterministic overrides for configs. - Deps = [X || X <- D, element(3, X) =:= 0], - [{{locks, default}, D}, {{deps, default}, Deps} | Config]; - _ -> - Config - end, + Config1 = rebar_config:merge_locks(Config, rebar_config:consult_file(?LOCK_FILE)), %% If $HOME/.config/rebar3/config exists load and use as global config GlobalConfigFile = rebar_dir:global_config(), @@ -228,8 +220,6 @@ version() -> ?CONSOLE("rebar ~s on Erlang/OTP ~s Erts ~s", [Vsn, erlang:system_info(otp_release), erlang:system_info(version)]). - - %% TODO: Actually make it 'global' %% %% set global flag based on getopt option boolean value diff --git a/src/rebar_config.erl b/src/rebar_config.erl index 949d6d4..43730ea 100644 --- a/src/rebar_config.erl +++ b/src/rebar_config.erl @@ -27,7 +27,9 @@ -module(rebar_config). -export([consult/1 - ,consult_file/1]). + ,consult_file/1 + + ,merge_locks/2]). -include("rebar.hrl"). @@ -58,6 +60,18 @@ consult_file(File) -> end end. +merge_locks(Config, []) -> + Config; +merge_locks(Config, [Locks]) -> + {deps, ConfigDeps} = lists:keyfind(deps, 1, Config), + %% We want the top level deps only from the lock file. + %% This ensures deterministic overrides for configs. + %% Then check if any new deps have been added to the config + %% since it was locked. + Deps = [X || X <- Locks, element(3, X) =:= 0], + NewDeps = find_newly_added(ConfigDeps, Locks), + [{{locks, default}, Locks}, {{deps, default}, NewDeps++Deps} | Config]. + %% =================================================================== %% Internal functions %% =================================================================== @@ -84,3 +98,24 @@ bs(Vars) -> lists:foldl(fun({K,V}, Bs) -> erl_eval:add_binding(K, V, Bs) end, erl_eval:new_bindings(), Vars). + +%% Find deps that have been added to the config after the lock was created +find_newly_added(ConfigDeps, LockedDeps) -> + [Dep || Dep <- ConfigDeps, + begin + NewDep = ec_cnv:to_binary(element(1, Dep)), + case lists:keyfind(NewDep, 1, LockedDeps) of + false -> + true; + Match -> + case element(3, Match) of + 0 -> + true; + _ -> + ?WARN("Newly added dep ~s is locked at a lower level. " + "If you really want to unlock it, use 'rebar3 upgrade ~s'", + [NewDep, NewDep]), + false + end + end + end]. diff --git a/src/rebar_otp_app.erl b/src/rebar_otp_app.erl index e5e2361..272d950 100644 --- a/src/rebar_otp_app.erl +++ b/src/rebar_otp_app.erl @@ -54,6 +54,7 @@ compile(State, App) -> validate_app(State2, App1). + format_error({file_read, File, Reason}) -> io_lib:format("Failed to read ~s for processing: ~p", [File, Reason]); format_error({invalid_name, File, AppName}) -> diff --git a/src/rebar_plugins.erl b/src/rebar_plugins.erl index 45cb5c9..333d8a1 100644 --- a/src/rebar_plugins.erl +++ b/src/rebar_plugins.erl @@ -32,14 +32,7 @@ handle_plugin(Plugin, State) -> Apps = rebar_state:all_deps(State1), ToBuild = lists:dropwhile(fun rebar_app_info:valid/1, Apps), - lists:foreach(fun(AppInfo) -> - AppDir = rebar_app_info:dir(AppInfo), - C = rebar_config:consult(AppDir), - S = rebar_state:new(rebar_state:new(), C, AppDir), - rebar_prv_compile:build(S, AppInfo), - true = code:add_patha(filename:join(AppDir, "ebin")) - end, ToBuild), - + [build_plugin(AppInfo) || AppInfo <- ToBuild], plugin_providers(Plugin) catch C:T -> @@ -48,6 +41,13 @@ handle_plugin(Plugin, State) -> false end. +build_plugin(AppInfo) -> + AppDir = rebar_app_info:dir(AppInfo), + C = rebar_config:consult(AppDir), + S = rebar_state:new(rebar_state:new(), C, AppDir), + rebar_prv_compile:compile(S, AppInfo), + true = code:add_patha(filename:join(AppDir, "ebin")). + plugin_providers({Plugin, _, _}) when is_atom(Plugin) -> validate_plugin(Plugin); plugin_providers({Plugin, _}) when is_atom(Plugin) -> diff --git a/src/rebar_prv_compile.erl b/src/rebar_prv_compile.erl index 073394c..c4cca16 100644 --- a/src/rebar_prv_compile.erl +++ b/src/rebar_prv_compile.erl @@ -6,7 +6,7 @@ do/1, format_error/1]). --export([build/2]). +-export([compile/2]). -include("rebar.hrl"). @@ -55,34 +55,36 @@ do(State) -> %% Use the project State for building project apps %% Set hooks to empty so top-level hooks aren't run for each project app State2 = rebar_state:set(rebar_state:set(State1, post_hooks, []), pre_hooks, []), - build_apps(State2, ProjectApps), + ProjectApps1 = build_apps(State2, ProjectApps), rebar_hooks:run_compile_hooks(Cwd, post_hooks, compile, State1), - {ok, State1}. + {ok, rebar_state:project_apps(State1, ProjectApps1)}. -spec format_error(any()) -> iolist(). format_error(Reason) -> io_lib:format("~p", [Reason]). build_apps(State, Apps) -> - lists:foreach(fun(AppInfo) -> - AppDir = rebar_app_info:dir(AppInfo), - S = case rebar_app_info:state(AppInfo) of - undefined -> - C = rebar_config:consult(AppDir), - rebar_state:new(State, C, AppDir); - AppState -> - AppState - end, - - %% Legacy hook support - rebar_hooks:run_compile_hooks(AppDir, pre_hooks, compile, S), - build(S, AppInfo), - rebar_hooks:run_compile_hooks(AppDir, post_hooks, compile, S), - true = code:add_patha(filename:join(AppDir, "ebin")) - end, Apps). - -build(State, AppInfo) -> + [build_app(State, AppInfo) || AppInfo <- Apps]. + +build_app(State, AppInfo) -> + AppDir = rebar_app_info:dir(AppInfo), + S = case rebar_app_info:state(AppInfo) of + undefined -> + C = rebar_config:consult(AppDir), + rebar_state:new(State, C, AppDir); + AppState -> + AppState + end, + + %% Legacy hook support + rebar_hooks:run_compile_hooks(AppDir, pre_hooks, compile, S), + AppInfo1 = compile(S, AppInfo), + rebar_hooks:run_compile_hooks(AppDir, post_hooks, compile, S), + true = code:add_patha(filename:join(AppDir, "ebin")), + AppInfo1. + +compile(State, AppInfo) -> ?INFO("Compiling ~s", [rebar_app_info:name(AppInfo)]), rebar_erlc_compiler:compile(State, ec_cnv:to_list(rebar_app_info:dir(AppInfo))), case rebar_otp_app:compile(State, AppInfo) of diff --git a/test/rebar_deps_SUITE.erl b/test/rebar_deps_SUITE.erl index 3228bc6..0e5c6fe 100644 --- a/test/rebar_deps_SUITE.erl +++ b/test/rebar_deps_SUITE.erl @@ -3,7 +3,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). -all() -> [sub_app_deps, {group, git}, {group, pkg}]. +all() -> [sub_app_deps, newly_added_dep, {group, git}, {group, pkg}]. groups() -> [{all, [], [flat, pick_highest_left, pick_highest_right, @@ -29,6 +29,8 @@ init_per_group(_, Config) -> end_per_group(_, Config) -> Config. +init_per_testcase(newly_added_dep, Config) -> + rebar_test_utils:init_rebar_state(Config); init_per_testcase(sub_app_deps, Config) -> rebar_test_utils:init_rebar_state(Config); init_per_testcase(Case, Config) -> @@ -129,9 +131,9 @@ expand_deps(pkg, [{Name, Vsn, Deps} | Rest]) -> setup_project(Case, Config0, Deps) -> DepsType = ?config(deps_type, Config0), Config = rebar_test_utils:init_rebar_state( - Config0, - atom_to_list(Case)++"_"++atom_to_list(DepsType)++"_" - ), + Config0, + atom_to_list(Case)++"_"++atom_to_list(DepsType)++"_" + ), AppDir = ?config(apps, Config), rebar_test_utils:create_app(AppDir, "A", "0.0.0", [kernel, stdlib]), TopDeps = top_level_deps(Deps), @@ -218,6 +220,40 @@ sub_app_deps(Config) -> Config, RebarConfig, ["compile"], {ok, [{app, Name}, {dep, "a"}, {dep, "b", "1.0.0"}]}). +%% Newly added dependency after locking +newly_added_dep(Config) -> + AppDir = ?config(apps, Config), + Deps = expand_deps(git, [{"a", "1.0.0", []} + ,{"b", "1.0.0", [{"c", "1.0.0", []}]} + ,{"c", "2.0.0", []}]), + mock_git_resource:mock([{deps, flat_deps(Deps)}]), + + Name = rebar_test_utils:create_random_name("app_"), + Vsn = rebar_test_utils:create_random_vsn(), + + SubAppsDir = filename:join([AppDir, Name]), + rebar_test_utils:create_app(SubAppsDir, Name, Vsn, [kernel, stdlib]), + + TopDeps = top_level_deps(expand_deps(git, [{"b", "1.0.0", []}])), + {ok, RebarConfig} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps}])), + rebar_test_utils:run_and_check( + Config, RebarConfig, ["compile"], + {ok, [{app, Name}, {dep, "b", "1.0.0"}, {dep, "c", "1.0.0"}]}), + + %% Add a and c to top level + TopDeps2 = top_level_deps(expand_deps(git, [{"a", "1.0.0", []} + ,{"c", "2.0.0", []} + ,{"b", "1.0.0", []}])), + {ok, RebarConfig2} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps2}])), + LockFile = filename:join(AppDir, "rebar.lock"), + RebarConfig3 = rebar_config:merge_locks(RebarConfig2, + rebar_config:consult_file(LockFile)), + + %% a should now be installed and c should not change + rebar_test_utils:run_and_check( + Config, RebarConfig3, ["compile"], + {ok, [{app, Name}, {dep, "a"}, {dep, "b", "1.0.0"}, {dep, "c", "1.0.0"}]}). + run(Config) -> {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), |