Просмотр исходного кода

Use concrete for standard Makefile goodness

Seth Falcon 10 лет назад
Родитель
Сommit
23271fe27a
4 измененных файлов с 365 добавлено и 77 удалено
  1. 3 0
      .gitignore
  2. 35 77
      Makefile
  3. 230 0
      concrete.mk
  4. 97 0
      rebar.config.script

+ 3 - 0
.gitignore

@@ -8,3 +8,6 @@ doc/*.html
 /bench/tests
 /bench/deps
 
+/.concrete/
+/.rebar/
+/deps/

+ 35 - 77
Makefile

@@ -1,80 +1,38 @@
-ERLFLAGS= -pa $(CURDIR)/.eunit -pa $(CURDIR)/ebin -pa $(CURDIR)/deps/*/ebin
-
-DEPS_PLT=$(CURDIR)/.deps_plt
-
-# =============================================================================
-# Verify that the programs we need to run are installed on this system
-# =============================================================================
-ERL = $(shell which erl)
-
-ifeq ($(ERL),)
-$(error "Erlang not available on this system")
-endif
-
-REBAR=$(shell which rebar)
-
-# If building on travis, use the rebar in the current directory
-ifeq ($(TRAVIS),true)
-REBAR=$(CURDIR)/rebar
+# This Makefile written by concrete
+#
+# {concrete_makefile_version, 2}
+#
+# ANY CHANGES TO THIS FILE WILL BE OVERWRITTEN on `concrete update`
+# IF YOU WANT TO CHANGE ANY OF THESE LINES BELOW, COPY THEM INTO
+# custom.mk FIRST
+
+# Use this to override concrete's default dialyzer options of
+# -Wunderspecs
+# DIALYZER_OPTS = ...
+
+# List dependencies that you do NOT want to be included in the
+# dialyzer PLT for the project here.  Typically, you would list a
+# dependency here if it isn't spec'd well and doesn't play nice with
+# dialyzer or otherwise mucks things up.
+#
+# DIALYZER_SKIP_DEPS = bad_dep_1 \
+#                      bad_dep_2
+
+# If you want to add dependencies to the default "all" target provided
+# by concrete, add them here (along with make rules to build them if needed)
+# ALL_HOOK = ...
+
+# custom.mk is totally optional
+custom_rules_file = $(wildcard custom.mk)
+ifeq ($(custom_rules_file),custom.mk)
+    include custom.mk
 endif
 
-ifeq ($(REBAR),)
-REBAR=$(CURDIR)/rebar
+concrete_rules_file = $(wildcard concrete.mk)
+ifeq ($(concrete_rules_file),concrete.mk)
+    include concrete.mk
+else
+    all:
+	@echo "ERROR: missing concrete.mk"
+	@echo "  run: concrete update"
 endif
