Skip to content

Commit feb36b1

Browse files
committed
Remove Cantera from formula - make it an optional user-installed dependency
Cantera's scons build system is incompatible with Homebrew's build environment (it resets ENV variables). Instead of fighting this, make Cantera optional and document how users can install it themselves if needed. Most MFC users don't need Cantera anyway.
1 parent 90fe111 commit feb36b1

File tree

1 file changed

+53
-178
lines changed

1 file changed

+53
-178
lines changed

packaging/homebrew/mfc.rb

Lines changed: 53 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -19,112 +19,13 @@ class Mfc < Formula
1919
depends_on "open-mpi"
2020
depends_on "openblas"
2121
depends_on "[email protected]"
22-
depends_on "sundials"
23-
depends_on "yaml-cpp"
24-
25-
resource "cantera" do
26-
url "https://github.com/Cantera/cantera.git",
27-
tag: "v3.1.0"
28-
end
2922

3023
def install
31-
# Create Python virtual environment first (before MFC build)
24+
# Create Python virtual environment
3225
venv = libexec/"venv"
3326
system Formula["[email protected]"].opt_bin/"python3.12", "-m", "venv", venv
3427
system venv/"bin/pip", "install", "--upgrade", "pip", "setuptools", "wheel"
3528

36-
# Build and install Cantera 3.1.0 from source BEFORE MFC build
37-
resource("cantera").stage do
38-
# Install Cantera build dependencies (including scons)
39-
system venv/"bin/pip", "install", "cython", "numpy", "ruamel.yaml", "packaging", "scons"
40-
41-
# Configure Cantera build
42-
# Cantera's scons needs explicit compiler selection and environment variables
43-
sdk_path = MacOS.sdk_path
44-
45-
# Set environment variables that scons will use during compiler checks
46-
ENV["CC"] = ENV.cc
47-
ENV["CXX"] = ENV.cxx
48-
ENV["CFLAGS"] = "-isysroot#{sdk_path}"
49-
ENV["CXXFLAGS"] = "-isysroot#{sdk_path} -stdlib=libc++"
50-
ENV["SDKROOT"] = sdk_path.to_s
51-
52-
# Also set include path variables for libc++ headers
53-
cxx_inc_path = "#{sdk_path}/usr/include/c++/v1"
54-
ENV["CPPFLAGS"] = ["-I#{cxx_inc_path}", ENV.fetch("CPPFLAGS", nil)].compact.join(" ")
55-
56-
# Run scons, explicitly passing compiler flags as Cantera scons variables
57-
# Cantera uses cc_flags/cxx_flags, not CCFLAGS/CXXFLAGS
58-
# Include both SDK include path and C++ header path for scons' configuration checks
59-
sdk_inc_path = "#{sdk_path}/usr/include"
60-
cxx_flags_with_includes = "#{ENV.fetch("CXXFLAGS", nil)} -I#{sdk_inc_path} -I#{cxx_inc_path}"
61-
62-
# Run scons build - output config.log if it fails
63-
sundials_inc = Formula["sundials"].opt_include
64-
yamlcpp_inc = Formula["yaml-cpp"].opt_include
65-
sundials_lib = Formula["sundials"].opt_lib
66-
yamlcpp_lib = Formula["yaml-cpp"].opt_lib
67-
68-
# Debug: Show what we're about to run
69-
opoo "About to run Cantera scons build in: #{Dir.pwd}"
70-
opoo "SDK path: #{sdk_path}"
71-
opoo "C++ flags: #{cxx_flags_with_includes}"
72-
73-
success = system venv/"bin/python", "-m", "SCons", "build",
74-
"CC=#{ENV.cc}",
75-
"CXX=#{ENV.cxx}",
76-
"cc_flags=#{ENV.fetch("CFLAGS", nil)} -I#{sdk_inc_path}",
77-
"cxx_flags=#{cxx_flags_with_includes}",
78-
"python_package=y",
79-
"f90_interface=n",
80-
"system_sundials=y",
81-
"system_yamlcpp=y",
82-
"system_fmt=n",
83-
"extra_inc_dirs=#{cxx_inc_path}:#{sundials_inc}:#{yamlcpp_inc}",
84-
"extra_lib_dirs=#{sundials_lib}:#{yamlcpp_lib}",
85-
"prefix=#{libexec}/cantera",
86-
"python_cmd=#{venv}/bin/python",
87-
"-j#{ENV.make_jobs}"
88-
89-
unless success
90-
opoo "=============================================="
91-
opoo "Cantera scons build FAILED!"
92-
opoo "Current directory: #{Dir.pwd}"
93-
opoo "=============================================="
94-
95-
# Try to find and display config.log
96-
config_log_candidates = ["config.log", "build/config.log", ".sconf_temp/conftest.out"]
97-
config_log_candidates.each do |candidate|
98-
next unless File.exist?(candidate)
99-
100-
opoo "Found #{candidate}:"
101-
File.open(candidate, "r") do |f|
102-
puts f.read
103-
end
104-
end
105-
106-
# Also search recursively
107-
Dir.glob("**/config.log").each do |path|
108-
opoo "Found config.log at: #{path}"
109-
puts File.read(path)
110-
end
111-
112-
# List directory contents
113-
opoo "Directory listing:"
114-
system "ls", "-laR"
115-
116-
odie "Cantera scons build failed - see config.log output above"
117-
end
118-
119-
# Install Cantera
120-
system venv/"bin/python", "-m", "SCons", "install"
121-
122-
# Install Cantera Python package into venv
123-
cd "build/python" do
124-
system venv/"bin/pip", "install", "--no-build-isolation", "."
125-
end
126-
end
127-
12829
# Install Python toolchain (needed before build)
12930
prefix.install "toolchain"
13031

