update
This commit is contained in:
BIN
phoenix/deps/cc_precompiler/.hex
Normal file
BIN
phoenix/deps/cc_precompiler/.hex
Normal file
Binary file not shown.
76
phoenix/deps/cc_precompiler/CHANGELOG.md
Normal file
76
phoenix/deps/cc_precompiler/CHANGELOG.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Changelog
|
||||
|
||||
## v0.1.11 (2025-08-02)
|
||||
|
||||
- allow `{:elixir_make, "~> 0.7 or ~> 0.8 or ~> 0.9"}`
|
||||
|
||||
## v0.1.10 (2024-03-10)
|
||||
|
||||
- allow `{:elixir_make, "~> 0.7 or ~> 0.8"}`
|
||||
|
||||
## v0.1.9 (2023-11-16)
|
||||
|
||||
- added an `exclude_current_target` option
|
||||
- updated ex_doc
|
||||
|
||||
## v0.1.8 (2023-07-19)
|
||||
|
||||
### Changed
|
||||
|
||||
Fixed `CCPrecompiler.all_supported_targets(:fetch)`. It should fetch and merge default available compilers when `include_default_ones` is `true`.
|
||||
|
||||
## v0.1.7 (2022-03-13)
|
||||
|
||||
### Added
|
||||
- using `:include_default_ones` in `project.cc_precompiler.compilers`. Default (cross-)compiler will be included if it's `true`, otherwise only specified targets will be used.
|
||||
|
||||
Default value of `:include_default_ones` is `false` to avoid breaking changes.
|
||||
|
||||
If a custom target has the same name as a default one, then the custom one will override the default configuration for that target (e.g., the `x86_64-linux-gnu` entry below will override the default gcc configuration and use clang instead).
|
||||
|
||||
```elixir
|
||||
def project do
|
||||
[
|
||||
# ...
|
||||
cc_precompiler: [
|
||||
compilers: %{
|
||||
{:unix, :linux} => %{
|
||||
:include_default_ones => true,
|
||||
"my-custom-target" => {
|
||||
"my-custom-target-gcc",
|
||||
"my-custom-target-g++"
|
||||
},
|
||||
"x86_64-linux-gnu" => {
|
||||
"x86_64-linux-gnu-clang",
|
||||
"x86_64-linux-gnu-clang++"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
## v0.1.6 (2022-02-20)
|
||||
|
||||
### Added
|
||||
- allow missing CC or CXX when detecting available targets by setting `allow_missing_compiler` to `true`.
|
||||
|
||||
Adding this option because there is no need to require the presence of both CC and CXX for projects that only uses one of them.
|
||||
|
||||
```elixir
|
||||
def project do
|
||||
[
|
||||
# ...
|
||||
cc_precompiler: [
|
||||
# optional config key
|
||||
# true - the corresponding target will be available as long as we can detect either `CC` or `CXX`
|
||||
# false - both `CC` and `CXX` should be present on the system
|
||||
# defaults to `false`
|
||||
allow_missing_compiler: false,
|
||||
# ...
|
||||
],
|
||||
# ...
|
||||
]
|
||||
end
|
||||
```
|
||||
201
phoenix/deps/cc_precompiler/LICENSE
Normal file
201
phoenix/deps/cc_precompiler/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
342
phoenix/deps/cc_precompiler/PRECOMPILATION_GUIDE.md
Normal file
342
phoenix/deps/cc_precompiler/PRECOMPILATION_GUIDE.md
Normal file
@@ -0,0 +1,342 @@
|
||||
# Precompilation guide
|
||||
|
||||
This guide has two sections, the first one is intended for precompiler module developers. It covers a minimal example of creating a precompiler module. The second section is intended for library developers who want their library to be able to use precompiled artefacts in a simple way.
|
||||
|
||||
## Library Developer
|
||||
|
||||
This guide assumes you have already added `elixir_make` to your library and you have written a `Makefile` that compiles the native code in your project. Once your native code compile and works as expected, you are now ready to precompile it.
|
||||
|
||||
A full demo project is available on [cocoa-xu/cc_precompiler_example](https://github.com/cocoa-xu/cc_precompiler_example).
|
||||
|
||||
### Setup mix.exs
|
||||
|
||||
To use a precompiler module such as the `CCPrecompiler` example above, we first add the precompiler (`:cc_precompiler` here) and `:elixir_make` to `deps`.
|
||||
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
# ...
|
||||
{:elixir_make, "~> 0.6", runtime: false},
|
||||
{:cc_precompiler, "~> 0.1", runtime: false, github: "cocoa-xu/cc_precompiler"}
|
||||
# ...
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Then add `:elixir_make` to the `compilers` list, and set `CCPrecompile` as the value for `make_precompiler`.
|
||||
|
||||
```elixir
|
||||
@version "0.1.0"
|
||||
def project do
|
||||
[
|
||||
# ...
|
||||
compilers: [:elixir_make] ++ Mix.compilers(),
|
||||
# elixir_make specific config
|
||||
make_precompiler: {:nif, CCPrecompiler},
|
||||
make_precompiler_url: "https://github.com/cocoa-xu/cc_precompiler_example/releases/download/v#{@version}/@{artefact_filename}",
|
||||
make_precompiler_filename: "nif",
|
||||
make_precompiler_priv_paths: ["nif.*"],
|
||||
make_precompiler_unavailable_target: :compile,
|
||||
# ...
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Another required field is `make_precompiled_url`. It is a URL template to the artefact file.
|
||||
|
||||
`@{artefact_filename}` in the URL template string will be replaced by corresponding artefact filenames when fetching them. For example, `cc_precompiler_example-nif-2.16-x86_64-linux-gnu-0.1.0.tar.gz`.
|
||||
|
||||
Note that there is an optional config key for elixir_make, `make_precompiler_filename`. If the name (file extension does not count) of the shared library is different from your app's name, then `make_precompiler_filename` should be set. For example, if the app name is `"cc_precompiler_example"` while the name shared library is `"nif.so"` (or `"nif.dll"` on windows), then `make_precompiler_filename` should be set as `"nif"`.
|
||||
|
||||
Another optional config key is `make_precompiler_priv_paths`. For example, say the `priv` directory is organised as follows in Linux, macOS and Windows respectively,
|
||||
|
||||
Also, you can specify how to recover from unavailable targets using the `make_precompiler_unavailable_target` config key. Allowed values are `:compile` and `:ignore`. Defaults to `:compile`.
|
||||
|
||||
It is also possible to pass in a 2-arity function to `make_precompiler_unavailable_target`: the first argument is the triplet of the unavailable target, and the second argument is a list that contains all available targets given by the precompiler.
|
||||
|
||||
```
|
||||
# Linux
|
||||
.
|
||||
├── assets
|
||||
│ ├── model.onnx
|
||||
│ └── data.json
|
||||
├── lib
|
||||
│ ├── libpriv1.so
|
||||
│ ├── libpriv2.so
|
||||
│ └── libpriv3.so
|
||||
└── nif.so
|
||||
|
||||
# macOS
|
||||
.
|
||||
├── assets
|
||||
│ ├── model.onnx
|
||||
│ └── data.json
|
||||
├── lib
|
||||
│ ├── libpriv1.dylib
|
||||
│ ├── libpriv2.dylib
|
||||
│ └── libpriv3.dylib
|
||||
└── nif.so
|
||||
|
||||
# Windows
|
||||
.
|
||||
├── assets
|
||||
│ ├── model.onnx
|
||||
│ └── data.json
|
||||
├── lib
|
||||
│ ├── libpriv1.dll
|
||||
│ ├── libpriv2.dll
|
||||
│ └── libpriv3.dll
|
||||
└── nif.dll
|
||||
```
|
||||
|
||||
By default, everything in `priv` will be included in the precompiled tar file. However, files in `assets` can be very large or platform-independent, therefore, we would like to only include the `nif.so` (`nif.dll`) file and everything in the `lib` directory in the precompiled tar file to reduce the footprint. In this case, we can set `make_precompiler_priv_paths` to `["nif.so", "nif.dll", "lib"]`.
|
||||
|
||||
Of course, wildcards (`?`, `**`, `*`) are supported when specifying files. For example, `["nif.*", "lib/*.so", "lib/*.dll", "lib/*.dylib"]` will include `nif.so` (Linux/macOS) or `nif.dll` (Windows), and `.so` or `.dll` files in the `lib` directory.
|
||||
|
||||
Directory structures and symbolic links are preserved.
|
||||
|
||||
### (Optional) Test the NIF code locally
|
||||
|
||||
To test the NIF code locally, you can either set `force_build` to `true` or append `"-dev"` to your NIF library's version string.
|
||||
|
||||
```elixir
|
||||
@version "0.1.0-dev"
|
||||
|
||||
def project do
|
||||
[
|
||||
# either append `"-dev"` to your NIF library's version string
|
||||
version: @version,
|
||||
# or set force_build to true
|
||||
force_build: true,
|
||||
# ...
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Doing so will ask `elixir_make` to only compile for the current host instead of building for all available targets.
|
||||
|
||||
```shell
|
||||
$ mix compile
|
||||
cc -shared -std=c11 -O3 -fPIC -I"/usr/local/lib/erlang/erts-13.0.3/include" -undefined dynamic_lookup -flat_namespace -undefined suppress "/Users/cocoa/git/cc_precompiler_example/c_src/cc_precompiler_example.c" -o "/Users/cocoa/Git/cc_precompiler_example/_build/dev/lib/cc_precompiler_example/priv/nif.so"
|
||||
$ mix test
|
||||
make: Nothing to be done for `build'.
|
||||
Generated cc_precompiler_example app
|
||||
.
|
||||
|
||||
Finished in 0.00 seconds (0.00s async, 0.00s sync)
|
||||
1 test, 0 failures
|
||||
|
||||
Randomized with seed 102464
|
||||
```
|
||||
|
||||
### Precompile for available targets
|
||||
|
||||
It's possible to either setup a CI task to do the precompilation job or precompile on a local machine and upload the precompiled artefacts.
|
||||
|
||||
To precompile for all targets on a local machine:
|
||||
|
||||
```shell
|
||||
MIX_ENV=prod mix elixir_make.precompile
|
||||
```
|
||||
|
||||
Environment variable `ELIXIR_MAKE_CACHE_DIR` can be used to set the cache dir for the precompiled artefacts, for instance, to output precompiled artefacts in the cache directory of the current working directory, `export ELIXIR_MAKE_CACHE_DIR="$(pwd)/cache"`.
|
||||
|
||||
To setup a CI task such as GitHub Actions, the following workflow file can be used for reference:
|
||||
|
||||
```yml
|
||||
name: precompile
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
MIX_ENV: "prod"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: erlef/setup-beam@v1
|
||||
with:
|
||||
otp-version: "25.1"
|
||||
elixir-version: "1.14"
|
||||
|
||||
- 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 g++ \
|
||||
gcc-i686-linux-gnu g++-i686-linux-gnu \
|
||||
gcc-aarch64-linux-gnu g++-aarch64-linux-gnu \
|
||||
gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
|
||||
gcc-riscv64-linux-gnu g++-riscv64-linux-gnu \
|
||||
gcc-powerpc64le-linux-gnu g++-powerpc64le-linux-gnu \
|
||||
gcc-s390x-linux-gnu g++-s390x-linux-gnu
|
||||
|
||||
- name: Get musl cross-compilers (Optional, use this if you have musl targets to compile)
|
||||
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: Mix Test
|
||||
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:
|
||||
files: |
|
||||
cache/*.tar.gz
|
||||
|
||||
macos:
|
||||
runs-on: macos-11
|
||||
env:
|
||||
MIX_ENV: "prod"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install erlang and elixir
|
||||
run: |
|
||||
brew install erlang elixir
|
||||
mix local.hex --force
|
||||
mix local.rebar --force
|
||||
|
||||
- name: Mix Test
|
||||
run: |
|
||||
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:
|
||||
files: |
|
||||
cache/*.tar.gz
|
||||
```
|
||||
|
||||
### Generate checksum file
|
||||
After CI has finished, you can fetch the precompiled binaries from GitHub.
|
||||
|
||||
```shell
|
||||
$ MIX_ENV=prod mix elixir_make.checksum --all --ignore-unavailable
|
||||
```
|
||||
|
||||
Meanwhile, a checksum file will be generated. In this example, the checksum file will be named as `checksum.exs` in current working directory.
|
||||
|
||||
This checksum file is extremely important in the scenario where you need to release a Hex package using precompiled NIFs. It's **MANDATORY** to include this file in your Hex package (by updating the `files` field in the `mix.exs`). Otherwise your package **won't work**.
|
||||
|
||||
```elixir
|
||||
defp package do
|
||||
[
|
||||
files: [
|
||||
# ...
|
||||
"checksum.exs",
|
||||
# ...
|
||||
],
|
||||
# ...
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
However, there is no need to track the checksum file in your version control system (git or other).
|
||||
|
||||
### (Optional) Test fetched artefacts can work locally
|
||||
```shell
|
||||
# delete previously built binaries so that
|
||||
# elixir_make will try to restore the NIF library
|
||||
# from the downloaded tarball file
|
||||
$ rm -rf _build/prod/lib/cc_precompiler_example
|
||||
# set to prod env and test everything
|
||||
$ MIX_ENV=prod mix test
|
||||
==> castore
|
||||
Compiling 1 file (.ex)
|
||||
Generated castore app
|
||||
==> elixir_make
|
||||
Compiling 5 files (.ex)
|
||||
Generated elixir_make app
|
||||
==> cc_precompiler
|
||||
Compiling 1 file (.ex)
|
||||
Generated cc_precompiler app
|
||||
|
||||
20:47:42.262 [debug] Restore NIF for current node from: /Users/cocoa/Library/Caches/cc_precompiler_example-nif-2.16-aarch64-apple-darwin-0.1.0.tar.gz
|
||||
==> cc_precompiler_example
|
||||
Compiling 1 file (.ex)
|
||||
Generated cc_precompiler_example app
|
||||
.
|
||||
|
||||
Finished in 0.01 seconds (0.00s async, 0.01s sync)
|
||||
1 test, 0 failures
|
||||
|
||||
Randomized with seed 539590
|
||||
```
|
||||
|
||||
## Recommended flow
|
||||
To recap, the suggested flow is the following:
|
||||
|
||||
1. Choose an appropriate precompiler for your NIF library and set all necessary options in the `mix.exs`.
|
||||
2. (Optional) Test if your NIF library compiles locally.
|
||||
|
||||
```shell
|
||||
mix compile
|
||||
mix test
|
||||
```
|
||||
|
||||
3. (Optional) Test if your NIF library can precompile to all specified targets locally.
|
||||
```shell
|
||||
MIX_ENV=prod mix elixir_make.precompile
|
||||
```
|
||||
|
||||
4. Precompile your library on CI or locally.
|
||||
|
||||
```shell
|
||||
# locally
|
||||
MIX_ENV=prod mix elixir_make.precompile
|
||||
# CI
|
||||
# please see the docs above
|
||||
```
|
||||
|
||||
5. Fetch precompiled binaries from GitHub.
|
||||
|
||||
```shell
|
||||
# only fetch artefact for current host
|
||||
MIX_ENV=prod mix elixir_make.checksum --only-local --print
|
||||
# fetch all
|
||||
MIX_ENV=prod mix elixir_make.checksum --all --print
|
||||
# to fetch all available artefacts at the moment
|
||||
MIX_ENV=prod mix elixir_make.checksum --all --print --ignore-unavailable
|
||||
```
|
||||
|
||||
6. (Optional) Test if the downloaded artefacts works as expected.
|
||||
|
||||
```shell
|
||||
rm -rf _build/prod/lib/NIF_LIBRARY_NAME
|
||||
MIX_ENV=prod mix test
|
||||
```
|
||||
|
||||
6. Update Hex package to include the checksum file.
|
||||
7. Release the package to Hex.pm (make sure your release includes the correct files).
|
||||
342
phoenix/deps/cc_precompiler/README.md
Normal file
342
phoenix/deps/cc_precompiler/README.md
Normal file
@@ -0,0 +1,342 @@
|
||||
# CC Precompiler
|
||||
|
||||
[](https://hex.pm/packages/cc_precompiler)
|
||||
|
||||
C/C++ Cross-compiler Precompiler is a library that supports [elixir_make](https://github.com/elixir-lang/elixir_make)'s precompilation feature. It's customisble and easy to extend.
|
||||
|
||||
The guide for how to `cc_precompiler` can be found in the `PRECOMPILATION_GUIED.md` file.
|
||||
|
||||
## Installation
|
||||
|
||||
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||
by adding `cc_precompiler` to your list of dependencies in `mix.exs`:
|
||||
|
||||
```elixir
|
||||
def deps do
|
||||
[
|
||||
{:cc_precompiler, "~> 0.1.6"}
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||
be found at <https://hexdocs.pm/cc_precompiler>.
|
||||
|
||||
## Default Targets
|
||||
|
||||
By default, it will probe some well-known C/C++ crosss-compilers existing on your system:
|
||||
|
||||
#### Linux
|
||||
|
||||
| Target Triplet | Compiler Prefix, `prefix` | `CC` | `CXX` |
|
||||
|:------------------------|:--------------------------|:----------------|:----------------|
|
||||
| `x86_64-linux-gnu` | `x86_64-linux-gnu-` | `#{prefix}gcc` | `#{prefix}g++` |
|
||||
| `i686-linux-gnu` | `i686-linux-gnu-` | `#{prefix}gcc` | `#{prefix}g++` |
|
||||
| `aarch64-linux-gnu` | `aarch64-linux-gnu-` | `#{prefix}gcc` | `#{prefix}g++` |
|
||||
| `armv7l-linux-gnuabihf` | `arm-linux-gnueabihf-` | `#{prefix}gcc` | `#{prefix}g++` |
|
||||
| `riscv64-linux-gnu` | `riscv64-linux-gnu-` | `#{prefix}gcc` | `#{prefix}g++` |
|
||||
| `powerpc64le-linux-gnu` | `powerpc64le-linux-gnu-` | `#{prefix}gcc` | `#{prefix}g++` |
|
||||
| `s390x-linux-gnu` | `s390x-linux-gnu-` | `#{prefix}gcc` | `#{prefix}g++` |
|
||||
|
||||
`cc_precompiler` will try to find `#{prefix}gcc` in `$PATH`, and if `#{prefix}gcc` can be found, then the correspondong target will be activiated. Otherwise, that target will be ignored.
|
||||
|
||||
#### macOS
|
||||
|
||||
| Target Triplet | Compiler Prefix, `prefix` | `CC` | `CXX` |
|
||||
|:------------------------|:--------------------------|:-------------------|:-------------------|
|
||||
| `x86_64-apple-darwin` | N/A | `gcc -arch x86_64` | `g++ -arch x86_64` |
|
||||
| `aarch64-apple-darwin` | N/A | `gcc -arch arm64` | `g++ -arch arm64` |
|
||||
|
||||
`cc_precompiler` will try to find `gcc` in `$PATH`, and if `gcc` can be found, then both `x86_64` and `arm64` target will be activiated. Otherwise, both targets will be ignored.
|
||||
|
||||
#### Note
|
||||
Triplet for current host will be always available, `:erlang.system_info(:system_architecture)`.
|
||||
|
||||
For macOS targets, the version part will be trimmed, e.g., `x86_64-apple-darwin21.6.0` will be `x86_64-apple-darwin`.
|
||||
|
||||
### Note
|
||||
#### Conditionally switch on/off compilation flags depending on the target
|
||||
During the compilation, `cc_precompiler` will set and update the environment variable `CC_PRECOMPILER_CURRENT_TARGET` to the current target's triplet.
|
||||
|
||||
The reason we might need this is that some 3rd party library may support some feature, like AVX, but they do not offer an auto-detection mechanism, and we have to manually switch on/off corresponding compilation flags.
|
||||
|
||||
An example with further explanation can be found on [cocoa-xu/nif_opt_flags](https://github.com/cocoa-xu/nif_opt_flags).
|
||||
|
||||
Last but not least, as the name suggests, this environment variable is set by `cc_precompiler`, thus if you switch to another precompiler, please check their manual for the equvilent.
|
||||
|
||||
### Customise Precompilation Targets
|
||||
|
||||
#### Quick Start
|
||||
|
||||
To add custom targets in addition to the default configuration, you can set `:include_default_ones` in `project.cc_precompiler.compilers`.
|
||||
|
||||
Default (cross-)compiler will be included if it's `true`, otherwise only specified targets will be used.
|
||||
|
||||
Default value of `:include_default_ones` is `false` to avoid breaking changes.
|
||||
|
||||
If a custom target has the same name as a default one, then the custom one will override the default configuration for that target (e.g., the `x86_64-linux-gnu` entry below will override the default gcc configuration and use clang instead).
|
||||
|
||||
```elixir
|
||||
def project do
|
||||
[
|
||||
# ...
|
||||
cc_precompiler: [
|
||||
compilers: %{
|
||||
{:unix, :linux} => %{
|
||||
:include_default_ones => true,
|
||||
"my-custom-target" => {
|
||||
"my-custom-target-gcc",
|
||||
"my-custom-target-g++"
|
||||
},
|
||||
"x86_64-linux-gnu" => {
|
||||
"x86_64-linux-gnu-clang",
|
||||
"x86_64-linux-gnu-clang++"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
end
|
||||
```
|
||||
|
||||
#### Fully Customise Precompilation Targets
|
||||
|
||||
```elixir
|
||||
|
||||
def project do
|
||||
[
|
||||
# ...
|
||||
cc_precompiler: [
|
||||
# optional config key
|
||||
# false - target triplet for the current machine will be included in all available targets
|
||||
# true - only targets listed in `compilers` will be included in all available targets
|
||||
# defaults to `false`
|
||||
only_listed_targets: true,
|
||||
|
||||
# optional config key
|
||||
# this option is valid if and only if `only_listed_targets` is set to `true`
|
||||
# - when `exclude_current_target` is `true`, it excludes current target (i.e., the machine that builds these binaries)
|
||||
# from the list. This can be helpful when you're doing some complex cross-compilations,
|
||||
# e.g., you'd like to specify which CI job should build for the x86_64-linux-gnu target
|
||||
# this will force current target to be excluded from the list
|
||||
exclude_current_target: false,
|
||||
|
||||
# optional config key
|
||||
# clean up the priv directory between different targets
|
||||
#
|
||||
# for example, common assets for different targets can stay
|
||||
# in the `priv` directory (instead of copying/downloading them
|
||||
# multiple times)
|
||||
# but target specific assets or .o files should be cleaned
|
||||
# so that `make` can compile/generate these files for the next target
|
||||
#
|
||||
# the value for `cleanup` should be a string indicating the cleanup target
|
||||
# in the makefile.
|
||||
#
|
||||
# for example, cc_precompiler will call `make mycleanup` between each build
|
||||
# if the value for the key `cleanup` is set to `mycleanup`
|
||||
#
|
||||
# also, cc_precompiler will stop if `make mycleanup` exited with non-zero code
|
||||
#
|
||||
# the default value for this key is `nil`, and in such case, cc_precompiler
|
||||
# will not do anything between each build
|
||||
cleanup: "mycleanup",
|
||||
|
||||
# optional config key
|
||||
# true - the corresponding target will be available as long as we can detect either `CC` or `CXX`
|
||||
# false - both `CC` and `CXX` should be present on the system
|
||||
# defaults to `false`
|
||||
allow_missing_compiler: false,
|
||||
|
||||
# optional config that provides a map of available compilers
|
||||
# on different systems
|
||||
compilers: %{
|
||||
# key (`:os.type()`)
|
||||
# this allows us to provide different available targets
|
||||
# on different systems
|
||||
# value is a map that describes which compilers are available
|
||||
#
|
||||
# key == {:unix, :linux} => when compiling on Linux
|
||||
{:unix, :linux} => %{
|
||||
# key (target triplet) => `riscv64-linux-gnu`
|
||||
# value => `PREFIX`
|
||||
# - for strings, the string will be used as the prefix of
|
||||
# the C and C++ compiler respectively, i.e.,
|
||||
# CC=`#{prefix}gcc`
|
||||
# CXX=`#{prefix}g++`
|
||||
"riscv64-linux-gnu" => "riscv64-linux-gnu-",
|
||||
# key (target triplet) => `armv7l-linux-gnueabihf`
|
||||
# value => `{CC, CXX}`
|
||||
# - for 2-tuples, the elements are the executable name of
|
||||
# the C and C++ compiler respectively
|
||||
"armv7l-linux-gnueabihf" => {
|
||||
"arm-linux-gnueabihf-gcc",
|
||||
"arm-linux-gnueabihf-g++"
|
||||
},
|
||||
# key (target triplet) => `armv7l-linux-gnueabihf`
|
||||
# value => `{CC_EXECUTABLE, CXX_EXECUTABLE, CC_TEMPLATE, CXX_TEMPLATE}`
|
||||
#
|
||||
# - for 4-tuples, the first two elements are the same as in
|
||||
# 2-tuple, the third and fourth elements are the template
|
||||
# string for CC and CPP/CXX. for example,
|
||||
#
|
||||
# the last entry below shows the example of using zig as the
|
||||
# crosscompiler for `aarch64-linux-musl`,
|
||||
# the "CC" will be
|
||||
# "zig cc -target aarch64-linux-musl",
|
||||
# and "CXX" and "CPP" will be
|
||||
# "zig c++ -target aarch64-linux-musl"
|
||||
"aarch64-linux-musl" => {
|
||||
"zig",
|
||||
"zig",
|
||||
"<% cc %> cc -target aarch64-linux-musl",
|
||||
"<% cxx %> c++ -target aarch64-linux-musl"
|
||||
}
|
||||
},
|
||||
# key == {:unix, :darwin} => when compiling on macOS
|
||||
{:unix, :darwin} => %{
|
||||
# key (target triplet) => `aarch64-apple-darwin`
|
||||
# value => `{CC, CXX}`
|
||||
"aarch64-apple-darwin" => {
|
||||
"gcc -arch arm64", "g++ -arch arm64"
|
||||
},
|
||||
# key (target triplet) => `aarch64-linux-musl`
|
||||
# value => `{CC_EXECUTABLE, CXX_EXECUTABLE, CC_TEMPLATE, CXX_TEMPLATE}`
|
||||
"aarch64-linux-musl" => {
|
||||
"zig",
|
||||
"zig",
|
||||
"<% cc %> cc -target aarch64-linux-musl",
|
||||
"<% cxx %> c++ -target aarch64-linux-musl"
|
||||
},
|
||||
# key (target triplet) => `my-custom-target`
|
||||
# - for 3-tuples, the first element should be `:script`
|
||||
# the second element is the path to the elixir script file
|
||||
# the third element is a 2-tuple,
|
||||
# the first one is the name of the module
|
||||
# the second one is custom args
|
||||
# the module need to impl the `compile/5` callback declared in
|
||||
# `CCPrecompiler.CompilationScript`
|
||||
"my-custom-target" => {
|
||||
:script, "custom.exs", {CustomCompile, []}
|
||||
},
|
||||
# key (target triplet) => `macos-universal`
|
||||
# on macOS, CCPrecompiler also provides a builtin module to create
|
||||
# universal binary for NIF libraries that only has a `nif.so` file
|
||||
"macos-universal" => {
|
||||
:script, "", {CCPrecompiler.UniversalBinary, []}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
```
|
||||
|
||||
`CCPrecompiler.CompilationScript` is defined as follows,
|
||||
|
||||
```elixir
|
||||
defmodule CCPrecompiler.CompilationScript do
|
||||
@callback compile(
|
||||
app :: atom(),
|
||||
version :: String.t(),
|
||||
nif_version :: String.t(),
|
||||
target :: String.t(),
|
||||
command_line_args :: [String.t()],
|
||||
custom_args :: [String.t()]
|
||||
) :: :ok | {:error, String.t()}
|
||||
end
|
||||
```
|
||||
|
||||
### Custom Compilation Script Examples
|
||||
#### Compile with `ccache`
|
||||
|
||||
```elixir
|
||||
defmodule CCPrecompiler.CCache do
|
||||
@moduledoc """
|
||||
Compile with ccache
|
||||
|
||||
## Example
|
||||
|
||||
"x86_64-linux-gnu" => {
|
||||
:script, "custom.exs", {CCPrecompiler.CCache, []}
|
||||
}
|
||||
|
||||
It's also possible to do this using a 4-tuple:
|
||||
|
||||
"x86_64-linux-musl" => {
|
||||
"gcc", "g++", "ccache <% cc %>", "ccache <% cxx %>"
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
@behaviour CCPrecompiler.CompilationScript
|
||||
|
||||
@impl CCPrecompiler.CompilationScript
|
||||
def compile(app, version, nif_version, target, args, _custom_args) do
|
||||
System.put_env("CC", "ccache gcc")
|
||||
System.put_env("CXX", "ccache g++")
|
||||
System.put_env("CPP", "ccache g++")
|
||||
|
||||
ElixirMake.Precompiler.mix_compile(args)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
#### Build A Universal NIF Binary on macOS
|
||||
File can be found at `lib/complation_script/universal_binary.ex`.
|
||||
|
||||
```elixir
|
||||
defmodule CCPrecompiler.UniversalBinary do
|
||||
@moduledoc """
|
||||
Build a universal binary on macOS
|
||||
|
||||
## Example
|
||||
|
||||
"macos-universal" => {
|
||||
:script, "universal_binary.exs", {CCPrecompiler.UniversalBinary, []}
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
@behaviour CCPrecompiler.CompilationScript
|
||||
|
||||
@impl CCPrecompiler.CompilationScript
|
||||
def compile(_app, _version, _nif_version, _target, args, _custom_args) do
|
||||
config = Mix.Project.config()
|
||||
app_priv = Path.join(Mix.Project.app_path(config), "priv")
|
||||
make_precompiler_filename = config[:make_precompiler_filename] || "nif"
|
||||
nif_file = "#{make_precompiler_filename}.so"
|
||||
|
||||
compiled_bin = Path.join(app_priv, nif_file)
|
||||
x86_64_bin = Path.join(app_priv, "#{make_precompiler_filename}_x86_64.so")
|
||||
aarch64_bin = Path.join(app_priv, "#{make_precompiler_filename}_aarch64.so")
|
||||
|
||||
File.rm(compiled_bin)
|
||||
|
||||
# first we compile `x86_64-apple-darwin`
|
||||
:ok = System.put_env("CC", "gcc -arch x86_64")
|
||||
System.put_env("CXX", "gcc -arch x86_64")
|
||||
System.put_env("CPP", "g++ -arch x86_64")
|
||||
ElixirMake.Compiler.compile(args)
|
||||
File.rename!(compiled_bin, x86_64_bin)
|
||||
|
||||
# then we compile `aarch64-apple-darwin`
|
||||
System.put_env("CC", "gcc -arch arm64")
|
||||
System.put_env("CXX", "gcc -arch arm64")
|
||||
System.put_env("CPP", "g++ -arch arm64")
|
||||
ElixirMake.Compiler.compile(args)
|
||||
File.rename!(compiled_bin, aarch64_bin)
|
||||
|
||||
{%IO.Stream{}, exit_status} = System.cmd("lipo", ["-create", "-output", compiled_bin, x86_64_bin, aarch64_bin])
|
||||
|
||||
File.rm!(x86_64_bin)
|
||||
File.rm!(aarch64_bin)
|
||||
|
||||
if exit_status == 0 do
|
||||
:ok
|
||||
else
|
||||
Mix.raise("Failed to create universal binary")
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
28
phoenix/deps/cc_precompiler/hex_metadata.config
Normal file
28
phoenix/deps/cc_precompiler/hex_metadata.config
Normal file
@@ -0,0 +1,28 @@
|
||||
{<<"links">>,
|
||||
[{<<"Changelog">>,
|
||||
<<"https://github.com/cocoa-xu/cc_precompiler/blob/v0.1.11/CHANGELOG.md">>},
|
||||
{<<"GitHub">>,<<"https://github.com/cocoa-xu/cc_precompiler">>},
|
||||
{<<"Precompilation Guide">>,
|
||||
<<"https://github.com/cocoa-xu/cc_precompiler/blob/v0.1.11/PRECOMPILATION_GUIDE.md">>},
|
||||
{<<"Readme">>,
|
||||
<<"https://github.com/cocoa-xu/cc_precompiler/blob/v0.1.11/README.md">>}]}.
|
||||
{<<"name">>,<<"cc_precompiler">>}.
|
||||
{<<"version">>,<<"0.1.11">>}.
|
||||
{<<"description">>,
|
||||
<<"NIF library Precompiler that uses C/C++ (cross-)compiler.">>}.
|
||||
{<<"elixir">>,<<"~> 1.11">>}.
|
||||
{<<"app">>,<<"cc_precompiler">>}.
|
||||
{<<"files">>,
|
||||
[<<"lib">>,<<"lib/cc_precompiler.ex">>,<<"lib/compilation_script">>,
|
||||
<<"lib/compilation_script/universal_binary.ex">>,
|
||||
<<"lib/compilation_script/compilation_script.ex">>,<<"mix.exs">>,
|
||||
<<"README.md">>,<<"LICENSE">>,<<"CHANGELOG.md">>,
|
||||
<<"PRECOMPILATION_GUIDE.md">>]}.
|
||||
{<<"licenses">>,[<<"Apache-2.0">>]}.
|
||||
{<<"requirements">>,
|
||||
[[{<<"name">>,<<"elixir_make">>},
|
||||
{<<"app">>,<<"elixir_make">>},
|
||||
{<<"optional">>,false},
|
||||
{<<"requirement">>,<<"~> 0.7 or ~> 0.8 or ~> 0.9">>},
|
||||
{<<"repository">>,<<"hexpm">>}]]}.
|
||||
{<<"build_tools">>,[<<"mix">>]}.
|
||||
576
phoenix/deps/cc_precompiler/lib/cc_precompiler.ex
Normal file
576
phoenix/deps/cc_precompiler/lib/cc_precompiler.ex
Normal file
@@ -0,0 +1,576 @@
|
||||
defmodule CCPrecompiler do
|
||||
@moduledoc """
|
||||
Precompile with existing crosscompiler in the system.
|
||||
"""
|
||||
|
||||
require Logger
|
||||
@behaviour ElixirMake.Precompiler
|
||||
|
||||
# The default configuration for this precompiler module on linux systems.
|
||||
# It will detect for the following targets
|
||||
# - x86_64-linux-gnu
|
||||
# - i686-linux-gnu
|
||||
# - aarch64-linux-gnu
|
||||
# - armv7l-linux-gnueabihf
|
||||
# - riscv64-linux-gnu
|
||||
# - powerpc64le-linux-gnu
|
||||
# - s390x-linux-gnu
|
||||
# by trying to find the corresponding executable, i.e.,
|
||||
# - x86_64-linux-gnu-gcc
|
||||
# - i686-linux-gnu-gcc
|
||||
# - aarch64-linux-gnu-gcc
|
||||
# - arm-linux-gnueabihf-gcc
|
||||
# - riscv64-linux-gnu-gcc
|
||||
# - powerpc64le-linux-gnu-gcc
|
||||
# - s390x-linux-gnu-gcc
|
||||
# (this module will only try to find the CC executable, a step further
|
||||
# will be trying to compile a simple C/C++ program using them)
|
||||
@default_compilers %{
|
||||
{:unix, :linux} => %{
|
||||
"x86_64-linux-gnu" => "x86_64-linux-gnu-",
|
||||
"i686-linux-gnu" => "i686-linux-gnu-",
|
||||
"aarch64-linux-gnu" => "aarch64-linux-gnu-",
|
||||
"armv7l-linux-gnueabihf" => "arm-linux-gnueabihf-",
|
||||
"riscv64-linux-gnu" => "riscv64-linux-gnu-",
|
||||
"powerpc64le-linux-gnu" => "powerpc64le-linux-gnu-",
|
||||
"s390x-linux-gnu" => "s390x-linux-gnu-"
|
||||
},
|
||||
{:unix, :darwin} => %{
|
||||
"x86_64-apple-darwin" => {
|
||||
"gcc",
|
||||
"g++",
|
||||
"<%= cc %> -arch x86_64",
|
||||
"<%= cxx %> -arch x86_64"
|
||||
},
|
||||
"aarch64-apple-darwin" => {
|
||||
"gcc",
|
||||
"g++",
|
||||
"<%= cc %> -arch arm64",
|
||||
"<%= cxx %> -arch arm64"
|
||||
}
|
||||
},
|
||||
{:win32, :nt} => %{
|
||||
"x86_64-windows-msvc" => {"cl", "cl"}
|
||||
}
|
||||
}
|
||||
defp default_compilers, do: @default_compilers
|
||||
defp user_config, do: Mix.Project.config()[:cc_precompiler] || default_compilers()
|
||||
defp compilers, do: Access.get(user_config(), :compilers, default_compilers())
|
||||
|
||||
defp compilers_current_os,
|
||||
do:
|
||||
{Access.get(compilers(), :os.type(), %{}), Access.get(default_compilers(), :os.type(), %{})}
|
||||
|
||||
defp compilers_current_os_with_override do
|
||||
{compiler_map1, compiler_map2} = compilers_current_os()
|
||||
|
||||
if Map.has_key?(compiler_map1, :include_default_ones) do
|
||||
include_default_ones = Map.get(compiler_map1, :include_default_ones, false)
|
||||
compiler_map1 = Map.delete(compiler_map1, :include_default_ones)
|
||||
|
||||
if include_default_ones == true do
|
||||
Map.merge(compiler_map1, compiler_map2, fn _, _, user_override -> user_override end)
|
||||
else
|
||||
compiler_map1
|
||||
end
|
||||
else
|
||||
compiler_map1
|
||||
end
|
||||
end
|
||||
|
||||
defp only_listed_targets, do: Access.get(user_config(), :only_listed_targets, false)
|
||||
defp exclude_current_target, do: Access.get(user_config(), :exclude_current_target, false)
|
||||
defp allow_missing_compiler, do: Access.get(user_config(), :allow_missing_compiler, false)
|
||||
|
||||
@impl ElixirMake.Precompiler
|
||||
def current_target do
|
||||
current_target_from_env = current_target_from_env()
|
||||
|
||||
if current_target_from_env do
|
||||
# overwrite current target triplet from environment variables
|
||||
{:ok, current_target_from_env}
|
||||
else
|
||||
current_target(:os.type())
|
||||
end
|
||||
end
|
||||
|
||||
defp current_target_from_env do
|
||||
arch = System.get_env("TARGET_ARCH")
|
||||
os = System.get_env("TARGET_OS")
|
||||
abi = System.get_env("TARGET_ABI")
|
||||
|
||||
if !Enum.all?([arch, os, abi], &Kernel.is_nil/1) do
|
||||
"#{arch}-#{os}-#{abi}"
|
||||
end
|
||||
end
|
||||
|
||||
def current_target({:win32, _}) do
|
||||
processor_architecture =
|
||||
String.downcase(String.trim(System.get_env("PROCESSOR_ARCHITECTURE")))
|
||||
|
||||
# https://docs.microsoft.com/en-gb/windows/win32/winprog64/wow64-implementation-details?redirectedfrom=MSDN
|
||||
partial_triplet =
|
||||
case processor_architecture do
|
||||
"amd64" ->
|
||||
"x86_64-windows-"
|
||||
|
||||
"ia64" ->
|
||||
"ia64-windows-"
|
||||
|
||||
"arm64" ->
|
||||
"aarch64-windows-"
|
||||
|
||||
"x86" ->
|
||||
"x86-windows-"
|
||||
end
|
||||
|
||||
{compiler, _} = :erlang.system_info(:c_compiler_used)
|
||||
|
||||
case compiler do
|
||||
:msc ->
|
||||
{:ok, partial_triplet <> "msvc"}
|
||||
|
||||
:gnuc ->
|
||||
{:ok, partial_triplet <> "gnu"}
|
||||
|
||||
other ->
|
||||
{:ok, partial_triplet <> Atom.to_string(other)}
|
||||
end
|
||||
end
|
||||
|
||||
def current_target({:unix, _}) do
|
||||
# get current target triplet from `:erlang.system_info/1`
|
||||
system_architecture = to_string(:erlang.system_info(:system_architecture))
|
||||
current = String.split(system_architecture, "-", trim: true)
|
||||
|
||||
case length(current) do
|
||||
4 ->
|
||||
{:ok, "#{Enum.at(current, 0)}-#{Enum.at(current, 2)}-#{Enum.at(current, 3)}"}
|
||||
|
||||
3 ->
|
||||
case :os.type() do
|
||||
{:unix, :darwin} ->
|
||||
# could be something like aarch64-apple-darwin21.0.0
|
||||
# but we don't really need the last 21.0.0 part
|
||||
if String.match?(Enum.at(current, 2), ~r/^darwin.*/) do
|
||||
{:ok, "#{Enum.at(current, 0)}-#{Enum.at(current, 1)}-darwin"}
|
||||
else
|
||||
{:ok, system_architecture}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:ok, system_architecture}
|
||||
end
|
||||
|
||||
_ ->
|
||||
{:error, "cannot decide current target"}
|
||||
end
|
||||
end
|
||||
|
||||
defp only_local do
|
||||
System.get_env("CC_PRECOMPILER_PRECOMPILE_ONLY_LOCAL") == "true"
|
||||
end
|
||||
|
||||
@impl ElixirMake.Precompiler
|
||||
def all_supported_targets(:compile) do
|
||||
# this callback is expected to return a list of string for
|
||||
# all supported targets by this precompiler. in this
|
||||
# implementation, we will try to find a few crosscompilers
|
||||
# available in the system.
|
||||
# Note that this implementation is mainly used for demonstration
|
||||
# purpose, therefore the hardcoded compiler names are used in
|
||||
# DEBIAN/Ubuntu Linux (as I only installed these ones at the
|
||||
# time of writing this example)
|
||||
available_targets = find_all_available_targets()
|
||||
|
||||
targets =
|
||||
case {only_local(), only_listed_targets(), current_target()} do
|
||||
{true, true, {:ok, current}} ->
|
||||
if Enum.member?(available_targets, current) do
|
||||
[current]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
{true, _, {:error, err_msg}} ->
|
||||
Mix.raise(err_msg)
|
||||
|
||||
{true, false, {:ok, current}} ->
|
||||
Enum.uniq([current] ++ available_targets)
|
||||
|
||||
{false, true, _} ->
|
||||
available_targets
|
||||
|
||||
{false, false, {:ok, current}} ->
|
||||
Enum.uniq([current] ++ available_targets)
|
||||
end
|
||||
|
||||
if exclude_current_target() do
|
||||
case current_target() do
|
||||
{:ok, current} ->
|
||||
targets -- [current]
|
||||
|
||||
_ ->
|
||||
targets
|
||||
end
|
||||
else
|
||||
targets
|
||||
end
|
||||
end
|
||||
|
||||
@impl ElixirMake.Precompiler
|
||||
def all_supported_targets(:fetch) do
|
||||
Enum.map(compilers(), fn {os, compilers} ->
|
||||
Enum.map(Map.keys(compilers), fn key ->
|
||||
if key == :include_default_ones do
|
||||
Map.keys(default_compilers()[os])
|
||||
else
|
||||
key
|
||||
end
|
||||
end)
|
||||
end)
|
||||
|> List.flatten()
|
||||
end
|
||||
|
||||
@impl ElixirMake.Precompiler
|
||||
def unavailable_target(_) do
|
||||
if only_listed_targets() do
|
||||
:ignore
|
||||
else
|
||||
:compile
|
||||
end
|
||||
end
|
||||
|
||||
defp find_all_available_targets do
|
||||
compilers = compilers_current_os_with_override()
|
||||
|
||||
compilers
|
||||
|> Map.keys()
|
||||
|> Enum.map(&find_available_compilers(&1, Map.get(compilers, &1)))
|
||||
|> Enum.reject(fn x -> x == nil end)
|
||||
end
|
||||
|
||||
defp find_available_compilers(triplet, prefix) when is_binary(prefix) do
|
||||
if ensure_executable(["#{prefix}gcc", "#{prefix}g++"]) do
|
||||
Logger.debug("Found compiler for #{triplet}")
|
||||
triplet
|
||||
else
|
||||
Logger.debug("Compiler not found for #{triplet}")
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp find_available_compilers(triplet, {cc, cxx}) when is_binary(cc) and is_binary(cxx) do
|
||||
if ensure_executable([cc, cxx]) do
|
||||
Logger.debug("Found compiler for #{triplet}")
|
||||
triplet
|
||||
else
|
||||
Logger.debug("Compiler not found for #{triplet}")
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp find_available_compilers(triplet, {:script, _, _}) do
|
||||
triplet
|
||||
end
|
||||
|
||||
defp find_available_compilers(triplet, {cc_executable, cxx_executable, _, _})
|
||||
when is_binary(cc_executable) and is_binary(cxx_executable) do
|
||||
if ensure_executable([cc_executable, cxx_executable]) do
|
||||
Logger.debug("Found compiler for #{triplet}")
|
||||
triplet
|
||||
else
|
||||
Logger.debug("Compiler not found for #{triplet}")
|
||||
nil
|
||||
end
|
||||
end
|
||||
|
||||
defp find_available_compilers(triplet, invalid) do
|
||||
Mix.raise(
|
||||
"Invalid configuration for #{triplet}, expecting a string, 2-tuple or 4-tuple. Got `#{inspect(invalid)}`"
|
||||
)
|
||||
end
|
||||
|
||||
defp ensure_executable(executable_list) when is_list(executable_list) do
|
||||
if allow_missing_compiler() do
|
||||
Enum.any?(executable_list, &System.find_executable/1)
|
||||
else
|
||||
Enum.all?(executable_list, &System.find_executable/1)
|
||||
end
|
||||
end
|
||||
|
||||
@impl ElixirMake.Precompiler
|
||||
def build_native(args) do
|
||||
# In this callback we just build the NIF library natively,
|
||||
# and because this precompiler module is designed for NIF
|
||||
# libraries that use C/C++ as the main language with Makefile,
|
||||
# we can just call `ElixirMake.Precompiler.mix_compile(args)`
|
||||
#
|
||||
# It's also possible to forward this call to:
|
||||
#
|
||||
# `precompile(args, elem(current_target(), 1))`
|
||||
#
|
||||
# This could be useful when the precompiler is using a universal
|
||||
# (cross-)compiler, say zig. in this way, the compiled binaries
|
||||
# (`mix compile`) will be consistent as the corresponding precompiled
|
||||
# one (with `mix elixir_make.precompile`)
|
||||
#
|
||||
# However, if you'd prefer to having the same behaviour for `mix compile`
|
||||
# then the following line is okay
|
||||
ElixirMake.Precompiler.mix_compile(args)
|
||||
end
|
||||
|
||||
@impl ElixirMake.Precompiler
|
||||
def precompile(args, target) do
|
||||
# in this callback we compile the NIF library for a given target
|
||||
config = Mix.Project.config()
|
||||
app = config[:app]
|
||||
version = config[:version]
|
||||
priv_paths = config[:make_precompiler_priv_paths] || ["."]
|
||||
|
||||
saved_cc = System.get_env("CC") || ""
|
||||
saved_cxx = System.get_env("CXX") || ""
|
||||
saved_cpp = System.get_env("CPP") || ""
|
||||
|
||||
Logger.debug("Current compiling target: #{target}")
|
||||
|
||||
cc_cxx = get_cc_and_cxx(target)
|
||||
|
||||
# remove files in the lists
|
||||
app_priv = Path.join(Mix.Project.app_path(config), "priv")
|
||||
|
||||
case priv_paths do
|
||||
["."] ->
|
||||
File.rm_rf!(app_priv)
|
||||
|
||||
_ ->
|
||||
for include <- priv_paths,
|
||||
file <- Path.wildcard(Path.join(app_priv, include)) do
|
||||
File.rm_rf(file)
|
||||
end
|
||||
end
|
||||
|
||||
File.mkdir_p!(app_priv)
|
||||
|
||||
case cc_cxx do
|
||||
{cc, cxx} ->
|
||||
System.put_env("CC", cc)
|
||||
System.put_env("CXX", cxx)
|
||||
System.put_env("CPP", cxx)
|
||||
|
||||
System.put_env("CC_PRECOMPILER_CURRENT_TARGET", target)
|
||||
ElixirMake.Precompiler.mix_compile(args)
|
||||
|
||||
{:script, module, custom_args} ->
|
||||
System.put_env("CC_PRECOMPILER_CURRENT_TARGET", target)
|
||||
|
||||
Kernel.apply(module, :compile, [
|
||||
app,
|
||||
version,
|
||||
"#{:erlang.system_info(:nif_version)}",
|
||||
target,
|
||||
args,
|
||||
custom_args
|
||||
])
|
||||
end
|
||||
|
||||
System.put_env("CC", saved_cc)
|
||||
System.put_env("CXX", saved_cxx)
|
||||
System.put_env("CPP", saved_cpp)
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp get_cc_and_cxx(triplet) do
|
||||
case Access.get(compilers_current_os_with_override(), triplet, nil) do
|
||||
nil ->
|
||||
cc = System.get_env("CC")
|
||||
cxx = System.get_env("CXX")
|
||||
cpp = System.get_env("CPP")
|
||||
|
||||
case {cc, cxx, cpp} do
|
||||
{nil, _, _} ->
|
||||
{"gcc", "g++"}
|
||||
|
||||
{_, nil, nil} ->
|
||||
{"gcc", "g++"}
|
||||
|
||||
{_, _, nil} ->
|
||||
{cc, cxx}
|
||||
|
||||
{_, nil, _} ->
|
||||
{cc, cpp}
|
||||
|
||||
{_, _, _} ->
|
||||
{cc, cxx}
|
||||
end
|
||||
|
||||
{cc, cxx} ->
|
||||
{cc, cxx}
|
||||
|
||||
prefix when is_binary(prefix) ->
|
||||
{"#{prefix}gcc", "#{prefix}g++"}
|
||||
|
||||
{:script, script_path, {module, args}} ->
|
||||
case {script_path, module} do
|
||||
{"", CCPrecompiler.UniversalBinary} ->
|
||||
{:script, module, args}
|
||||
|
||||
_ ->
|
||||
Code.require_file(script_path)
|
||||
{:script, module, args}
|
||||
end
|
||||
|
||||
{cc, cxx, cc_args, cxx_args} ->
|
||||
{EEx.eval_string(cc_args, cc: cc), EEx.eval_string(cxx_args, cxx: cxx)}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def post_precompile_target(target) do
|
||||
config = Mix.Project.config()
|
||||
cc_precompiler_config = config[:cc_precompiler]
|
||||
cleanup(config, cc_precompiler_config[:cleanup], target)
|
||||
end
|
||||
|
||||
defp cleanup(_, nil, _), do: :ok
|
||||
|
||||
defp cleanup(config, make_target, current_precompilation_target) when is_binary(make_target) do
|
||||
exec =
|
||||
System.get_env("MAKE") ||
|
||||
os_specific_executable(Keyword.get(config, :make_executable, :default))
|
||||
|
||||
makefile = Keyword.get(config, :make_makefile, :default)
|
||||
env = Keyword.get(config, :make_env, %{})
|
||||
env = if is_function(env), do: env.(), else: env
|
||||
env = default_env(config, env, current_precompilation_target)
|
||||
|
||||
# In OTP 19, Erlang's `open_port/2` ignores the current working
|
||||
# directory when expanding relative paths. This means that `:make_cwd`
|
||||
# must be an absolute path. This is a different behaviour from earlier
|
||||
# OTP versions and appears to be a bug. It is being tracked at
|
||||
# https://bugs.erlang.org/browse/ERL-175.
|
||||
cwd = Keyword.get(config, :make_cwd, ".") |> Path.expand(File.cwd!())
|
||||
|
||||
if String.contains?(cwd, " ") do
|
||||
IO.warn(
|
||||
"the absolute path to the makefile for this project contains spaces. Make might " <>
|
||||
"not work properly if spaces are present in the path. The absolute path is: " <>
|
||||
inspect(cwd)
|
||||
)
|
||||
end
|
||||
|
||||
base = exec |> Path.basename() |> Path.rootname()
|
||||
args = args_for_makefile(base, makefile) ++ [make_target]
|
||||
|
||||
case cmd(exec, args, cwd, env) do
|
||||
0 ->
|
||||
:ok
|
||||
|
||||
exit_status ->
|
||||
raise_cleanup_error(exec, exit_status)
|
||||
end
|
||||
end
|
||||
|
||||
defp raise_cleanup_error(exec, exit_status) do
|
||||
Mix.raise(~s{Could not complete cleanup work with "#{exec}" (exit status: #{exit_status}).\n})
|
||||
end
|
||||
|
||||
# Returns a map of default environment variables
|
||||
# Defaults may be overwritten.
|
||||
defp default_env(config, default_env, current_precompilation_target) do
|
||||
root_dir = :code.root_dir()
|
||||
erl_interface_dir = Path.join(root_dir, "usr")
|
||||
erts_dir = Path.join(root_dir, "erts-#{:erlang.system_info(:version)}")
|
||||
erts_include_dir = Path.join(erts_dir, "include")
|
||||
erl_ei_lib_dir = Path.join(erl_interface_dir, "lib")
|
||||
erl_ei_include_dir = Path.join(erl_interface_dir, "include")
|
||||
|
||||
Map.merge(
|
||||
%{
|
||||
# Don't use Mix.target/0 here for backwards compatibility
|
||||
"MIX_TARGET" => env("MIX_TARGET", "host"),
|
||||
"MIX_ENV" => to_string(Mix.env()),
|
||||
"MIX_BUILD_PATH" => Mix.Project.build_path(config),
|
||||
"MIX_APP_PATH" => Mix.Project.app_path(config),
|
||||
"MIX_COMPILE_PATH" => Mix.Project.compile_path(config),
|
||||
"MIX_CONSOLIDATION_PATH" => Mix.Project.consolidation_path(config),
|
||||
"MIX_DEPS_PATH" => Mix.Project.deps_path(config),
|
||||
"MIX_MANIFEST_PATH" => Mix.Project.manifest_path(config),
|
||||
|
||||
# Rebar naming
|
||||
"ERL_EI_LIBDIR" => env("ERL_EI_LIBDIR", erl_ei_lib_dir),
|
||||
"ERL_EI_INCLUDE_DIR" => env("ERL_EI_INCLUDE_DIR", erl_ei_include_dir),
|
||||
|
||||
# erlang.mk naming
|
||||
"ERTS_INCLUDE_DIR" => env("ERTS_INCLUDE_DIR", erts_include_dir),
|
||||
"ERL_INTERFACE_LIB_DIR" => env("ERL_INTERFACE_LIB_DIR", erl_ei_lib_dir),
|
||||
"ERL_INTERFACE_INCLUDE_DIR" => env("ERL_INTERFACE_INCLUDE_DIR", erl_ei_include_dir),
|
||||
|
||||
# Disable default erlang values
|
||||
"BINDIR" => nil,
|
||||
"ROOTDIR" => nil,
|
||||
"PROGNAME" => nil,
|
||||
"EMU" => nil,
|
||||
|
||||
# cc_precompiler
|
||||
"CC_PRECOMPILER_CURRENT_TARGET" => current_precompilation_target
|
||||
},
|
||||
default_env
|
||||
)
|
||||
end
|
||||
|
||||
defp os_specific_executable(exec) when is_binary(exec) do
|
||||
exec
|
||||
end
|
||||
|
||||
defp os_specific_executable(:default) do
|
||||
case :os.type() do
|
||||
{:win32, _} ->
|
||||
cond do
|
||||
System.find_executable("nmake") -> "nmake"
|
||||
System.find_executable("make") -> "make"
|
||||
true -> "nmake"
|
||||
end
|
||||
|
||||
{:unix, type} when type in [:freebsd, :openbsd, :netbsd] ->
|
||||
"gmake"
|
||||
|
||||
_ ->
|
||||
"make"
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a list of command-line args to pass to make (or nmake/gmake) in
|
||||
# order to specify the makefile to use.
|
||||
defp args_for_makefile("nmake", :default), do: ["/F", "Makefile.win"]
|
||||
defp args_for_makefile("nmake", makefile), do: ["/F", makefile]
|
||||
defp args_for_makefile(_, :default), do: []
|
||||
defp args_for_makefile(_, makefile), do: ["-f", makefile]
|
||||
|
||||
# Runs `exec [args]` in `cwd` and prints the stdout and stderr in real time,
|
||||
# as soon as `exec` prints them (using `IO.Stream`).
|
||||
defp cmd(exec, args, cwd, env) do
|
||||
opts = [
|
||||
into: IO.stream(:stdio, :line),
|
||||
stderr_to_stdout: true,
|
||||
cd: cwd,
|
||||
env: env
|
||||
]
|
||||
|
||||
{%IO.Stream{}, status} = System.cmd(find_executable(exec), args, opts)
|
||||
status
|
||||
end
|
||||
|
||||
defp find_executable(exec) do
|
||||
System.find_executable(exec) ||
|
||||
Mix.raise("""
|
||||
"#{exec}" not found in the path. If you have set the MAKE environment variable,
|
||||
please make sure it is correct.
|
||||
""")
|
||||
end
|
||||
|
||||
defp env(var, default) do
|
||||
System.get_env(var) || default
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
defmodule CCPrecompiler.CompilationScript do
|
||||
@callback compile(
|
||||
app :: atom(),
|
||||
version :: String.t(),
|
||||
nif_version :: String.t(),
|
||||
target :: String.t(),
|
||||
command_line_args :: [String.t()],
|
||||
custom_args :: [String.t()]
|
||||
) :: :ok | {:error, String.t()}
|
||||
end
|
||||
@@ -0,0 +1,54 @@
|
||||
defmodule CCPrecompiler.UniversalBinary do
|
||||
@moduledoc """
|
||||
Build a universal binary on macOS
|
||||
|
||||
## Example
|
||||
|
||||
"macos-universal" => {
|
||||
:script, "", {CCPrecompiler.UniversalBinary, []}
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
@behaviour CCPrecompiler.CompilationScript
|
||||
|
||||
@impl CCPrecompiler.CompilationScript
|
||||
def compile(_app, _version, _nif_version, _target, args, _custom_args) do
|
||||
config = Mix.Project.config()
|
||||
app_priv = Path.join(Mix.Project.app_path(config), "priv")
|
||||
make_precompiler_filename = config[:make_precompiler_filename] || "nif"
|
||||
nif_file = "#{make_precompiler_filename}.so"
|
||||
|
||||
compiled_bin = Path.join(app_priv, nif_file)
|
||||
x86_64_bin = Path.join(app_priv, "#{make_precompiler_filename}_x86_64.so")
|
||||
aarch64_bin = Path.join(app_priv, "#{make_precompiler_filename}_aarch64.so")
|
||||
|
||||
File.rm(compiled_bin)
|
||||
|
||||
# first we compile `x86_64-apple-darwin`
|
||||
:ok = System.put_env("CC", "gcc -arch x86_64")
|
||||
System.put_env("CXX", "gcc -arch x86_64")
|
||||
System.put_env("CPP", "g++ -arch x86_64")
|
||||
ElixirMake.Compiler.compile(args)
|
||||
File.rename!(compiled_bin, x86_64_bin)
|
||||
|
||||
# then we compile `aarch64-apple-darwin`
|
||||
System.put_env("CC", "gcc -arch arm64")
|
||||
System.put_env("CXX", "gcc -arch arm64")
|
||||
System.put_env("CPP", "g++ -arch arm64")
|
||||
ElixirMake.Compiler.compile(args)
|
||||
File.rename!(compiled_bin, aarch64_bin)
|
||||
|
||||
{_, exit_status} =
|
||||
System.cmd("lipo", ["-create", "-output", compiled_bin, x86_64_bin, aarch64_bin])
|
||||
|
||||
File.rm!(x86_64_bin)
|
||||
File.rm!(aarch64_bin)
|
||||
|
||||
if exit_status == 0 do
|
||||
:ok
|
||||
else
|
||||
Mix.raise("Failed to create universal binary")
|
||||
end
|
||||
end
|
||||
end
|
||||
63
phoenix/deps/cc_precompiler/mix.exs
Normal file
63
phoenix/deps/cc_precompiler/mix.exs
Normal file
@@ -0,0 +1,63 @@
|
||||
defmodule CCPrecompiler.MixProject do
|
||||
use Mix.Project
|
||||
|
||||
@app :cc_precompiler
|
||||
@version "0.1.11"
|
||||
@github_url "https://github.com/cocoa-xu/cc_precompiler"
|
||||
def project do
|
||||
[
|
||||
app: @app,
|
||||
version: @version,
|
||||
elixir: "~> 1.11",
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
docs: docs(),
|
||||
description: "NIF library Precompiler that uses C/C++ (cross-)compiler.",
|
||||
package: package()
|
||||
]
|
||||
end
|
||||
|
||||
def application do
|
||||
[
|
||||
extra_applications: [:logger, :eex]
|
||||
]
|
||||
end
|
||||
|
||||
defp deps do
|
||||
[
|
||||
{:elixir_make, "~> 0.7 or ~> 0.8 or ~> 0.9", runtime: false},
|
||||
# docs
|
||||
{:ex_doc, ">= 0.0.0", only: :docs, runtime: false}
|
||||
]
|
||||
end
|
||||
|
||||
defp package do
|
||||
[
|
||||
name: Atom.to_string(@app),
|
||||
files: ~w(lib mix.exs README* LICENSE* *.md),
|
||||
licenses: ["Apache-2.0"],
|
||||
links: links()
|
||||
]
|
||||
end
|
||||
|
||||
defp docs do
|
||||
[
|
||||
main: "PRECOMPILATION_GUIDE",
|
||||
source_ref: "v#{@version}",
|
||||
source_url: @github_url,
|
||||
extras: [
|
||||
"PRECOMPILATION_GUIDE.md",
|
||||
"CHANGELOG.md"
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
defp links do
|
||||
%{
|
||||
"GitHub" => @github_url,
|
||||
"Readme" => "#{@github_url}/blob/v#{@version}/README.md",
|
||||
"Precompilation Guide" => "#{@github_url}/blob/v#{@version}/PRECOMPILATION_GUIDE.md",
|
||||
"Changelog" => "#{@github_url}/blob/v#{@version}/CHANGELOG.md"
|
||||
}
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user