From 69157c7b68987acbf56c00a2a924c00c9faad6ae Mon Sep 17 00:00:00 2001 From: Przemyslaw Rzepecki Date: Mon, 18 Dec 2017 09:27:49 +0100 Subject: [PATCH] Udated Erlang support. This adds a scaner method to track Erlang heders dependencies. Support for EUnit tests Support for EDocs Support for ERL, ERLC, ERLC_FLAGS environment settings. --- playground/erlang/hello.erl | 35 +++++++- playground/erlang/hello_eunit.erl | 10 +++ playground/erlang/inc/hello.hrl | 1 + playground/erlang/wscript | 5 +- waflib/extras/erlang.py | 134 ++++++++++++++++++++++++++++-- 5 files changed, 175 insertions(+), 10 deletions(-) create mode 100644 playground/erlang/hello_eunit.erl create mode 100644 playground/erlang/inc/hello.hrl diff --git a/playground/erlang/hello.erl b/playground/erlang/hello.erl index ae71681f..d58a3731 100644 --- a/playground/erlang/hello.erl +++ b/playground/erlang/hello.erl @@ -1,5 +1,36 @@ % what a weird language ... :-) +%%% @author Przemyslaw Rzepecki +%%% @version 0.01 + +%%% @doc == Hello World, Example Module == +%%% This module contains some Erlang code for WAF build system support for +%%% Erlang language. +%%% @end -module(hello). --export([hello_world/0]). -hello_world() -> io:fwrite("hello, world\n"). +-export([say_hello/1, hello_world/0]). +-include("hello.hrl"). + +%%% ########################################################################### +%% @doc Returns a greetings string +%% +%% Some more specific description of the function should be written here... +%% +%% See http://erlang.org/doc/apps/edoc/users_guide.html for the complete Edoc +%% guide. +%% +%% @end +%%% ---------------------------------------------------------- +say_hello(waf) -> "Hello WAF, cool to see you!"; +say_hello(make) -> "Oh Make, you again..."; +say_hello(Other) -> "Hi " ++ Other. + + +%%% ########################################################################### +%% @doc Print a 'Hello World' string to stdout of the program.. +%% +%% This is an Erlang Version of the famous hello_world function. +%% +%% @end +%%% ---------------------------------------------------------- +hello_world() -> io:fwrite("~p~n", [?HELLO_WORLD]). diff --git a/playground/erlang/hello_eunit.erl b/playground/erlang/hello_eunit.erl new file mode 100644 index 00000000..024b76d2 --- /dev/null +++ b/playground/erlang/hello_eunit.erl @@ -0,0 +1,10 @@ +-module(hello_eunit). +-include_lib("eunit/include/eunit.hrl"). +-include("hello.hrl"). + +example_test_() -> + [ + ?_assert(hello:say_hello(waf) =:= "Hello WAF, cool to see you!"), + ?_assert(hello:say_hello(make) =:= "Oh Make, you again..."), + ?_assert(hello:say_hello("Mike") =:= "Hi Mike") + ]. diff --git a/playground/erlang/inc/hello.hrl b/playground/erlang/inc/hello.hrl new file mode 100644 index 00000000..c0dd4dc1 --- /dev/null +++ b/playground/erlang/inc/hello.hrl @@ -0,0 +1 @@ +-define(HELLO_WORLD, "hello, world"). diff --git a/playground/erlang/wscript b/playground/erlang/wscript index c594f711..2cf077d9 100644 --- a/playground/erlang/wscript +++ b/playground/erlang/wscript @@ -4,5 +4,8 @@ def configure(conf): conf.load('erlang') def build(bld): - bld(source='hello.erl') + bld(source='hello.erl', includes=['inc']) + # This requires EUnit. The Erlangs EUnit header files are available erlang-dev package. + bld(source=['hello_eunit.erl', 'hello.beam'], includes=['inc'], features="eunit") + bld(source=['hello.erl'], includes=['inc'], features="edoc") diff --git a/waflib/extras/erlang.py b/waflib/extras/erlang.py index b6349fe0..f9c15afe 100644 --- a/waflib/extras/erlang.py +++ b/waflib/extras/erlang.py @@ -1,19 +1,139 @@ #!/usr/bin/env python # encoding: utf-8 -# Thomas Nagy, 2010 (ita) +#Thomas Nagy, 2010 (ita), Przemyslaw Rzepecki, 2016 """ Erlang support """ -from waflib import TaskGen +from waflib import Task, TaskGen +from waflib.TaskGen import extension, feature, after_method, before_method +import os +import re -TaskGen.declare_chain(name = 'erlc', - rule = '${ERLC} ${ERLC_FLAGS} ${SRC[0].abspath()} -o ${TGT[0].name}', - ext_in = '.erl', - ext_out = '.beam') +# Those flags are required by the Erlang VM to execute/evaluate code in +# non-interactive mode. It is used in this tool to create Erlang modules +# documentation and run unit tests. The user can pass additional arguments to the +# 'erl' command with ERL_FLAGS environment variable. +EXEC_NON_INTERACTIVE = ['-noshell', '-noinput', '-eval'] + +def scan_meth(task): + node = task.inputs[0] + parent = node.parent + + deps = [] + scanned = set([]) + nodes_to_scan = [node] + + for n in nodes_to_scan: + if n.abspath() in scanned: + continue + + for i in re.findall('-include\("(.*)"\)\.', n.read()): + found = False + for d in task.includes_nodes: + r = task.generator.path.find_resource(os.path.join(d,i)) + if r: + deps.append(r) + nodes_to_scan.append(r) + found = True + break + r = task.generator.bld.root.find_resource(os.path.join(d,i)) + if r: + deps.append(r) + nodes_to_scan.append(r) + found = True + break + if not found: + pass + scanned.add(n.abspath()) + + return (deps, []) def configure(conf): conf.find_program('erlc', var='ERLC') - conf.env.ERLC_FLAGS = [] + conf.find_program('erl', var='ERL') + conf.add_os_flags('ERLC_FLAGS') + conf.add_os_flags('ERL_FLAGS') +@TaskGen.extension('.erl') +def process(self, node): + tsk = self.create_task('erl', node, node.change_ext('.beam')) + tsk.includes_nodes = self.to_list(getattr(self, 'includes', [])) + self.env['INCLUDES'] + [node.parent.abspath()] + tsk.defines = self.to_list(getattr(self, 'defines', [])) + self.env['DEFINES'] + tsk.flags = self.to_list(getattr(self, 'flags', [])) + self.env['ERLC_FLAGS'] + +class erl(Task.Task): + scan=scan_meth + color='GREEN' + vars = ['ERLC_FLAGS', 'ERLC', 'ERL', 'INCLUDES', 'DEFINES'] + + def run(self): + output=self.inputs[0].change_ext('.beam') + erlc = self.generator.env["ERLC"] + inca = [i for i in self.includes_nodes if os.path.isabs(i)] + incr = [self.generator.path.find_dir(i) for i in self.includes_nodes if not os.path.isabs(i)] + incr = filter(lambda x:x, incr) + incb = [i.get_bld() for i in incr] + inc = inca + [i.abspath() for i in incr+incb] + r = self.exec_command( + erlc + self.flags + + ["-I"+i for i in inc] + + ["-D"+d for d in self.defines] + + [self.inputs[0].path_from(output.parent)], + cwd=output.parent.abspath(), + shell=False) + return r + +@TaskGen.extension('.beam') +def process(self, node): + pass + + +class erl_test(Task.Task): + color = 'BLUE' + vars = ['ERL', 'ERL_FLAGS'] + + def run(self): + test_list = ", ".join([m.change_ext("").path_from(m.parent)+":test()" for m in self.modules]) + flags = " ".join(self.flags) + return self.exec_command( + self.generator.env.ERL + + self.generator.env.ERL_FLAGS + + self.flags + + EXEC_NON_INTERACTIVE + + ['halt(case lists:all(fun(Elem) -> Elem == ok end, [%s]) of true -> 0; false -> 1 end).' % test_list], + cwd = self.modules[0].parent.abspath()) + +@feature('eunit') +@after_method('process_source') +def addtestrun(self): + test_modules = [t.outputs[0] for t in self.tasks] + test_task = self.create_task('erl_test') + test_task.set_inputs(self.source + test_modules) + test_task.modules = test_modules + test_task.flags = self.to_list(getattr(self, 'flags', [])) + +class edoc(Task.Task): + color = 'BLUE' + vars = ['ERL_FLAGS', 'ERL'] + + def run(self): + self.exec_command( + self.generator.env.ERL + + self.generator.env.ERL_FLAGS + + EXEC_NON_INTERACTIVE + + ['edoc:files([\"'+self.inputs[0].abspath()+'\"]), halt(0).'], + cwd = self.outputs[0].parent.abspath() + ) + +@feature('edoc') +@before_method('process_source') +def add_edoc_task(self): + # do not process source, it would create double erl->beam task + self.meths.remove('process_source') + e = self.path.find_resource(self.source) + t = e.change_ext('.html') + png = t.parent.make_node('erlang.png') + css = t.parent.make_node('stylesheet.css') + self.create_task('edoc', e, [t, png, css])