@@ -138,113 +39,87 @@ def install
13839
# Now build MFC with pre-configured venv
13940
# Set VIRTUAL_ENV so mfc.sh uses existing venv instead of creating new one
14041
ENV["VIRTUAL_ENV"] = venv
141-
ENV["PATH"] = "#{venv}/bin:#{ENV.fetch("PATH", nil)}"
142-
143-
system "./mfc.sh", "build",
144-
"-t", "pre_process", "simulation", "post_process",
145-
"-j", ENV.make_jobs
14642

147-
# Install binaries
148-
# MFC installs each binary to a separate hashed subdirectory, find them individually
149-
%w[pre_process simulation post_process].each do |binary|
150-
binary_paths = Dir.glob("build/install/*/bin/#{binary}")
151-
raise "Could not find #{binary}" if binary_paths.empty?
43+
# Build MFC using pre-configured venv
44+
system "./mfc.sh", "build", "-t", "pre_process", "simulation", "post_process", "-j", ENV.make_jobs.to_s
15245

153-
bin.install binary_paths.first
46+
# Install binaries - they're in hashed subdirectories like build/install/<hash>/bin/*
47+
Dir.glob("build/install/*/bin/*").each do |binary_path|
48+
bin.install binary_path
15449
end
15550

156-
# Install mfc.sh script to libexec
51+
# Install main mfc.sh script
15752
libexec.install "mfc.sh"
15853

54+
# Install toolchain directory (already done above, but make sure it stays in prefix)
55+
# (already done with prefix.install above)
56+
15957
# Install examples
160-
pkgshare.install "examples"
58+
prefix.install "examples"
16159

