homebrew-core/Formula/emscripten.rb

225 lines
8.6 KiB
Ruby

require "language/node"
class Emscripten < Formula
desc "LLVM bytecode to JavaScript compiler"
homepage "https://emscripten.org/"
url "https://github.com/emscripten-core/emscripten/archive/3.1.26.tar.gz"
sha256 "f4c4425e2881bd62f75c6010cfd711c3e4f420870260a20391faa83af6a51993"
license all_of: [
"Apache-2.0", # binaryen
"Apache-2.0" => { with: "LLVM-exception" }, # llvm
any_of: ["MIT", "NCSA"], # emscripten
]
head "https://github.com/emscripten-core/emscripten.git", branch: "main"
livecheck do
url :stable
regex(/^v?(\d+(?:\.\d+)+)$/i)
end
bottle do
sha256 cellar: :any, arm64_ventura: "54cdba2e8a65fee702624fbba39e3204eefc25e678f3d6b074ce80cde83a046b"
sha256 cellar: :any, arm64_monterey: "581b68979a68214237f4b07459f543d8cd90eb4ec4ee1f121e05395888161766"
sha256 cellar: :any, arm64_big_sur: "fca713d70c1373391c52e556c5848450c784271c2a6d9995aeadd39d0e686504"
sha256 cellar: :any, ventura: "1128c4809eedad6d5081339d5d8a7c2414fe5661fe937603a262db1fc4121698"
sha256 cellar: :any, monterey: "7a9a311f8313591fbdcb6f7e299c6db018bafc779e7a856b4ad1e856e05172e2"
sha256 cellar: :any, big_sur: "2f3dfc812c27061c6bca313e579875045609ea063ab53e39e6939dd6ea126fd6"
sha256 cellar: :any, catalina: "e132c5862dbf6a1513802590c15cbcdb19c78f15db4db726b90833a9e754f553"
sha256 cellar: :any_skip_relocation, x86_64_linux: "af64c9ffffeae6c4803275ae1cd9d50678ea296cebb9ae5e2858a9b8ad3310d7"
end
depends_on "cmake" => :build
depends_on "node"
depends_on "python@3.11"
depends_on "yuicompressor"
# OpenJDK is needed as a dependency on Linux and ARM64 for google-closure-compiler,
# an emscripten dependency, because the native GraalVM image will not work.
on_macos do
on_arm do
depends_on "openjdk"
end
end
on_linux do
depends_on "openjdk"
end
fails_with gcc: "5"
# Use emscripten's recommended binaryen revision to avoid build failures.
# https://github.com/emscripten-core/emscripten/issues/12252
# See llvm resource below for instructions on how to update this.
resource "binaryen" do
url "https://github.com/WebAssembly/binaryen.git",
revision: "590f63782b9ee6307a2bc0e21917001a66ba16ba"
end
# emscripten does not support using the stable version of LLVM.
# https://github.com/emscripten-core/emscripten/issues/11362
# To find the correct llvm revision, find a corresponding commit at:
# https://github.com/emscripten-core/emsdk/blob/main/emscripten-releases-tags.json
# Then take this commit and go to:
# https://chromium.googlesource.com/emscripten-releases/+/<commit>/DEPS
# Then use the listed llvm_project_revision for the resource below.
resource "llvm" do
url "https://github.com/llvm/llvm-project.git",
revision: "f81f0cb75a2808a67d2662f044ad07628fc9d900"
end
def install
# Avoid hardcoding the executables we pass to `write_env_script` below.
# Prefer executables without `.py` extensions, but include those with `.py`
# extensions if there isn't a matching executable without the `.py` extension.
emscripts = buildpath.children.select do |pn|
pn.file? && pn.executable? && !(pn.extname == ".py" && pn.basename(".py").exist?)
end.map(&:basename)
# All files from the repository are required as emscripten is a collection
# of scripts which need to be installed in the same layout as in the Git
# repository.
libexec.install buildpath.children
# Remove unneded files. See `tools/install.py`.
(libexec/"test/third_party").rmtree
# emscripten needs an llvm build with the following executables:
# https://github.com/emscripten-core/emscripten/blob/#{version}/docs/packaging.md#dependencies
resource("llvm").stage do
projects = %w[
clang
lld
]
targets = %w[
host
WebAssembly
]
# Apple's libstdc++ is too old to build LLVM
ENV.libcxx if ENV.compiler == :clang
# See upstream configuration in `src/build.py` at
# https://chromium.googlesource.com/emscripten-releases/
args = %W[
-DLLVM_ENABLE_LIBXML2=OFF
-DLLVM_INCLUDE_EXAMPLES=OFF
-DLLVM_LINK_LLVM_DYLIB=OFF
-DLLVM_BUILD_LLVM_DYLIB=OFF
-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON
-DLLVM_ENABLE_BINDINGS=OFF
-DLLVM_TOOL_LTO_BUILD=OFF
-DLLVM_INSTALL_TOOLCHAIN_ONLY=ON
-DLLVM_TARGETS_TO_BUILD=#{targets.join(";")}
-DLLVM_ENABLE_PROJECTS=#{projects.join(";")}
-DLLVM_ENABLE_TERMINFO=#{!OS.linux?}
-DCLANG_ENABLE_ARCMT=OFF
-DCLANG_ENABLE_STATIC_ANALYZER=OFF
-DLLVM_INCLUDE_TESTS=OFF
-DLLVM_INSTALL_UTILS=OFF
-DLLVM_ENABLE_ZSTD=OFF
-DLLVM_ENABLE_Z3_SOLVER=OFF
]
args << "-DLLVM_ENABLE_LIBEDIT=OFF" if OS.linux?
system "cmake", "-S", "llvm", "-B", "build",
"-G", "Unix Makefiles",
*args, *std_cmake_args(install_prefix: libexec/"llvm")
system "cmake", "--build", "build"
system "cmake", "--build", "build", "--target", "install"
# Remove unneeded tools. Taken from upstream `src/build.py`.
unneeded = %w[
check cl cpp extef-mapping format func-mapping import-test offload-bundler refactor rename scan-deps
].map { |suffix| "clang-#{suffix}" }
unneeded += %w[lld-link ld.lld ld64.lld llvm-lib ld64.lld.darwinnew ld64.lld.darwinold]
(libexec/"llvm/bin").glob("{#{unneeded.join(",")}}").map(&:unlink)
(libexec/"llvm/lib").glob("libclang.{dylib,so.*}").map(&:unlink)
# Include needed tools omitted by `LLVM_INSTALL_TOOLCHAIN_ONLY`.
# Taken from upstream `src/build.py`.
extra_tools = %w[FileCheck llc llvm-as llvm-dis llvm-link llvm-mc
llvm-nm llvm-objdump llvm-readobj llvm-size opt
llvm-dwarfdump llvm-dwp]
(libexec/"llvm/bin").install extra_tools.map { |tool| "build/bin/#{tool}" }
%w[clang clang++].each do |compiler|
(libexec/"llvm/bin").install_symlink compiler => "wasm32-#{compiler}"
(libexec/"llvm/bin").install_symlink compiler => "wasm32-wasi-#{compiler}"
bin.install_symlink libexec/"llvm/bin/wasm32-#{compiler}"
bin.install_symlink libexec/"llvm/bin/wasm32-wasi-#{compiler}"
end
end
resource("binaryen").stage do
system "cmake", "-S", ".", "-B", "build", *std_cmake_args(install_prefix: libexec/"binaryen")
system "cmake", "--build", "build"
system "cmake", "--install", "build"
end
cd libexec do
system "npm", "install", *Language::Node.local_npm_install_args
rm_f "node_modules/ws/builderror.log" # Avoid references to Homebrew shims
# Delete native GraalVM image in incompatible platforms.
if OS.linux?
rm_rf "node_modules/google-closure-compiler-linux"
elsif Hardware::CPU.arm?
rm_rf "node_modules/google-closure-compiler-osx"
end
end
# Add JAVA_HOME to env_script on ARM64 macOS and Linux, so that google-closure-compiler
# can find OpenJDK
emscript_env = { PYTHON: Formula["python@3.11"].opt_bin/"python3.11" }
emscript_env.merge! Language::Java.overridable_java_home_env if OS.linux? || Hardware::CPU.arm?
emscripts.each do |emscript|
(bin/emscript).write_env_script libexec/emscript, emscript_env
end
end
def post_install
return if File.exist?("#{Dir.home}/.emscripten")
return if (libexec/".emscripten").exist?
system bin/"emcc", "--generate-config"
inreplace libexec/".emscripten" do |s|
s.change_make_var! "LLVM_ROOT", "'#{libexec}/llvm/bin'"
s.change_make_var! "BINARYEN_ROOT", "'#{libexec}/binaryen'"
s.change_make_var! "NODE_JS", "'#{Formula["node"].opt_bin}/node'"
s.change_make_var! "JAVA", "'#{Formula["openjdk"].opt_bin}/java'"
end
end
def caveats
return unless File.exist?("#{Dir.home}/.emscripten")
return if (libexec/".emscripten").exist?
<<~EOS
You have a ~/.emscripten configuration file, so the default configuration
file was not generated. To generate the default configuration:
rm ~/.emscripten
brew postinstall emscripten
EOS
end
test do
# We're targetting WASM, so we don't want to use the macOS SDK here.
ENV.remove_macosxsdk if OS.mac?
ENV["NODE_OPTIONS"] = "--no-experimental-fetch"
(testpath/"test.c").write <<~EOS
#include <stdio.h>
int main()
{
printf("Hello World!");
return 0;
}
EOS
system bin/"emcc", "test.c", "-o", "test.js", "-s", "NO_EXIT_RUNTIME=0"
assert_equal "Hello World!", shell_output("node test.js").chomp
end
end