diff --git a/.github/workflows/elixir.yml b/.github/workflows/elixir.yml index a45bb32..2c1380c 100644 --- a/.github/workflows/elixir.yml +++ b/.github/workflows/elixir.yml @@ -16,13 +16,19 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Elixir - uses: erlef/setup-beam@5304e04ea2b355f03681464e683d92e3b2f18451 # v1 + - name: Install toolchain + uses: jdx/mise-action@v2 with: - elixir-version: "1.18" - otp-version: "27" - - name: Install Protoc - uses: arduino/setup-protoc@f4d5893b897028ff5739576ea0409746887fa536 # v3 + mise_toml: | + [tools] + erlang = "27.3" + elixir = "1.18.4-otp-27" + zig = "0.15.2" + protoc = "latest" + - name: Install Hex and Rebar + run: | + mix local.hex --force + mix local.rebar --force - name: Restore dependencies cache uses: actions/cache@v3 with: @@ -34,11 +40,11 @@ jobs: - name: Run tests run: mix coveralls.json - name: Upload coverage reports to Codecov - uses: codecov/codecov-action@13ce06bfc6bbe3ecf90edbbf1bc32fe5978ca1d3 # v5 + uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de with: token: ${{ secrets.CODECOV_TOKEN }} - name: Upload test results to Codecov if: ${{ !cancelled() }} - uses: codecov/test-results-action@44ecb3a270cd942bdf0fa8f2ce14cb32493e810a # v1 + uses: codecov/test-results-action@0fa95f0e1eeaafde2c782583b36b28ad0d8c77d3 with: token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/precompile.yml b/.github/workflows/precompile.yml index 7016176..1b1917a 100644 --- a/.github/workflows/precompile.yml +++ b/.github/workflows/precompile.yml @@ -9,99 +9,41 @@ permissions: contents: write jobs: - linux: - runs-on: ubuntu-latest + precompile: + name: ${{ matrix.runner }} / Elixir ${{ matrix.elixir }} + runs-on: ${{ matrix.runner }} env: MIX_ENV: "prod" strategy: fail-fast: false matrix: include: - - pair: - elixir: "1.16.3-otp-24" - otp: "24.3.4.17" - - pair: - elixir: "1.18.2-otp-27" - otp: "27.2.2" + - runner: ubuntu-latest + elixir: "1.19.5-otp-28" + otp: "28.4" + - runner: macos-latest + elixir: "1.19.5-otp-28" + otp: "28.4" steps: - uses: actions/checkout@v4 - - name: Set up Elixir - uses: erlef/setup-beam@5304e04ea2b355f03681464e683d92e3b2f18451 # v1 - with: - otp-version: ${{matrix.pair.otp}} - elixir-version: ${{matrix.pair.elixir}} - - name: Install Protoc - uses: arduino/setup-protoc@f4d5893b897028ff5739576ea0409746887fa536 # v3 - - name: Restore dependencies cache - uses: actions/cache@v3 - with: - path: deps - key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }} - restore-keys: ${{ runner.os }}-mix- - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install -y build-essential automake autoconf pkg-config bc m4 unzip zip \ - gcc-aarch64-linux-gnu g++-aarch64-linux-gnu - - name: Get musl cross-compilers - run: | - for musl_arch in x86_64 aarch64 riscv64 - do - wget "https://musl.cc/${musl_arch}-linux-musl-cross.tgz" -O "${musl_arch}-linux-musl-cross.tgz" - tar -xf "${musl_arch}-linux-musl-cross.tgz" - done - - name: Run tests - run: | - # Optional, use this if you have musl targets to compile - for musl_arch in x86_64 aarch64 riscv64 - do - export PATH="$(pwd)/${musl_arch}-linux-musl-cross/bin:${PATH}" - done - - mix deps.get - MIX_ENV=test mix test - - name: Create precompiled library - run: | - export ELIXIR_MAKE_CACHE_DIR=$(pwd)/cache - mkdir -p "${ELIXIR_MAKE_CACHE_DIR}" - mix elixir_make.precompile - - uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/') - with: - draft: true - files: | - cache/*.tar.gz - - macos: - runs-on: macos-latest - env: - MIX_ENV: "prod" - ELIXIR_VERSION: ${{ matrix.pair.elixir }} - OTP_VERSION: ${{ matrix.pair.otp }} - strategy: - fail-fast: false - matrix: - include: - - pair: - elixir: "1.16.3-otp-24" - otp: "24.3.4.17" - - pair: - elixir: "1.18.2-otp-27" - otp: "27.2.2" - steps: - - uses: actions/checkout@v4 - - name: Install erlang and elixir + - name: Install toolchain uses: jdx/mise-action@v2 with: mise_toml: | [tools] - elixir = "${{ matrix.pair.elixir }}" - erlang = "${{ matrix.pair.otp }}" - - name: Install utilities + elixir = "${{ matrix.elixir }}" + erlang = "${{ matrix.otp }}" + zig = "0.15.2" + protoc = "latest" + - name: Install Hex and Rebar run: | - brew install protobuf mix local.hex --force mix local.rebar --force + - name: Install Linux utilities + if: matrix.runner == 'ubuntu-latest' + run: | + sudo apt-get update + sudo apt-get install -y unzip zip - name: Restore dependencies cache uses: actions/cache@v3 with: diff --git a/Makefile b/Makefile index 304f667..b581a00 100644 --- a/Makefile +++ b/Makefile @@ -7,12 +7,19 @@ LIBPG_QUERY_PATH = libpg_query CFLAGS += -I$(LIBPG_QUERY_PATH) -fPIC +ifdef ZIG_TARGET +CC ?= zig cc -target $(ZIG_TARGET) +else +CC ?= zig cc +endif +LIBPG_QUERY_AR = $(if $(filter zig,$(firstword $(CC))),zig ar rs,ar rs) + LDFLAGS = -lpthread -shared ifeq ($(shell uname -s),Darwin) LDFLAGS += -undefined dynamic_lookup endif -.PHONY: all pg_inspect clean libpg_query_build +.PHONY: all pg_inspect clean precompile_clean libpg_query_build all: priv/pg_inspect.so @@ -20,7 +27,7 @@ priv: mkdir -p priv libpg_query_build: - $(MAKE) -C $(LIBPG_QUERY_PATH) libpg_query.a + $(MAKE) -C $(LIBPG_QUERY_PATH) CC="$(CC)" AR="$(LIBPG_QUERY_AR)" libpg_query.a priv/pg_inspect.so: priv libpg_query_build src/pg_inspect.c $(CC) $(CFLAGS) $(LDFLAGS) -o $@ src/pg_inspect.c $(LIBPG_QUERY_PATH)/libpg_query.a @@ -29,3 +36,7 @@ clean: $(MIX) clean $(MAKE) -C $(LIBPG_QUERY_PATH) clean $(RM) priv/pg_inspect.so + +precompile_clean: + $(MAKE) -C $(LIBPG_QUERY_PATH) clean + $(RM) priv/pg_inspect.so diff --git a/mise.ci.toml b/mise.ci.toml new file mode 100644 index 0000000..5c6130a --- /dev/null +++ b/mise.ci.toml @@ -0,0 +1,5 @@ +[tools] +erlang = "28.4" +elixir = "1.19.5-otp-28" +zig = "0.15.2" +protoc = "latest" diff --git a/mise.toml b/mise.toml index 7f4763e..5f9e95d 100644 --- a/mise.toml +++ b/mise.toml @@ -1,5 +1,6 @@ [tools] -elixir = "1.20.0-rc.2-otp-28" +erlang = "28.4" +elixir = "1.20.0-rc.3-otp-28" ast-grep = "latest" bat = "latest" eza = "latest" @@ -9,3 +10,5 @@ jq = "latest" rg = "latest" sd = "latest" yq = "latest" +zig = "0.15.2" +protoc = "latest" diff --git a/mix.exs b/mix.exs index 155d7f5..d36c2db 100644 --- a/mix.exs +++ b/mix.exs @@ -2,12 +2,18 @@ defmodule PgInspect.MixProject do use Mix.Project @version "0.1.0" + @zig_linux_targets [ + "x86_64-linux-gnu", + "aarch64-linux-gnu", + "x86_64-linux-musl", + "aarch64-linux-musl" + ] def project do [ app: :pg_inspect, version: @version, - elixir: "~> 1.17", + elixir: "~> 1.18", start_permanent: Mix.env() == :prod, elixirc_paths: elixirc_paths(Mix.env()), deps: deps(), @@ -21,8 +27,9 @@ defmodule PgInspect.MixProject do make_precompiler_url: "https://github.com/hlindset/pg_inspect/releases/download/v#{@version}/@{artefact_filename}", make_precompiler_priv_paths: ["pg_inspect.*"], - make_precompiler_nif_versions: [versions: ["2.16", "2.17"]], + make_precompiler_nif_versions: [versions: ["2.17"]], make_precompiler_unavailable_target: :compile, + cc_precompiler: cc_precompiler(), # Docs name: "PgInspect", source_url: "https://github.com/hlindset/pg_inspect", @@ -88,6 +95,19 @@ defmodule PgInspect.MixProject do ] end + defp cc_precompiler do + [ + cleanup: "precompile_clean", + compilers: %{ + {:unix, :linux} => + Enum.into(@zig_linux_targets, %{}, fn target -> + {target, + {"zig", "zig", "<%= cc %> cc -target #{target}", "<%= cxx %> c++ -target #{target}"}} + end) + } + ] + end + # Run "mix help deps" to learn about dependencies. defp deps do [ diff --git a/src/pg_inspect.c b/src/pg_inspect.c index 020a643..0690e27 100644 --- a/src/pg_inspect.c +++ b/src/pg_inspect.c @@ -471,10 +471,10 @@ static ERL_NIF_TERM normalize(ErlNifEnv *env, int argc, * {:ok, result} | {:error, reason} */ static ErlNifFunc funcs[] = { - {"parse_protobuf", 1, parse_protobuf, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"deparse_protobuf", 1, deparse_protobuf, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"scan", 1, scan, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"fingerprint", 1, fingerprint, ERL_NIF_DIRTY_JOB_CPU_BOUND}, - {"normalize", 1, normalize, ERL_NIF_DIRTY_JOB_CPU_BOUND}}; + {"parse_protobuf", 1, parse_protobuf}, + {"deparse_protobuf", 1, deparse_protobuf}, + {"scan", 1, scan}, + {"fingerprint", 1, fingerprint}, + {"normalize", 1, normalize}}; ERL_NIF_INIT(Elixir.PgInspect.Native, funcs, NULL, NULL, NULL, NULL)