diff options
| author | Dave Smith <dizzyd@dizzyd.com> | 2009-11-30 16:03:45 -0700 | 
|---|---|---|
| committer | Dave Smith <dizzyd@dizzyd.com> | 2009-11-30 16:03:45 -0700 | 
| commit | c7c1001012831eb06957b147ee3e5424c0ab2c77 (patch) | |
| tree | 84616addc000805658c6ee6af12f0a9e52ccb579 /src | |
| parent | 961c95b419ca0aa1e1c8ff6520450fe8e3368de5 (diff) | |
Slowly working out port driver implementation
Diffstat (limited to 'src')
| -rw-r--r-- | src/rebar_port_compiler.erl | 179 | ||||
| -rw-r--r-- | src/rebar_utils.erl | 32 | 
2 files changed, 210 insertions, 1 deletions
| diff --git a/src/rebar_port_compiler.erl b/src/rebar_port_compiler.erl new file mode 100644 index 0000000..b920966 --- /dev/null +++ b/src/rebar_port_compiler.erl @@ -0,0 +1,179 @@ +%% ------------------------------------------------------------------- +%% +%% rebar: Erlang Build Tools +%% +%% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com) +%% +%% 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. +%% +%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +%% THE SOFTWARE. +%% ------------------------------------------------------------------- +-module(rebar_port_compiler). + +-export([compile/2, +         clean/2]). + +-include("rebar.hrl"). + +%% =================================================================== +%% Public API +%% =================================================================== + +%% Port driver name - determined by app name +%% Source files (or c_src/*.c by default) +%% Pre-compile hook (optional) +%% Env variables + +compile(Config, _AppFile) -> +    %% Compose list of sources from config file -- default to c_src/*.c +    Sources = expand_sources(rebar_config:get_list(Config, port_sources, ["c_src/*.c"]), []), +    case Sources of +        [] -> +            ok; +        _ -> +            %% Extract environment values from the config (if specified) and merge with the +            %% default for this operating system. This enables max flexibility for users. +            OperatingSystem = rebar_utils:get_os(), +            DefaultEnvs  = driver_envs() ++ default_envs(OperatingSystem), +            OverrideEnvs = rebar_config:get_list(Config, port_env, []), +            Env = merge_envs(OverrideEnvs, DefaultEnvs), +             +            %% One or more files are available for building. Run the pre-compile hook, if necessary. +%            run_precompile_hook(Config), + +            %% Compile each of the sources +            compile_each(Sources, [], Config, Env), +            ok + +            %% Finally, link everything together +%            do_link(Config, AppFile, Bins) +    end. + +clean(Config, _AppFile) -> +    ok. + + +%% =================================================================== +%% Internal functions +%% =================================================================== + +expand_sources([], Acc) -> +    Acc; +expand_sources([Spec | Rest], Acc) -> +    Acc2 = filelib:wildcard(Spec) ++ Acc, +    expand_sources(Rest, Acc2). + + +%% CC       - C compiler +%% CXX      - C++ compiler +%% CFLAGS   - C compiler +%% CXXFLAGS - C++ compiler +%% LDFLAGS  - Link flags + +%% DRIVER_CFLAGS  - default -I paths for erts and ei +%% DRIVER_LDFLAGS - default -L and -lerl_interface -lei  + + +compile_each([], Acc, Config, Env) -> +    lists:reverse(Acc); +compile_each([Source | Rest], Acc, Config, Env) -> +    Ext = filename:extension(Source), +    Bin = filename:rootname(Source, Ext) ++ ".o", +    ?CONSOLE("Compiling ~s\n", [Source]), +    Compiler = compiler(Ext), +    case compiler(Ext) of +        "$CC" -> +            sh(?FMT("$CC -c $CFLAGS $DRIVER_CFLAGS ~s ~s", [Source, Bin]), Env); +        "$CXX" -> +            sh(?FMT("$CXX -c $CXXFLAGS $DRIVER_CFLAGS ~s ~s", [Source, Bin]), Env) +    end, +    compile_each(Rest, [Bin | Acc], Config, Env). + + + +             + + +needs_compile(Source, Bin) -> +    %% TODO: Generate depends using gcc -MM so we can also check for include changes +    filelib:last_modified(Bin) < filelib:last_modified(Source). + +merge_envs(OverrideEnvs, DefaultEnvs) -> +    orddict:merge(fun(Key, Override, Default) -> +                          expand_env_variable(Override, Key, Default) +                  end, +                  orddict:from_list(OverrideEnvs), +                  orddict:from_list(DefaultEnvs)). + +     +     + +compiler(".cc")  -> "$CXX"; +compiler(".cp")  -> "$CXX"; +compiler(".cxx") -> "$CXX"; +compiler(".cpp") -> "$CXX"; +compiler(".CPP") -> "$CXX"; +compiler(".c++") -> "$CXX"; +compiler(".C")   -> "$CXX"; +compiler(_)      -> "$CC". +      +expand_env_variable(InStr, VarName, VarValue) -> +    %% Given env. variable FOO we want to expand all references to +    %% it in InStr. References can have two forms: $FOO and ${FOO} +    R1 = re:replace(InStr, "\\\$" ++ VarName, VarValue), +    re:replace(R1, "\\\${" ++ VarName ++ "}", VarValue). + + +erts_dir() -> +    lists:concat([code:root_dir(), "/erts-", erlang:system_info(version)]). + +driver_envs() -> +    [{"DRIVER_CFLAGS", lists:concat([" -I", code:lib_dir(erl_interface, include), +                                     " -I", filename:join(erts_dir(), include), +                                     " "])}, +     {"DRIVER_LDFLAGS", lists:concat([" -L", code:lib_dir(erl_interface, lib), +                                      " -lerl_interface -lei"])}]. + +default_envs(darwin) -> +    [{"CC", "gcc"}, +     {"CXX", "g++"}, +     {"CFLAGS", "-g -Wall -fPIC"}, +     {"LDFLAGS", "-bundle -flat_namespace -undefined surpress"}]; +default_envs(linux) -> +    [{"CC", "gcc"}, +     {"CXX", "g++"}, +     {"CFLAGS", "-g -Wall -fPIC"}, +     {"LDFLAGS", "-shared"}]; +default_envs(Os) -> +    ?ERROR("Unsupported operating system ~s: can not generate default build environment.\n", [Os]), +    ?FAIL. + + +sh(Command, Env) -> +    ?CONSOLE("Cmd: ~p\n~p\n", [Command, Env]), +    Port = open_port({spawn, Command}, [{env, Env}, exit_status, {line, 16384}, +                                        use_stdio, stderr_to_stdout]), +    sh_loop(Port). +     +sh_loop(Port) ->             +    receive +        {Port, {data, {_, Line}}} -> +            ?CONSOLE("> ~s\n", [Line]), +            sh_loop(Port); +        {Port, Other} -> +            ?CONSOLE(">> ~p\n", [Other]) +    end. diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index 92f40ec..d3717cc 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -24,9 +24,39 @@  %% -------------------------------------------------------------------  -module(rebar_utils). --export([get_cwd/0]). +-export([get_cwd/0, +         get_os/0]). + + +%% ==================================================================== +%% Public API +%% ====================================================================  get_cwd() ->      {ok, Dir} = file:get_cwd(),      Dir. + +get_os() -> +    Arch = erlang:system_info(system_architecture), +    case match_first([{"linux", linux}, {"darwin", darwin}], Arch) of +        nomatch -> +            {unknown, Arch}; +        ArchAtom -> +            ArchAtom +    end. + + +%% ==================================================================== +%% Internal functions +%% ==================================================================== + +match_first([], Val) -> +    nomatch; +match_first([{Regex, MatchValue} | Rest], Val) -> +    case re:run(Val, Regex, [{capture, none}]) of +        match -> +            MatchValue; +        nomatch -> +           match_first(Rest, Val) +    end. | 
