Skip to content
Snippets Groups Projects
Commit b6392da4 authored by Xiangrong Hao's avatar Xiangrong Hao Committed by GitHub
Browse files

dynamic backend do not break compile (#29)

* dynamic backend and make compile and start app never fail

* update README

* bugfix: functions check for custom backend

* more log for get backend error
parent a22044c2
Branches
Tags
No related merge requests found
...@@ -99,9 +99,3 @@ FileSystem.start_link(dirs: ["/path/to/some/files"], listener_extra_args: "--lat ...@@ -99,9 +99,3 @@ FileSystem.start_link(dirs: ["/path/to/some/files"], listener_extra_args: "--lat
``` ```
See the [fs source](https://github.com/synrc/fs/tree/master/c_src) for more details. See the [fs source](https://github.com/synrc/fs/tree/master/c_src) for more details.
## List Events from Backend
```shell
iex > FileSystem.known_events
```
...@@ -13,9 +13,4 @@ defmodule FileSystem do ...@@ -13,9 +13,4 @@ defmodule FileSystem do
def subscribe(pid) do def subscribe(pid) do
GenServer.call(pid, :subscribe) GenServer.call(pid, :subscribe)
end end
@backend FileSystem.Backend.backend
def backend, do: @backend
def known_events, do: @backend.known_events()
end end
require Logger
defmodule FileSystem.Backend do defmodule FileSystem.Backend do
@callback bootstrap() :: any() @callback bootstrap() :: :ok | {:error, atom()}
@callback supported_systems() :: [{atom(), atom()}] @callback supported_systems() :: [{atom(), atom()}]
@callback known_events() :: [atom()] @callback known_events() :: [atom()]
@callback find_executable() :: Sting.t
def backend do def backend(backend) do
os_type = :os.type() with {:ok, module} <- backend_module(backend),
backend = :ok <- validate_os(backend, module),
Application.get_env(:file_system, :backend, :ok <- module.bootstrap
case os_type do do
{:ok, module}
else
{:error, reason} -> {:error, reason}
end
end
defp backend_module(nil) do
case :os.type() do
{:unix, :darwin} -> :fs_mac {:unix, :darwin} -> :fs_mac
{:unix, :linux} -> :fs_inotify {:unix, :linux} -> :fs_inotify
{:unix, :freebsd} -> :fs_inotify {:unix, :freebsd} -> :fs_inotify
{:win32, :nt} -> :fs_windows {:win32, :nt} -> :fs_windows
_ -> nil system -> {:unsupported_system, system}
end |> backend_module
end end
) |> case do defp backend_module(:fs_mac), do: {:ok, FileSystem.Backends.FSMac}
nil -> raise "undefined backend" defp backend_module(:fs_inotify), do: {:ok, FileSystem.Backends.FSInotify}
:fs_mac -> FileSystem.Backends.FSMac defp backend_module(:fs_windows), do: {:ok, FileSystem.Backends.FSWindows}
:fs_inotify -> FileSystem.Backends.FSInotify defp backend_module({:unsupported_system, system}) do
:fs_windows -> FileSystem.Backends.FSWindows Logger.error "I'm so sorry but `file_system` does NOT support your current system #{inspect system} for now."
any -> any {:error, :unsupported_system}
end
defp backend_module(module) do
functions = module.__info__(:functions)
{:start_link, 1} in functions &&
{:bootstrap, 0} in functions &&
{:supported_systems, 0} in functions ||
raise "illegal backend"
rescue
_ ->
Logger.error "You are using custom backend `#{inspect module}`, make sure it's a legal file_system backend module."
{:error, :illegal_backend}
end
defp validate_os(backend, module) do
os_type = :os.type()
if os_type in module.supported_systems() do
:ok
else
Logger.error "The backend #{backend} you are using does NOT support your current system #{inspect os_type}."
{:error, :unsupported_system}
end end
os_type in backend.supported_systems || raise "unsupported system for current backend"
backend
end end
end end
...@@ -8,9 +8,11 @@ defmodule FileSystem.Backends.FSInotify do ...@@ -8,9 +8,11 @@ defmodule FileSystem.Backends.FSInotify do
def bootstrap do def bootstrap do
exec_file = find_executable() exec_file = find_executable()
unless File.exists?(exec_file) do if is_nil(exec_file) do
Logger.error "`inotify-tools` is needed to run `file_system` for your system, check https://github.com/rvoicilas/inotify-tools/wiki for more information about how to install it." Logger.error "`inotify-tools` is needed to run `file_system` for your system, check https://github.com/rvoicilas/inotify-tools/wiki for more information about how to install it."
raise CompileError {:error, :fs_inotify_bootstrap_error}
else
:ok
end end
end end
......
...@@ -12,11 +12,14 @@ defmodule FileSystem.Backends.FSMac do ...@@ -12,11 +12,14 @@ defmodule FileSystem.Backends.FSMac do
cmd = "clang -framework CoreFoundation -framework CoreServices -Wno-deprecated-declarations c_src/mac/*.c -o #{exec_file}" cmd = "clang -framework CoreFoundation -framework CoreServices -Wno-deprecated-declarations c_src/mac/*.c -o #{exec_file}"
if Mix.shell.cmd(cmd) > 0 do if Mix.shell.cmd(cmd) > 0 do
Logger.error "Compile executable file error, try to run `#{cmd}` manually." Logger.error "Compile executable file error, try to run `#{cmd}` manually."
raise CompileError raise "compile backend error"
else else
Logger.info "Compile executable file, Done." Logger.info "Compile executable file, Done."
end end
end end
:ok
rescue
_ -> {:error, :fs_mac_bootstrap_error}
end end
def supported_systems do def supported_systems do
......
...@@ -8,9 +8,11 @@ defmodule FileSystem.Backends.FSWindows do ...@@ -8,9 +8,11 @@ defmodule FileSystem.Backends.FSWindows do
def bootstrap do def bootstrap do
exec_file = find_executable() exec_file = find_executable()
unless File.exists?(exec_file) do if File.exists?(exec_file) do
:ok
else
Logger.error "Can't find executable `inotifywait.exe`, make sure the file is in your priv dir." Logger.error "Can't find executable `inotifywait.exe`, make sure the file is in your priv dir."
raise CompileError {:error, :fs_windows_bootstrap_error}
end end
end end
......
...@@ -2,13 +2,18 @@ defmodule FileSystem.Worker do ...@@ -2,13 +2,18 @@ defmodule FileSystem.Worker do
use GenServer use GenServer
def start_link(args) do def start_link(args) do
{args, opts} = Keyword.split(args, [:dirs, :listener_extra_args]) {args, opts} = Keyword.split(args, [:backend, :dirs, :listener_extra_args])
GenServer.start_link(__MODULE__, args, opts) GenServer.start_link(__MODULE__, args, opts)
end end
def init(args) do def init(args) do
{:ok, backend_pid} = FileSystem.backend.start_link([{:worker_pid, self()} | args]) case FileSystem.Backend.backend(args[:backend]) do
{:ok, backend} ->
{:ok, backend_pid} = backend.start_link([{:worker_pid, self()} | Keyword.drop(args, [:backend])])
{:ok, %{backend_pid: backend_pid, subscribers: %{}}} {:ok, %{backend_pid: backend_pid, subscribers: %{}}}
{:error, reason} ->
{:stop, reason}
end
end end
def handle_call(:subscribe, {pid, _}, state) do def handle_call(:subscribe, {pid, _}, state) do
......
defmodule Mix.Tasks.FileSystem.FsMac do
use Mix.Task
@doc false
def run(["init"]) do
case FileSystem.Backends.FSMac.bootstrap do
:ok ->
IO.puts "Initialize fs_mac backend successfully."
{:error, reason} ->
IO.puts :stderr, "Initialize fs_mac backend error, reason: #{reason}."
end
end
def run(args) do
IO.puts :stderr, "unknown command `#{args}`"
end
end
defmodule Mix.Tasks.Compile.FileSystem do
def run(_) do
FileSystem.backend.bootstrap
end
end
defmodule FileSystem.Mixfile do defmodule FileSystem.Mixfile do
use Mix.Project use Mix.Project
...@@ -12,7 +5,7 @@ defmodule FileSystem.Mixfile do ...@@ -12,7 +5,7 @@ defmodule FileSystem.Mixfile do
[ app: :file_system, [ app: :file_system,
version: "0.1.0", version: "0.1.0",
elixir: "~> 1.5-rc", elixir: "~> 1.5-rc",
compilers: [:elixir, :app, :file_system], compilers: [:elixir, :app],
deps: deps(), deps: deps(),
description: "A file system change watcher wrapper based on [fs](https://github.com/synrc/fs)", description: "A file system change watcher wrapper based on [fs](https://github.com/synrc/fs)",
source_url: "https://github.com/falood/file_system", source_url: "https://github.com/falood/file_system",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment