Skip to content

Commit 3881908

Browse files
committed
Support builds that completely exclude I2CDev support
This removes both the Elixir module and the NIF. There's no longer any unintended NIF builds or loads or anything extra. To enable this, either set the `:default_backend` application environment key or force it by setting `:include_i2c_dev`. Most users probably want: ```elixir config :circuits_i2c, default_backend: MyI2CBackend ```
1 parent 743ad6f commit 3881908

File tree

11 files changed

+102
-36
lines changed

11 files changed

+102
-36
lines changed

.formatter.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Used by "mix format"
22
[
3-
inputs: ["{mix,.formatter,.credo}.exs", "{config,lib,test}/**/*.{ex,exs}"]
3+
inputs: ["{mix,.formatter,.credo}.exs", "{config,i2c_dev,lib,test}/**/*.{ex,exs}"]
44
]

Makefile

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# Variables to override:
1515
#
1616
# MIX_APP_PATH path to the build directory
17-
# CIRCUITS_I2C_I2CDEV Backend to build - `"normal"`, `"test"`, or `"disabled"` will build a NIF
17+
# CIRCUITS_I2C_I2CDEV Backend to build - `"normal"` or `"test"`
1818
#
1919
# CC C compiler
2020
# CROSSCOMPILE crosscompiler prefix, if any
@@ -30,7 +30,7 @@ BUILD = $(MIX_APP_PATH)/obj
3030

3131
NIF = $(PREFIX)/i2c_nif.so
3232

33-
SRC = c_src/i2c_nif.c
33+
SRC = i2c_dev/c_src/i2c_nif.c
3434
CFLAGS ?= -O2 -Wall -Wextra -Wno-unused-parameter -pedantic
3535

3636
# Check that we're on a supported build platform
@@ -40,7 +40,7 @@ ifeq ($(shell uname -s),Linux)
4040
CFLAGS += -fPIC
4141
LDFLAGS += -fPIC -shared
4242
else
43-
CFLAGS += -Ic_src/compat
43+
CFLAGS += -Ii2c_dev/c_src/compat
4444
LDFLAGS += -undefined dynamic_lookup -dynamiclib
4545
ifeq ($(CIRCUITS_I2C_I2CDEV),normal)
4646
$(error Circuits.I2C Linux I2CDev backend is not supported on non-Linux platforms. Review circuits_i2c backend configuration or report an issue if improperly detected.)
@@ -59,17 +59,16 @@ ifeq ($(CIRCUITS_I2C_I2CDEV),test)
5959
# Stub out ioctls and send back test data
6060
CFLAGS += -DTEST_BACKEND
6161
else
62-
# Don't build NIF
63-
NIF =
62+
$(error Invalid CIRCUITS_I2C_I2CDEV value: $(CIRCUITS_I2C_I2CDEV))
6463
endif
6564
endif
6665

6766
# Set Erlang-specific compile and linker flags
6867
ERL_CFLAGS ?= -I"$(ERL_EI_INCLUDE_DIR)"
6968
ERL_LDFLAGS ?= -L"$(ERL_EI_LIBDIR)" -lei
7069