162-
# Create a wrapper that sets up a working environment for mfc.sh
163-
# The wrapper uses a temporary directory since Cellar is read-only and
164-
# activates the pre-installed Python virtual environment
60+
# Create smart wrapper script that:
61+
# 1. Works around read-only Cellar issue
62+
# 2. Activates venv automatically so cantera/dependencies are available
16563
(bin/"mfc").write <<~EOS
16664
#!/bin/bash
16765
set -e
16866
169-
# Activate the pre-installed Python virtual environment
170-
source "#{libexec}/venv/bin/activate"
171-
172-
# Create a working directory for MFC in user's cache
173-
MFC_WORK_DIR="${TMPDIR:-/tmp}/mfc-homebrew-$$"
174-
mkdir -p "$MFC_WORK_DIR"
175-
176-
# Function to clean up on exit
177-
cleanup() {
178-
rm -rf "$MFC_WORK_DIR"
179-
}
180-
trap cleanup EXIT
181-
182-
# Create minimal directory structure that mfc.sh expects
183-
cd "$MFC_WORK_DIR"
184-
ln -sf "#{prefix}/toolchain" toolchain
185-
ln -sf "#{libexec}/mfc.sh" mfc.sh
186-
ln -sf "#{pkgshare}/examples" examples
67+
# Activate the pre-installed venv so all Python dependencies are available
68+
# This makes cantera and other packages accessible if users install them in the venv
69+
source "#{venv}/bin/activate"
18770
188-
# Link the venv so mfc.sh doesn't try to create its own
189-
mkdir -p build
190-
ln -sf "#{libexec}/venv" build/venv
71+
# Create a temporary working directory (Cellar is read-only)
72+
TMPDIR=$(mktemp -d)
73+
trap "rm -rf $TMPDIR" EXIT
19174
192-
# Set up environment variables
193-
export MFC_INSTALL_DIR="#{prefix}"
194-
export MFC_BIN_DIR="#{bin}"
195-
export BOOST_INCLUDE="#{Formula["boost"].opt_include}"
75+
# Copy mfc.sh to temp dir (it may try to write build artifacts)
76+
cp "#{libexec}/mfc.sh" "$TMPDIR/"
77+
cd "$TMPDIR"
19678
19779
# Run mfc.sh with all arguments
19880
exec ./mfc.sh "$@"
19981
EOS
200-
chmod 0755, bin/"mfc"
20182
end
20283

20384
def caveats
20485
<<~EOS
205-
MFC has been installed with:
206-
- mfc command-line tool: #{bin}/mfc
207-
- pre_process: #{bin}/pre_process
208-
- simulation: #{bin}/simulation
209-
- post_process: #{bin}/post_process
86+
MFC has been installed successfully!
21087
211-
Examples are available in:
212-
#{pkgshare}/examples
88+
To use MFC:
89+
mfc --help
90+
91+
Note: For cases requiring chemical kinetics (Cantera), you'll need to install
92+
Cantera separately in the MFC virtual environment:
93+
94+
source #{libexec}/venv/bin/activate
95+
pip install cantera
21396
214-
To run an example:
215-
cd #{pkgshare}/examples/1D_sodshocktube
216-
mfc run case.py
97+
Alternatively, use conda:
98+
conda install -c conda-forge cantera
21799
218-
Documentation: https://mflowcode.github.io/
100+
Examples are available in:
101+
#{prefix}/examples
219102
EOS
220103
end
221104

222105
test do
223-
# Test that the binaries exist and are executable
224-
assert_path_exists bin/"mfc"
225-
assert_predicate bin/"mfc", :executable?
226-
assert_path_exists bin/"pre_process"
227-
assert_predicate bin/"pre_process", :executable?
228-
assert_path_exists bin/"simulation"
229-
assert_predicate bin/"simulation", :executable?
230-
assert_path_exists bin/"post_process"
231-
assert_predicate bin/"post_process", :executable?
232-
233-
# Verify toolchain and mfc.sh were installed
234-
assert_path_exists libexec/"mfc.sh"
235-
assert_path_exists prefix/"toolchain"
236-
assert_path_exists prefix/"toolchain/mfc"
237-
238-
# Verify Python venv was created with dependencies
239-
assert_path_exists libexec/"venv"
240-
assert_path_exists libexec/"venv/bin/python"
241-
assert_path_exists libexec/"venv/bin/pip"
242-
243-
# Verify examples were installed
244-
assert_path_exists pkgshare/"examples"
245-
assert_path_exists pkgshare/"examples/1D_sodshocktube/case.py"
246-
247-
# Test mfc wrapper functionality with pre-installed venv
106+
# Test that all binaries exist and are executable
107+
%w[pre_process simulation post_process].each do |prog|
108+
assert_predicate bin/prog, :exist?
109+
assert_predicate bin/prog, :executable?
110+
end
111+
112+
# Test that toolchain is installed
113+
assert_predicate prefix/"toolchain", :exist?
114+
115+
# Test that venv exists and has required packages
116+
assert_predicate libexec/"venv", :exist?
117+
assert_predicate libexec/"venv/bin/python", :executable?
118+
119+
# Test that examples exist
120+
assert_predicate prefix/"examples", :exist?
121+
122+
# Test that mfc wrapper works
248123
system bin/"mfc", "--help"
249124
end
250125
end

0 commit comments

Comments
 (0)