Packages

An EtherCAT MainDevice in pure Elixir, for EtherCAT I/O such as a Beckhoff EK1100 with its terminals: scan, SII, the AL state machine, CoE, and cyclic process data at the pace of the application, over raw Ethernet on Linux and macOS, with cable redundancy on a ring.

Current section

Files

Jump to
yaethercat mix.exs
Raw

mix.exs

defmodule EtherCAT.MixProject do
  use Mix.Project

  @version "0.1.0"
  @source_url "https://github.com/tallakt/yaethercat"

  def project do
    [
      app: :yaethercat,
      version: @version,
      elixir: "~> 1.15",
      start_permanent: Mix.env() == :prod,
      compilers: Mix.compilers() ++ [:ethercat_bpf],
      deps: deps(),
      elixirc_paths: if(Mix.env() == :test, do: ["lib", "test/support"], else: ["lib"]),
      description:
        "An EtherCAT MainDevice in pure Elixir, for EtherCAT I/O such as a Beckhoff EK1100 " <>
          "with its terminals: scan, SII, the AL state machine, CoE, and cyclic process data " <>
          "at the pace of the application, over raw Ethernet on Linux and macOS, with cable " <>
          "redundancy on a ring.",
      package: package(),
      source_url: @source_url,
      docs: docs(),
      # The raw transports run only against a rack, and the simulator and its helpers are tests.
      test_coverage: [
        summary: [threshold: 80],
        ignore_modules: [
          EtherCAT.Transport.Packet,
          EtherCAT.Transport.BPF,
          EtherCAT.Test.Simulator,
          EtherCAT.Test.SIIBuilder,
          EtherCAT.Test.Rack,
          EtherCAT.Test.Generators
        ]
      ],
      dialyzer: [
        # Kept between runs, and between CI jobs by its cache.
        plt_core_path: "priv/plts",
        plt_local_path: "priv/plts",
        # For the mix task.
        plt_add_apps: [:mix],
        flags: [:error_handling, :extra_return, :unmatched_returns]
      ]
    ]
  end

  defp package do
    [
      licenses: ["Apache-2.0"],
      links: %{"GitHub" => @source_url},
      files:
        ~w(lib priv/bpf/ethercat_bpf.c mix.exs .formatter.exs README.md CHANGELOG.md LICENSE NOTICE)
    ]
  end

  defp docs do
    [
      main: "readme",
      extras: ["README.md", "CHANGELOG.md", "LICENSE", "NOTICE"],
      source_ref: "v#{@version}",
      groups_for_modules: [
        MainDevice: [EtherCAT, EtherCAT.MainDevice, EtherCAT.SubDevice, EtherCAT.ProcessImage],
        "Application layer": [EtherCAT.AL, EtherCAT.CoE, EtherCAT.SII, EtherCAT.Mailbox],
        "Data link layer": [EtherCAT.Frame, EtherCAT.Datagram, EtherCAT.Register],
        Transports: [
          EtherCAT.Transport,
          EtherCAT.Transport.Packet,
          EtherCAT.Transport.BPF,
          EtherCAT.Transport.UDP
        ]
      ]
    ]
  end

  def application do
    [extra_applications: []]
  end

  defp deps do
    [
      # Only for the fuzz and property tests.
      {:stream_data, "~> 1.1", only: :test},
      {:ex_doc, "~> 0.40", only: :dev, runtime: false},
      {:credo, "~> 1.7", only: [:dev, :test], runtime: false},
      {:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false}
    ]
  end
end

defmodule Mix.Tasks.Compile.EthercatBpf do
  @moduledoc false
  # Builds the port program of EtherCAT.Transport.BPF on macOS, where it's needed, with the C
  # compiler there; elsewhere it does nothing.
  use Mix.Task.Compiler

  @source "priv/bpf/ethercat_bpf.c"
  @program "priv/bpf/ethercat_bpf"

  @impl true
  def run(_args) do
    cond do
      :os.type() != {:unix, :darwin} ->
        {:noop, []}

      File.exists?(@program) and File.stat!(@program).mtime >= File.stat!(@source).mtime ->
        {:noop, []}

      System.find_executable("cc") == nil ->
        Mix.shell().info(
          "yaethercat: no cc, so #{@program} is not built and raw Ethernet is unavailable"
        )

        {:noop, []}

      true ->
        case System.cmd("cc", ["-O2", "-Wall", "-Wextra", "-o", @program, @source],
               stderr_to_stdout: true
             ) do
          {_output, 0} ->
            {:ok, []}

          {output, _status} ->
            Mix.shell().error("yaethercat: could not build #{@program}:\n" <> output)
            {:error, []}
        end
    end
  end
end