-
-.PHONY: all compile test dialyzer clean distclean doc
-
-all: compile test dialyzer
-
-REBAR_URL=https://github.com/rebar/rebar/wiki/rebar
-$(REBAR):
-	curl -Lo rebar $(REBAR_URL) || wget $(REBAR_URL)
-	chmod a+x rebar
-
-get-rebar: $(REBAR)
-
-compile: $(REBAR)
-	$(REBAR) compile
-
-eunit: compile clean-common-test-data
-	$(REBAR) skip_deps=true eunit
-
-ct: compile clean-common-test-data
-	mkdir -p $(CURDIR) logs
-	ct_run -pa $(CURDIR)/ebin \
-	-pa $(CURDIR)/deps/*/ebin \
-	-logdir $(CURDIR)/logs \
-	-dir $(CURDIR)/test/ \
-	-cover cover.spec
-
-test: compile dialyzer eunit ct
-
-$(DEPS_PLT): compile
-	@echo Building local erts plt at $(DEPS_PLT)
-	@echo
-	$(DIALYZER) --output_plt $(DEPS_PLT) --build_plt \
-	   --apps erts kernel stdlib -r deps
-
-dialyzer: compile $(DEPS_PLT)
-	@dialyzer -Wunderspecs -r ebin
-
-doc:
-	$(REBAR) doc skip_deps=true
-
-clean-common-test-data:
-# We have to do this because of the unique way we generate test
-# data. Without this rebar eunit gets very confused
-	- rm -rf $(CURDIR)/test/*_SUITE_data
-
-clean: clean-common-test-data $(REBAR)
-	- rm -rf $(CURDIR)/test/*.beam
-	- rm -rf $(CURDIR)/logs
-	- rm -rf $(CURDIR)/ebin
-	$(REBAR) skip_deps=true clean
-
-distclean: clean
-	- rm -rf $(DEPS_PLT)
-	$(REBAR) delete-deps
-
-demo_shell: compile test
-	@erl -pa .eunit ebin -config pooler-example -s pooler manual_start

+ 230 - 0
concrete.mk

@@ -0,0 +1,230 @@
+# =============================================================================
+# Verify that the programs we need to run are installed on this system
+# =============================================================================
+ERL = $(shell which erl)
+
+ifeq ($(ERL),)
+$(error "Erlang not available on this system")
+endif
+
+# If building on travis, use the rebar in the current directory
+ifeq ($(TRAVIS),true)
+REBAR = $(CURDIR)/rebar
+endif
+
+# If there is a rebar in the current directory, use it
+ifeq ($(wildcard rebar),rebar)
+REBAR = $(CURDIR)/rebar
+endif
+
+# Fallback to rebar on PATH
+REBAR ?= $(shell which rebar)
+
+# And finally, prep to download rebar if all else fails
+ifeq ($(REBAR),)
+REBAR = $(CURDIR)/rebar
+endif
+
+# If we have a rebar.config.lock file, use it!
+ifeq ($(wildcard rebar.config.lock),rebar.config.lock)
+REBAR_CONFIG = rebar.config.lock
+else
+REBAR_CONFIG = rebar.config
+endif
+
+# This is the variable to use to respect the lock file
+REBARC = $(REBAR) -C $(REBAR_CONFIG)
+
+# For use on Travis CI, skip dialyzer for R14 and R15. Newer versions
+# have a faster dialyzer that is less likely to cause a build timeout.
+DIALYZER = dialyzer
+R14 = $(findstring R14,$(TRAVIS_OTP_RELEASE))
+R15 = $(findstring R15,$(TRAVIS_OTP_RELEASE))
+ifneq ($(R14),)
+DIALYZER = echo "SKIPPING dialyzer"
+endif
+ifneq ($(R15),)
+DIALYZER = echo "SKIPPING dialyzer"
+endif
+ifneq ($(SKIP_DIALYZER),)
+DIALYZER = echo "SKIPPING dialyzer"
+endif
+
+REBAR_URL=https://github.com/rebar/rebar/wiki/rebar
+
+DEPS ?= $(CURDIR)/deps
+
+DIALYZER_OPTS ?=
+
+# Find all the deps the project has by searching the deps dir
+ALL_DEPS = $(notdir $(wildcard deps/*))
+# Create a list of deps that should be used by dialyzer by doing a
+# complement on the sets
+DEPS_LIST = $(filter-out $(DIALYZER_SKIP_DEPS), $(ALL_DEPS))
+# Create the path structure from the dep names
+# so dialyzer can find the .beam files in the ebin dir
+# This list is then used by dialyzer in creating the local PLT
+DIALYZER_DEPS = $(foreach dep,$(DEPS_LIST),deps/$(dep)/ebin)
+
+DEPS_PLT = deps.plt
+
+ERLANG_DIALYZER_APPS ?= asn1 \
+                        compiler \
+                        crypto \
+                        edoc \
+                        erts \
+                        inets \
+                        kernel \
+                        mnesia \
+                        public_key \
+                        ssl \
+                        stdlib \
+                        syntax_tools \
+                        tools \
+                        xmerl
+
+PROJ = $(notdir $(CURDIR))
+
+# Let's compute $(BASE_PLT_ID) that identifies the base PLT to use for this project
+# and depends on your `$(ERLANG_DIALYZER_APPS)' list and your erlang version
+ERLANG_VERSION := $(shell $(ERL) -eval 'io:format("~s~n", [erlang:system_info(otp_release)]), halt().' -noshell)
+MD5_BIN := $(shell which md5 || which md5sum)
+ifeq ($(MD5_BIN),)
+# neither md5 nor md5sum, we just take the project name
+BASE_PLT_ID := $(PROJ)
+else
+BASE_PLT_ID := $(word 1, $(shell echo $(ERLANG_DIALYZER_APPS) $(ERLANG_VERSION) | $(MD5_BIN)))
+endif
+BASE_PLT := ~/.concrete_dialyzer_plt_$(BASE_PLT_ID)_$(ERLANG_VERSION).plt
+
+all: all_but_dialyzer dialyzer
+
+all_but_dialyzer: .concrete/DEV_MODE compile eunit $(ALL_HOOK)
+
+$(REBAR):
+	curl -Lo rebar $(REBAR_URL) || wget $(REBAR_URL)
+	chmod a+x rebar
+
+get-rebar: $(REBAR)
+
+.concrete/DEV_MODE:
+	@mkdir -p .concrete
+	@touch $@
+
+# Clean ebin and .eunit of this project
+clean:
+	@$(REBARC) clean skip_deps=true
+
+# Clean this project and all deps
+# Newer rebar requires -r to recursively clean
+allclean:
+	@($(REBARC) --help 2>&1|grep -q recursive && $(REBARC) -r clean) || $(REBARC) clean
+
+compile: $(DEPS)
+	@$(REBARC) compile
+
+$(DEPS):
+	@$(REBARC) get-deps
+
+# Full clean and removal of all deps. Remove deps first to avoid
+# wasted effort of cleaning deps before nuking them.
+distclean:
+	@rm -rf deps $(DEPS_PLT)
+	@$(REBARC) clean
+
+eunit:
+	@$(REBARC) skip_deps=true eunit
+
+test: eunit
+
+# Only include local PLT if we have deps that we are going to analyze
+ifeq ($(strip $(DIALYZER_DEPS)),)
+dialyzer: $(BASE_PLT)
+	@$(DIALYZER) $(DIALYZER_OPTS) -r ebin
+else
+dialyzer: $(BASE_PLT) $(DEPS_PLT)
+	@$(DIALYZER) $(DIALYZER_OPTS) --plts $(BASE_PLT) $(DEPS_PLT) -r ebin
+
+$(DEPS_PLT):
+	@$(DIALYZER) --build_plt $(DIALYZER_DEPS) --output_plt $(DEPS_PLT)
+endif
+
+$(BASE_PLT):
+	@echo "Missing $(BASE_PLT). Please wait while a new PLT is compiled."
+	$(DIALYZER) --build_plt --apps $(ERLANG_DIALYZER_APPS) --output_plt $(BASE_PLT)
+	@echo "now try your build again"
+
+doc:
+	@$(REBARC) doc skip_deps=true
+
+shell:
+	@$(ERL) -pa deps/*/ebin ebin .eunit
+
+tags:
+	find src deps -name "*.[he]rl" -print | etags -
+
+# Releases via relx. we will install a local relx, as we do for rebar,
+# if we don't find one on PATH.
+RELX_CONFIG ?= $(CURDIR)/relx.config
+RELX = $(shell which relx)
+RELX_OPTS ?=
+RELX_OUTPUT_DIR ?= _rel
+ifeq ($(RELX),)
+RELX = $(CURDIR)/relx
+RELX_VERSION = 1.0.4
+else
+RELX_VERSION = $(shell relx --version)
+endif
+RELX_URL = https://github.com/erlware/relx/releases/download/v$(RELX_VERSION)/relx
+
+# relx introduced a breaking change in v1: the output doesn't have the same structure
+# see https://github.com/erlware/relx/releases/tag/v1.0.0
+ifeq ($(shell echo $(RELX_VERSION) | head -c 1), 0)
+RELX_RELEASE_DIR = $(RELX_OUTPUT_DIR)
+else
+RELX_RELEASE_DIR = $(RELX_OUTPUT_DIR)/$(PROJ)
+endif
+
+$(RELX):
+	curl -Lo relx $(RELX_URL) || wget $(RELX_URL)
+	chmod a+x relx
+
+rel: relclean all_but_dialyzer $(RELX)
+	@$(RELX) -c $(RELX_CONFIG) -o $(RELX_OUTPUT_DIR) $(RELX_OPTS)
+
+devrel: rel
+devrel: lib_dir=$(wildcard $(RELX_RELEASE_DIR)/lib/$(PROJ)-* )
+devrel:
+	@/bin/echo Symlinking deps and apps into release
+	@rm -rf $(lib_dir); mkdir -p $(lib_dir)
+	@ln -sf `pwd`/ebin $(lib_dir)
+	@ln -sf `pwd`/priv $(lib_dir)
+	@ln -sf `pwd`/src $(lib_dir)
+
+relclean:
+	rm -rf $(RELX_OUTPUT_DIR)
+
+## Release prep and dep locking. These recipes use $(REBAR), not
+## $(REBARC) in order to NOT use the lock file since they are
+## concerned with the task of updating the lock file.
+BUMP ?= patch
+prepare_release: distclean unlocked_deps unlocked_compile update_locked_config rel
+	@echo 'release prepared, bumping version'
+	@$(REBAR) bump-rel-version version=$(BUMP)
+
+unlocked_deps:
+	@echo 'Fetching deps as: rebar -C rebar.config'
+	@$(REBAR) -C rebar.config get-deps
+
+# When running the prepare_release target, we have to ensure that a
+# compile occurs using the unlocked rebar.config. If a dependency has
+# been removed, then using the locked version that contains the stale
+# dep will cause a compile error.
+unlocked_compile:
+	@$(REBAR) -C rebar.config compile
+
+update_locked_config:
+	@$(REBAR) lock-deps skip_deps=true
+
+
+.PHONY: all all_but_dialyzer compile eunit test dialyzer clean allclean relclean distclean doc tags get-rebar rel devrel

