forked from schacon/geef
-
Notifications
You must be signed in to change notification settings - Fork 20
/
mix.exs
143 lines (114 loc) · 3.38 KB
/
mix.exs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
defmodule Mix.Tasks.Compile.Nif do
@moduledoc """
A task to compile C code into a NIF.
To use, define a function in your project called `nif`.
## Configuration
* `:paths` - directories to find source files. Defaults to
`["c_src"]`. Can be configured as:
````
[paths: ["c_src", "vendor/src"]]
````
* `:exts` - extensions of the source files. Defaults to `[:c]`, can
be configured as:
```
[exts: [:c, :cpp]]
```
* `:file` - path of the shared object to compile to. Can be configured as:
````
[file: "priv/fancy.so"]
````
* `:flags` - flags to pass to the compiler. Can be configured as:
````
[flags: ["-lfancy", "-DDEBUG"]]
````
"""
def run(_) do
project = Mix.Project.get!
Mix.shell.info("* Running make...")
Mix.shell.info(:os.cmd('make'))
if function_exported?(project, :nif, 0) do
do_run(project.nif)
else
:noop
end
end
def do_run(config) do
file = Keyword.fetch!(config, :file)
paths = Keyword.get(config, :paths, ["c_src"])
flags = Keyword.get(config, :flags, [])
exts = Keyword.get(config, :exts, [:c])
compiler = Keyword.get(config, :compilers, ["cc", "gcc", "clang"]) |> find_compiler
flags =
case cflags = System.get_env("CFLAGS") do
# FIXME: this isn't going to work too well with quoted spaces
# inside arguments
true -> [flags, String.split(cflags)]
_ -> flags
end
# Create the directory (e.g. "priv/")
File.mkdir_p!(Path.dirname(file))
# Figure out whether we should compile the NIF. Compare the source
# files and header timestamps with the target binary. If the
# target is older than any source file, recompile.
to_compile = Mix.Utils.extract_files(paths, exts)
to_check = Mix.Utils.extract_files(paths, [:h | exts])
case Mix.Utils.extract_stale(to_check, [file]) do
[] ->
:noop
_ ->
Mix.shell.info("* Compiling #{file}")
args = ["-shared", "-fpic", "-o", file, to_compile, flags] |> List.flatten
port = Port.open({:spawn_executable, compiler},
[:stream, :binary, :use_stdio, :stderr_to_stdout, :hide,
:exit_status, {:args, args}])
if do_cmd(port) != 0 do
raise Mix.Error, message: "Error compiling #{file}"
end
:ok
end
end
defp find_compiler([compiler | tail]) do
case System.find_executable(compiler) do
nil ->
find_compiler(tail)
path ->
path
end
end
defp do_cmd(port) do
receive do
{^port, {:data, data}} ->
IO.write(data)
do_cmd(port)
{^port, {:exit_status, status}} ->
status
end
end
end
defmodule Geef.Mixfile do
use Mix.Project
def project do
[ app: :geef,
version: "0.0.1",
compilers: [:nif, :erlang, :elixir, :app],
deps: deps,
dialyzer: dialyzer ]
end
def nif do
[ file: "#{Path.join [__DIR__, "priv", "geef.so"]}",
flags: "-lgit2" ]
end
# Configuration for the OTP application
def application do
[]
end
# Returns the list of dependencies in the format:
# { :foobar, "0.1", git: "https://github.com/elixir-lang/foobar.git" }
defp deps do
[]
end
def dialyzer do
[ plt_apps: [:erts, :kernel, :stdlib, :mnesia],
flags: ["-Wunmatched_returns","-Werror_handling","-Wrace_conditions", "-Wno_opaque"]]
end
end