Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 69 additions & 30 deletions Formula/emscripten.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
class Emscripten < Formula
desc "LLVM bytecode to JavaScript compiler"
homepage "https://emscripten.org/"
# TODO: Remove from versioned dependency conflict allowlist when `python`
# symlink is migrated to `python@3.10`.
url "https://github.com/emscripten-core/emscripten/archive/3.1.22.tar.gz"
sha256 "417be3a3ccec6a8ddaaecfd07e6962ceabc5a66cc26f3a7f885a7601832bc15c"
url "https://github.com/emscripten-core/emscripten/archive/3.1.23.tar.gz"
sha256 "f590deb9bb0a34efaa3c4a5621795c4673ba8f953ed363e80d1701aa835f812b"
license all_of: [
"Apache-2.0", # binaryen
"Apache-2.0" => { with: "LLVM-exception" }, # llvm
Expand Down Expand Up @@ -49,40 +47,41 @@ class Emscripten < Formula
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: "989489020e635d35870b22894a5d129c8c55d640"
revision: "58bedde3ac54f82657d5de092e7142ffb2ff735c"
end

# emscripten needs argument '-fignore-exceptions', which is only available in llvm >= 12
# 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: "8491d01cc385d08b8b4f5dd097239ea0009ddc63"
revision: "8b587113b746f31b63fd6473083df78cef30a72e"
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|
next false unless pn.file?
next false unless pn.executable?
next false if pn.extname == ".py" && pn.basename(".py").exist?

true
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
Expand All @@ -99,31 +98,56 @@ def install
# Apple's libstdc++ is too old to build LLVM
ENV.libcxx if ENV.compiler == :clang

# compiler-rt has some iOS simulator features that require i386 symbols
# I'm assuming the rest of clang needs support too for 32-bit compilation
# to work correctly, but if not, perhaps universal binaries could be
# limited to compiler-rt. llvm makes this somewhat easier because compiler-rt
# can almost be treated as an entirely different build from llvm.
ENV.permit_arch_flags

# See upstream configuration in `src/build.py` at
# https://chromium.googlesource.com/emscripten-releases/
args = %W[
-DLLVM_ENABLE_PROJECTS=#{projects.join(";")}
-DLLVM_TARGETS_TO_BUILD=#{targets.join(";")}
-DLLVM_LINK_LLVM_DYLIB=ON
-DLLVM_BUILD_LLVM_DYLIB=ON
-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
]

sdk = MacOS.sdk_path_if_needed
args << "-DDEFAULT_SYSROOT=#{sdk}" if sdk
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
Expand Down Expand Up @@ -154,18 +178,33 @@ def install
end

def post_install
return if File.exist?("#{Dir.home}/.emscripten")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

emcc --generate-config will error if this file exists, so let's skip the post_install and just warn in caveats if this file exists.

return if (libexec/".emscripten").exist?

system bin/"emcc", "--generate-config"
inreplace libexec/".emscripten" do |s|
s.gsub!(/^(LLVM_ROOT.*)/, "#\\1\nLLVM_ROOT = \"#{libexec}/llvm/bin\"\\2")
s.gsub!(/^(BINARYEN_ROOT.*)/, "#\\1\nBINARYEN_ROOT = \"#{libexec}/binaryen\"\\2")
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
# Fixes "Unsupported architecture" Xcode prepocessor error
ENV.delete "CPATH"
# 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"

Expand Down