Current section
Files
Jump to
Current section
Files
lib/bundlex/toolchain/common/unix.ex
defmodule Bundlex.Toolchain.Common.Unix do
@moduledoc false
use Bunch
alias Bundlex.{Native, Project, Toolchain}
alias Bundlex.Toolchain.Common.Compilers
@spec compiler_commands(
Native.t(),
compile :: String.t(),
link :: String.t(),
lang :: Project.native_language(),
options :: Keyword.t()
) :: [String.t()]
def compiler_commands(native, compile, link, lang, options \\ []) do
includes = native.includes |> paths("-I")
compiler_flags = resolve_compiler_flags(native.compiler_flags, native.interface, lang)
output = Toolchain.output_path(native.app, native.name, native.interface)
output_obj = output <> "_obj"
objects =
native.sources
|> Enum.map(fn source ->
"""
#{Path.join(output_obj, source |> Path.basename())}_\
#{:crypto.hash(:sha, source) |> Base.encode16()}.o\
"""
end)
compile_commands =
native.sources
|> Enum.zip(objects)
|> Enum.map(fn {source, object} ->
"""
#{compile} -Wall -Wextra -c -O2 -g #{compiler_flags} \
-o #{path(object)} #{includes} #{path(source)}
"""
end)
["mkdir -p #{path(output_obj)}"] ++
compile_commands ++ link_commands(native, link, output, objects, options)
end
defp resolve_compiler_flags(compiler_flags, interface, lang) do
compiler_flags
|> add_interface_macro_flag(interface)
|> maybe_add_std_flag(lang)
|> Enum.join(" ")
end
defp add_interface_macro_flag(compiler_flags, nil) do
compiler_flags
end
defp add_interface_macro_flag(compiler_flags, interface) do
macro_flag = "-DBUNDLEX_#{interface |> Atom.to_string() |> String.upcase()}"
[macro_flag] ++ compiler_flags
end
defp maybe_add_std_flag(compiler_flags, lang) do
if standard_specyfied?(compiler_flags) do
compiler_flags
else
flag = Compilers.get_default_std_flag(lang)
[flag | compiler_flags]
end
end
defp standard_specyfied?(compiler_flags) do
Enum.any?(compiler_flags, &String.match?(&1, ~r/^-std=/))
end
defp link_commands(%Native{type: :lib}, _link, output, objects, _options) do
a_path = path(output <> ".a")
["rm -f #{a_path}", "ar rcs #{a_path} #{paths(objects)}"]
end
defp link_commands(native, link, output, objects, options) do
extension =
case native.interface do
:nif -> ".so"
interface when interface in [:cnode, :port] -> ""
end
wrap_deps = options |> Keyword.get(:wrap_deps, & &1)
deps =
native.deps
|> Enum.map(&(Toolchain.output_path(&1.app, &1.name, &1.interface) <> ".a"))
|> paths()
|> wrap_deps.()
linker_flags = native.linker_flags |> Enum.join(" ")
[
"""
#{link} -o #{path(output <> extension)} \
#{deps} #{paths(objects)} #{libs(native)} #{linker_flags}
"""
]
end
defp paths(paths, flag \\ "") do
Enum.map_join(paths, " ", fn p -> "#{flag}#{path(p)}" end)
end
defp path(path) do
path = path |> String.replace(~S("), ~S(\")) |> Path.expand()
~s("#{path}")
end
defp libs(native) do
lib_dirs = native.lib_dirs |> paths("-L")
libs = native.libs |> Enum.map_join(" ", fn lib -> "-l#{lib}" end)
"#{lib_dirs} #{libs}"
end
end