+ 97 - 0
rebar.config.script

@@ -0,0 +1,97 @@
+%% -*- mode: erlang -*-
+%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+
+%% rebar.config.script GENERATED BY concrete
+%%
+%% YOU SHOULDN'T NEED TO EDIT THIS FILE
+%%
+{concrete_rebar_script_version, 3}.
+
+%% We need the following helper function to merge dev only options
+%% into the values provided by rebar.config.
+
+%% Merge the list values in `ToAdd' into the list found at key `Key'
+%% in proplist `C'. Don't duplicate items. New Items are added to the
+%% front of existing items. It is an error if the value at `Key' is
+%% not a list in `C'.
+MergeConfig = fun(skip, C) ->
+                      C;
+                 ({Key, ToAdd}, C) ->
+                      case lists:keyfind(Key, 1, C) of
+                          false ->
+                              lists:keystore(Key, 1, C, {Key, ToAdd});
+                          {Key, List} when is_list(List) ->
+                              %% remove items in ToAdd already in List
+                              ToAdd1 = [ I || I <- ToAdd, not lists:member(I, List) ],
+                              lists:keystore(Key, 1, C, {Key, List ++ ToAdd1 })
+                      end
+              end,
+
+%% -- Add development only options if we are a top-level build  --
+%%
+%% If a file named `.concrete/DEV_MODE' exists, we assume we are a
+%% top-level build (not being built as a dep of another project). We
+%% add the deps from dev_only_deps defined in rebar.config, add
+%% concrete as a dep, and define the compiler macro DEV_ONLY.
+
+%% This macro can be used to conditionally enable code (e.g. tests)
+%% that depend on development only dependencies.
+ErlOpts = {erl_opts, [{d, 'DEV_ONLY'}]},
+
+%% Development only dependencies can be specified in the main
+%% rebar.config. This file should not need to be edited directly.
+DevOnlyDeps = case lists:keyfind(dev_only_deps, 1, CONFIG) of
+                  false ->
+                      skip;
+                  {dev_only_deps, DOD} ->
+                      {deps, DOD}
+              end,
+
+EDown = case proplists:get_value(use_edown, CONFIG) of
+               false ->
+                   skip;
+               _ ->
+                DocOpts = case lists:keymember(edoc_opts, 1, CONFIG) of
+                              true ->
+                                  skip;
+                              false ->
+                                  {edoc_opts, [{doclet, edown_doclet}]}
+                          end,
+                [DocOpts,
+                 {deps,
+                  [{edown, ".*",
+                    {git, "git://github.com/seth/edown.git",
+                     {branch, "master"}}}]}]
+           end,
+
+LockDeps = case proplists:get_value(use_lock_deps, CONFIG) of
+               false ->
+                   skip;
+               V ->
+                   Tag = case V of
+                             {_, _} = T -> T;
+                             _ -> {branch, "master"}
+                         end,
+                   [{deps,
+                     [{rebar_lock_deps_plugin, ".*",
+                       {git, "git://github.com/seth/rebar_lock_deps_plugin.git",
+                        Tag}}]},
+                    {plugins, [rebar_lock_deps_plugin]}]
+           end,
+
+ConfigPath = filename:dirname(SCRIPT),
+DevMarker = filename:join([ConfigPath, ".concrete/DEV_MODE"]),
+
+case filelib:is_file(DevMarker) of
+    true ->
+        ToMerge = lists:flatten([DevOnlyDeps, LockDeps, EDown, ErlOpts]),
+        lists:foldl(fun(I, C) -> MergeConfig(I, C) end,
+                    CONFIG, ToMerge);
+    false ->
+        %% If the .concrete/ marker is not present, this script simply
+        %% returns the config specified in rebar.config. This will be
+        %% the behavior when the project is built as a dependency of
+        %% another project.
+        CONFIG
+end.