71-
HEADERS =$(wildcard c_src/*.h)
72-
OBJ = $(SRC:c_src/%.c=$(BUILD)/%.o)
70+
HEADERS =$(wildcard i2c_dev/c_src/*.h)
71+
OBJ = $(SRC:i2c_dev/c_src/%.c=$(BUILD)/%.o)
7372

7473
calling_from_make:
7574
mix compile
@@ -80,7 +79,7 @@ install: $(PREFIX) $(BUILD) $(NIF)
8079

8180
$(OBJ): $(HEADERS) Makefile
8281

83-
$(BUILD)/%.o: c_src/%.c
82+
$(BUILD)/%.o: i2c_dev/c_src/%.c
8483
@echo " CC $(notdir $@)"
8584
$(CC) -c $(ERL_CFLAGS) $(CFLAGS) -o $@ $<
8685

README.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,33 @@ may be helpful.
4848

4949
Internally, it uses the [Linux "i2c-dev"
5050
interface](https://elixir.bootlin.com/linux/latest/source/Documentation/i2c/dev-interface)
51-
so that it does not require board-dependent code.
51+
so that it does not require board-dependent code. See the application
52+
configuration for disabling all things Linux/Nerves specific.
5253

5354
## Getting started without hardware
5455

5556
If you don't have any real I2C devices, it's possible to work with simulated
5657
devices. See the [CircuitsSim](https://github.com/elixir-circuits/circuits_sim)
5758
project for details.
5859

60+
## Application config
61+
62+
`Circuits.I2C` supports the following application environment keys:
63+
64+
| Key | Description |
65+
| --- | ----------- |
66+
| `:include_i2c_dev` | Set to `true` or `false` to indicate whether or not to include the `Circuits.I2C.I2CDev` backend. |
67+
| `:default_backend` | Default I2C backend to use when unspecified in API calls. The format is either a module name or a `{module_name, options}` tuple. |
68+
69+
`Circuits.I2C` uses a heuristic for the default values of both
70+
`:include_i2c_dev`. For example, if the `:default_backend` is set to
71+
`Circuits.I2C.I2CDev`, then `:include_i2c_dev` has to be `true`. If
72+
`:default_backend` is set to something else, then `:include_i2c_dev` defaults to
73+
`false`. If neither is set, then `Circuits.I2C.I2CDev` is built. On non-Linux
74+
platforms, the `Circuits.I2C.I2CDev` NIF will be compiled in test mode which
75+
minimal supports unit testing. Mocking is generally a better option for most
76+
users, though.
77+
5978
## I2C background
6079

6180
An [Inter-Integrated Circuit](https://en.wikipedia.org/wiki/I%C2%B2C) (I2C) bus
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

lib/i2c/nil_backend.ex

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ defmodule Circuits.I2C.NilBackend do
99
@behaviour Circuits.I2C.Backend
1010

1111
alias Circuits.I2C.Backend
12+
alias Circuits.I2C.Bus
13+
14+
defstruct [:empty]
1215

1316
@doc """
1417
Return the I2C bus names on this system
@@ -35,4 +38,27 @@ defmodule Circuits.I2C.NilBackend do
3538
def info() do
3639
%{name: __MODULE__}
3740
end
41+
42+
defimpl Bus do
43+
@impl Bus
44+
def flags(%Circuits.I2C.NilBackend{}), do: []
45+
46+
@impl Bus
47+
def read(%Circuits.I2C.NilBackend{}, _address, _count, _options) do
48+
{:error, :unimplemented}
49+
end
50+
51+
@impl Bus
52+
def write(%Circuits.I2C.NilBackend{}, _address, _data, _options) do
53+
{:error, :unimplemented}
54+
end
55+
56+
@impl Bus
57+
def write_read(%Circuits.I2C.NilBackend{}, _address, _write_data, _read_count, _options) do
58+
{:error, :unimplemented}
59+
end
60+
61+
@impl Bus
62+
def close(%Circuits.I2C.NilBackend{}), do: :ok
63+
end
3864
end

mix.exs

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,54 @@ defmodule Circuits.I2C.MixProject do
66
@source_url "https://github.com/elixir-circuits/circuits_i2c"
77

88
def project do
9-
[
9+
base = [
1010
app: :circuits_i2c,
1111
version: @version,
1212
elixir: "~> 1.13",
1313
description: @description,
1414
package: package(),
1515
source_url: @source_url,
16-
compilers: [:elixir_make | Mix.compilers()],
17-
elixirc_paths: elixirc_paths(Mix.env()),
18-
make_targets: ["all"],
19-
make_clean: ["clean"],
2016
docs: docs(),
21-
aliases: [compile: [&set_make_env/1, "compile"], format: [&format_c/1, "format"]],
2217
start_permanent: Mix.env() == :prod,
2318
dialyzer: [
2419
flags: [:missing_return, :extra_return, :unmatched_returns, :error_handling, :underspecs]
2520
],
2621
deps: deps()
2722
]
23+
24+
if build_i2c_dev?() do
25+
additions = [
26+
compilers: [:elixir_make | Mix.compilers()],
27+
elixirc_paths: ["lib", "i2c_dev/lib"],
28+
make_targets: ["all"],
29+
make_clean: ["clean"],
30+
aliases: [compile: [&set_make_env/1, "compile"], format: [&format_c/1, "format"]]
31+
]
32+
33+
Keyword.merge(base, additions)
34+
else
35+
base
36+
end
2837
end
2938

3039
def cli do
3140
[preferred_envs: %{docs: :docs, "hex.publish": :docs, "hex.build": :docs}]
3241
end
3342

34-
defp elixirc_paths(env) when env in [:test, :dev], do: ["lib", "examples"]
35-
defp elixirc_paths(_env), do: ["lib"]
36-
3743
def application do
38-
# IMPORTANT: This provides a default at runtime and at compile-time when
44+
# IMPORTANT: This provides defaults at runtime and at compile-time when
3945
# circuits_i2c is pulled in as a dependency.
40-
[env: [default_backend: default_backend()]]
46+
[env: [default_backend: default_backend(), build_i2c_dev: false]]
4147
end
4248

4349
defp package do
4450
%{
4551
files: [
4652
"CHANGELOG.md",
47-
"c_src/*.[ch]",
48-
"c_src/linux/*.h",
49-
"c_src/compat/linux/*.h",
53+
"i2c_dev/c_src/*.[ch]",
54+
"i2c_dev/c_src/linux/*.h",
55+
"i2c_dev/c_src/compat/linux/*.h",
56+
"i2c_dev/lib",
5057
"lib",
5158
"LICENSES",
5259
"Makefile",
@@ -85,24 +92,43 @@ defmodule Circuits.I2C.MixProject do
8592
]
8693
end
8794

88-
defp default_backend(), do: default_backend(Mix.env(), Mix.target())
89-
defp default_backend(:test, _target), do: {Circuits.I2C.I2CDev, test: true}
95+
defp build_i2c_dev?() do
96+
include_i2c_dev = Application.get_env(:circuits_i2c, :include_i2c_dev)
97+
98+
if include_i2c_dev != nil do
99+
# If the user set :include_i2c_dev, then use it
100+
include_i2c_dev
101+
else
102+
# Otherwise, infer whether to build it based on the default_backend
103+
# setting. If default_backend references it, then build it. If it
104+
# references something else, then don't build. Default is to build.
105+
default_backend = Application.get_env(:circuits_i2c, :default_backend)
90106

91-
defp default_backend(_env, :host) do
107+
default_backend == nil or default_backend == Circuits.I2C.I2CDev or
108+
(is_tuple(default_backend) and elem(default_backend, 0) == Circuits.I2C.I2CDev)
109+
end
110+
end
111+
112+
defp default_backend(), do: default_backend(Mix.env(), Mix.target(), build_i2c_dev?())
113+
defp default_backend(:test, _target, true), do: {Circuits.I2C.I2CDev, test: true}
114+
115+
defp default_backend(_env, :host, true) do
92116
case :os.type() do
93117
{:unix, :linux} -> Circuits.I2C.I2CDev
94118
_ -> {Circuits.I2C.I2CDev, test: true}
95119
end
96120
end
97121

122+
defp default_backend(_env, _target, false), do: Circuits.I2C.NilBackend
123+
98124
# MIX_TARGET set to something besides host
99-
defp default_backend(env, _not_host) do
125+
defp default_backend(env, _not_host, true) do
100126
# If CROSSCOMPILE is set, then the Makefile will use the crosscompiler and
101127
# assume a Linux/Nerves build If not, then the NIF will be build for the
102128
# host, so use the default host backend
103129
case System.fetch_env("CROSSCOMPILE") do
104130
{:ok, _} -> Circuits.I2C.I2CDev
105-
:error -> default_backend(env, :host)
131+
:error -> default_backend(env, :host, true)
106132
end
107133
end
108134

@@ -123,12 +149,8 @@ defmodule Circuits.I2C.MixProject do
123149
end
124150
end
125151

126-
defp i2c_dev_compile_mode(Circuits.I2C.I2CDev) do
127-
"normal"
128-
end
129-
130152
defp i2c_dev_compile_mode(_other) do
131-
"disabled"
153+
"normal"
132154
end
133155

134156
defp format_c([]) do
@@ -137,7 +159,7 @@ defmodule Circuits.I2C.MixProject do
137159
Mix.Shell.IO.info("Install astyle to format C code.")
138160

139161
astyle ->
140-
System.cmd(astyle, ["-n", "c_src/*.c"], into: IO.stream(:stdio, :line))
162+
System.cmd(astyle, ["-n", "i2c_dev/c_src/*.c"], into: IO.stream(:stdio, :line))
141163
end
142164
end
143165

0 commit comments

Comments
 (0)