# SPDX-License-Identifier: Apache-2.0 # SPDX-FileCopyrightText: 2021 The Elixir Team # SPDX-FileCopyrightText: 2012 Plataformatec PREFIX ?= /usr/local TEST_FILES ?= "*_test.exs" SHARE_PREFIX ?= $(PREFIX)/share MAN_PREFIX ?= $(SHARE_PREFIX)/man CANONICAL := main/ ELIXIRC := bin/elixirc --ignore-module-conflict $(ELIXIRC_OPTS) ELIXIRC_MIN_SIG := $(ELIXIRC) -e 'Code.put_compiler_option :infer_signatures, []' ERLC := erlc -I lib/elixir/include ERL_MAKE := erl -make ERL := erl -I lib/elixir/include -noshell -pa lib/elixir/ebin GENERATE_APP := $(CURDIR)/lib/elixir/scripts/generate_app.escript VERSION := $(strip $(shell cat VERSION)) Q := @ LIBDIR := lib BINDIR := bin INSTALL = install INSTALL_DIR = $(INSTALL) -m755 -d INSTALL_DATA = $(INSTALL) -m644 INSTALL_PROGRAM = $(INSTALL) -m755 GIT_REVISION = $(strip $(shell git rev-parse HEAD 2> /dev/null )) GIT_TAG = $(strip $(shell head="$(call GIT_REVISION)"; git tag --points-at $$head 2> /dev/null | grep -v latest | tail -1)) SOURCE_DATE_EPOCH_PATH = lib/elixir/tmp/ebin_reproducible SOURCE_DATE_EPOCH_FILE = $(SOURCE_DATE_EPOCH_PATH)/SOURCE_DATE_EPOCH .PHONY: cover install install_man build_plt clean_plt dialyze test check_reproducible clean clean_elixir clean_man format docs Docs.zip Precompiled.zip zips .NOTPARALLEL: #==> Functions define CHECK_ERLANG_RELEASE erl -noshell -eval '{V,_} = string:to_integer(erlang:system_info(otp_release)), io:fwrite("~s", [is_integer(V) and (V >= 27)])' -s erlang halt | grep -q '^true'; \ if [ $$? != 0 ]; then \ echo "At least Erlang/OTP 27.0 is required to build Elixir"; \ exit 1; \ fi endef define APP_TEMPLATE $(1): lib/$(1)/ebin/Elixir.$(2).beam lib/$(1)/ebin/$(1).app lib/$(1)/ebin/$(1).app: lib/$(1)/mix.exs $(Q) cd lib/$(1) && ../../bin/elixir -e 'Mix.start(:permanent, [])' -r mix.exs -e 'Mix.Task.run("compile.app", ~w[--compile-path ebin])' lib/$(1)/ebin/Elixir.$(2).beam: $(wildcard lib/$(1)/lib/*.ex) $(wildcard lib/$(1)/lib/*/*.ex) $(wildcard lib/$(1)/lib/*/*/*.ex) @ echo "==> $(1) (compile)" @ rm -rf lib/$(1)/ebin $(Q) cd lib/$(1) && ../../$$(ELIXIRC) "lib/**/*.ex" -o ebin test_$(1): test_formatted $(1) @ echo "==> $(1) (ex_unit)" $(Q) cd lib/$(1) && ../../bin/elixir -r "test/test_helper.exs" -pr "test/**/$(TEST_FILES)"; cover/ex_unit_$(1).coverdata: $(Q) COVER="1" $(MAKE) test_$(1) cover/combined.coverdata: cover/ex_unit_$(1).coverdata endef define WRITE_SOURCE_DATE_EPOCH $(shell mkdir -p $(SOURCE_DATE_EPOCH_PATH) && bin/elixir -e \ 'IO.puts System.build_info()[:date] \ |> DateTime.from_iso8601() \ |> elem(1) \ |> DateTime.to_unix()' > $(SOURCE_DATE_EPOCH_FILE)) endef define READ_SOURCE_DATE_EPOCH $(strip $(shell cat $(SOURCE_DATE_EPOCH_FILE))) endef #==> Compilation tasks APP := lib/elixir/ebin/elixir.app EEX := lib/eex/ebin/Elixir.EEx.beam ELIXIR := lib/elixir/ebin/elixir.beam PARSER := lib/elixir/src/elixir_parser.erl KERNEL := lib/elixir/ebin/Elixir.Kernel.beam UNICODE := lib/elixir/ebin/Elixir.String.Unicode.beam default: compile compile: erlang elixir erlang: $(ELIXIR) $(ELIXIR): $(PARSER) lib/elixir/src/* $(Q) if [ ! -f $(APP) ]; then $(call CHECK_ERLANG_RELEASE); fi $(Q) cd lib/elixir && mkdir -p ebin && $(ERL_MAKE) $(Q) $(GENERATE_APP) $(VERSION) $(PARSER): lib/elixir/src/elixir_parser.yrl $(Q) erlc -o $@ +'{verbose,true}' +'{report,true}' $< # Since Mix depends on EEx and EEx depends on Mix, # we first compile EEx without the .app file, # then Mix, and then compile EEx fully elixir: stdlib $(EEX) mix ex_unit logger eex iex stdlib: $(KERNEL) $(UNICODE) $(APP) $(KERNEL): lib/elixir/src/* lib/elixir/lib/*.ex lib/elixir/lib/*/*.ex lib/elixir/lib/*/*/*.ex VERSION $(Q) if [ ! -f $(KERNEL) ]; then \ echo "==> bootstrap (compile)"; \ $(ERL) -s elixir_compiler bootstrap -s erlang halt; \ "$(MAKE)" unicode; \ fi @ echo "==> elixir (compile)"; $(Q) cd lib/elixir && ../../$(ELIXIRC_MIN_SIG) "lib/**/*.ex" -o ebin; $(Q) $(GENERATE_APP) $(VERSION) $(Q) bin/elixir lib/elixir/scripts/infer.exs; $(APP): lib/elixir/src/elixir.app.src $(GENERATE_APP) $(Q) $(GENERATE_APP) $(VERSION) unicode: $(UNICODE) $(UNICODE): lib/elixir/unicode/* @ echo "==> unicode (compile)"; $(Q) $(ELIXIRC_MIN_SIG) lib/elixir/unicode/unicode.ex -o lib/elixir/ebin; $(Q) $(ELIXIRC_MIN_SIG) lib/elixir/unicode/tokenizer.ex -o lib/elixir/ebin; $(Q) $(ELIXIRC_MIN_SIG) lib/elixir/unicode/security.ex -o lib/elixir/ebin; $(eval $(call APP_TEMPLATE,ex_unit,ExUnit)) $(eval $(call APP_TEMPLATE,logger,Logger)) $(eval $(call APP_TEMPLATE,eex,EEx)) $(eval $(call APP_TEMPLATE,mix,Mix)) $(eval $(call APP_TEMPLATE,iex,IEx)) install: compile @ echo "==> elixir (install)" $(Q) for dir in lib/*; do \ rm -rf $(DESTDIR)$(PREFIX)/$(LIBDIR)/elixir/$$dir/ebin; \ $(INSTALL_DIR) "$(DESTDIR)$(PREFIX)/$(LIBDIR)/elixir/$$dir/ebin"; \ $(INSTALL_DATA) $$dir/ebin/* "$(DESTDIR)$(PREFIX)/$(LIBDIR)/elixir/$$dir/ebin"; \ done $(Q) $(INSTALL_DIR) "$(DESTDIR)$(PREFIX)/$(LIBDIR)/elixir/bin" $(Q) $(INSTALL_PROGRAM) $(filter-out %.ps1, $(filter-out %.bat, $(wildcard bin/*))) "$(DESTDIR)$(PREFIX)/$(LIBDIR)/elixir/bin" $(Q) $(INSTALL_DIR) "$(DESTDIR)$(PREFIX)/$(BINDIR)" $(Q) for file in "$(DESTDIR)$(PREFIX)"/$(LIBDIR)/elixir/bin/*; do \ ln -sf "../$(LIBDIR)/elixir/bin/$${file##*/}" "$(DESTDIR)$(PREFIX)/$(BINDIR)/"; \ done "$(MAKE)" install_man check_reproducible: compile $(Q) echo "==> Checking for reproducible builds..." $(Q) rm -rf lib/*/tmp/ebin_reproducible/ $(call WRITE_SOURCE_DATE_EPOCH) $(Q) mkdir -p lib/elixir/tmp/ebin_reproducible/ \ lib/eex/tmp/ebin_reproducible/ \ lib/ex_unit/tmp/ebin_reproducible/ \ lib/iex/tmp/ebin_reproducible/ \ lib/logger/tmp/ebin_reproducible/ \ lib/mix/tmp/ebin_reproducible/ $(Q) mv lib/elixir/ebin/* lib/elixir/tmp/ebin_reproducible/ $(Q) mv lib/eex/ebin/* lib/eex/tmp/ebin_reproducible/ $(Q) mv lib/ex_unit/ebin/* lib/ex_unit/tmp/ebin_reproducible/ $(Q) mv lib/iex/ebin/* lib/iex/tmp/ebin_reproducible/ $(Q) mv lib/logger/ebin/* lib/logger/tmp/ebin_reproducible/ $(Q) mv lib/mix/ebin/* lib/mix/tmp/ebin_reproducible/ $(Q) rm -rf lib/*/ebin SOURCE_DATE_EPOCH=$(call READ_SOURCE_DATE_EPOCH) "$(MAKE)" compile $(Q) echo "Diffing..." $(Q) bin/elixir lib/elixir/scripts/diff.exs lib/elixir/ebin/ lib/elixir/tmp/ebin_reproducible/ $(Q) bin/elixir lib/elixir/scripts/diff.exs lib/eex/ebin/ lib/eex/tmp/ebin_reproducible/ $(Q) bin/elixir lib/elixir/scripts/diff.exs lib/ex_unit/ebin/ lib/ex_unit/tmp/ebin_reproducible/ $(Q) bin/elixir lib/elixir/scripts/diff.exs lib/iex/ebin/ lib/iex/tmp/ebin_reproducible/ $(Q) bin/elixir lib/elixir/scripts/diff.exs lib/logger/ebin/ lib/logger/tmp/ebin_reproducible/ $(Q) bin/elixir lib/elixir/scripts/diff.exs lib/mix/ebin/ lib/mix/tmp/ebin_reproducible/ $(Q) echo "Builds are reproducible" clean: clean_man rm -rf ebin rm -rf lib/*/ebin rm -rf $(PARSER) rm -rf lib/*/_build/ rm -rf lib/*/tmp/ rm -rf lib/elixir/test/ebin/ rm -rf lib/mix/test/fixtures/deps_on_git_repo/ rm -rf lib/mix/test/fixtures/git_rebar/ rm -rf lib/mix/test/fixtures/git_repo/ rm -rf lib/mix/test/fixtures/git_sparse_repo/ rm -rf lib/mix/test/fixtures/archive/ebin/ rm -f erl_crash.dump rm -rf cover clean_elixir: $(Q) rm -f lib/*/ebin/Elixir.*.beam #==> Documentation tasks SOURCE_REF = $(shell tag="$(call GIT_TAG)" revision="$(call GIT_REVISION)"; echo "$${tag:-$$revision}") DOCS_COMPILE = CANONICAL=$(CANONICAL) bin/elixir ../ex_doc/bin/ex_doc "$(1)" "$(VERSION)" "lib/$(2)/ebin" --main "$(3)" --source-url "https://github.com/elixir-lang/elixir" --source-ref "$(call SOURCE_REF)" --logo lib/elixir/pages/images/logo.png --output doc/$(2) --canonical "https://$(2).hexdocs.pm/$(CANONICAL)" --homepage-url "https://elixir-lang.org/docs.html" $(DOCS_OPTIONS) $(4) DOCS_CONFIG = bin/elixir lib/elixir/scripts/docs_config.exs "$(1)" docs: compile ../ex_doc/bin/ex_doc docs_elixir docs_eex docs_mix docs_iex docs_ex_unit docs_logger docs_elixir: compile ../ex_doc/bin/ex_doc @ echo "==> ex_doc (elixir)" $(Q) rm -rf doc/elixir $(call DOCS_COMPILE,Elixir,elixir,Kernel,--config "lib/elixir/scripts/elixir_docs.exs") $(call DOCS_CONFIG,elixir) docs_eex: compile ../ex_doc/bin/ex_doc @ echo "==> ex_doc (eex)" $(Q) rm -rf doc/eex $(call DOCS_COMPILE,EEx,eex,EEx,--config "lib/elixir/scripts/mix_docs.exs") $(call DOCS_CONFIG,eex) docs_mix: compile ../ex_doc/bin/ex_doc @ echo "==> ex_doc (mix)" $(Q) rm -rf doc/mix $(call DOCS_COMPILE,Mix,mix,Mix,--config "lib/elixir/scripts/mix_docs.exs") $(call DOCS_CONFIG,mix) docs_iex: compile ../ex_doc/bin/ex_doc @ echo "==> ex_doc (iex)" $(Q) rm -rf doc/iex $(call DOCS_COMPILE,IEx,iex,IEx,--config "lib/elixir/scripts/mix_docs.exs") $(call DOCS_CONFIG,iex) docs_ex_unit: compile ../ex_doc/bin/ex_doc @ echo "==> ex_doc (ex_unit)" $(Q) rm -rf doc/ex_unit $(call DOCS_COMPILE,ExUnit,ex_unit,ExUnit,--config "lib/elixir/scripts/mix_docs.exs") $(call DOCS_CONFIG,ex_unit) docs_logger: compile ../ex_doc/bin/ex_doc @ echo "==> ex_doc (logger)" $(Q) rm -rf doc/logger $(call DOCS_COMPILE,Logger,logger,Logger,--config "lib/elixir/scripts/mix_docs.exs") $(call DOCS_CONFIG,logger) ../ex_doc/bin/ex_doc: @ echo "ex_doc is not found in ../ex_doc as expected. See CONTRIBUTING.md for more information." @ false #==> Zip tasks Docs.zip: docs rm -f Docs.zip zip -9 -r Docs.zip CHANGELOG.md doc LICENSE README.md @ echo "Docs file created $(CURDIR)/Docs.zip" Precompiled.zip: build_man compile rm -f Precompiled.zip zip -9 -r Precompiled.zip bin CHANGELOG.md lib/*/ebin lib/*/lib LICENSE Makefile man README.md VERSION @ echo "Precompiled file created $(CURDIR)/Precompiled.zip" #==> Test tasks test: test_formatted test_erlang test_elixir test_windows: test test_taskkill test_taskkill: taskkill //IM erl.exe //F //T //FI "MEMUSAGE gt 0" taskkill //IM epmd.exe //F //T //FI "MEMUSAGE gt 0" TEST_ERL = lib/elixir/test/erlang TEST_EBIN = lib/elixir/test/ebin TEST_ERLS = $(addprefix $(TEST_EBIN)/, $(addsuffix .beam, $(basename $(notdir $(wildcard $(TEST_ERL)/*.erl))))) define FORMAT $(Q) if [ "$(OS)" = "Windows_NT" ]; then \ cmd //C call ./bin/mix.bat format $(1); \ else \ bin/elixir bin/mix format $(1); \ fi endef format: compile $(call FORMAT) test_formatted: compile $(call FORMAT,--check-formatted) test_erlang: compile $(TEST_ERLS) @ echo "==> elixir (eunit)" $(Q) $(ERL) -pa $(TEST_EBIN) -s test_helper test; @ echo "" $(TEST_EBIN)/%.beam: $(TEST_ERL)/%.erl $(Q) mkdir -p $(TEST_EBIN) $(Q) $(ERLC) -o $(TEST_EBIN) $< test_elixir: test_stdlib test_ex_unit test_logger test_eex test_iex test_mix test_stdlib: compile @ echo "==> elixir (ex_unit)" $(Q) exec epmd & exit $(Q) if [ "$(OS)" = "Windows_NT" ]; then \ cd lib/elixir && cmd //C call ../../bin/elixir.bat --sname primary -r "test/elixir/test_helper.exs" -pr "test/elixir/**/$(TEST_FILES)"; \ else \ cd lib/elixir && ../../bin/elixir --sname primary -r "test/elixir/test_helper.exs" -pr "test/elixir/**/$(TEST_FILES)"; \ fi cover/ex_unit_elixir.coverdata: $(Q) COVER="1" $(MAKE) test_stdlib cover/combined.coverdata: cover/ex_unit_elixir.coverdata cover/combined.coverdata: bin/elixir ./lib/elixir/scripts/cover.exs cover: cover/combined.coverdata #==> Dialyzer tasks DIALYZER_OPTS = --no_check_plt --fullpath -Werror_handling -Wunmatched_returns -Wunderspecs PLT = .elixir.plt $(PLT): @ echo "==> Building PLT with Elixir's dependencies..." $(Q) dialyzer --output_plt $(PLT) --build_plt --apps erts kernel stdlib compiler syntax_tools parsetools tools ssl inets crypto runtime_tools ftp tftp mnesia public_key asn1 sasl clean_plt: $(Q) rm -f $(PLT) build_plt: clean_plt $(PLT) dialyze: compile $(PLT) @ echo "==> Dialyzing Elixir..." $(Q) dialyzer -pa lib/elixir/ebin --plt $(PLT) $(DIALYZER_OPTS) lib/*/ebin #==> Man page tasks build_man: man/iex.1 man/elixir.1 define BUILD_MANPAGES man/$(APP).1: $(Q) cp man/$(APP).1.in man/$(APP).1 $(Q) sed -i.bak "/{COMMON}/r man/common" man/$(APP).1 $(Q) sed -i.bak "/{COMMON}/d" man/$(APP).1 $(Q) rm -f man/$(APP).1.bak endef $(foreach APP, elixir iex, $(eval $(BUILD_MANPAGES))) clean_man: rm -f man/elixir.1 rm -f man/elixir.1.bak rm -f man/iex.1 rm -f man/iex.1.bak install_man: build_man $(Q) mkdir -p $(DESTDIR)$(MAN_PREFIX)/man1 $(Q) $(INSTALL_DATA) man/elixir.1 $(DESTDIR)$(MAN_PREFIX)/man1 $(Q) $(INSTALL_DATA) man/elixirc.1 $(DESTDIR)$(MAN_PREFIX)/man1 $(Q) $(INSTALL_DATA) man/iex.1 $(DESTDIR)$(MAN_PREFIX)/man1 $(Q) $(INSTALL_DATA) man/mix.1 $(DESTDIR)$(MAN_PREFIX)/man1 "$(MAKE)" clean_man