Compiling on macOS using Anaconda

From GRASS-Wiki
Jump to navigation Jump to search

Using Anaconda to Create a Binary Mac App for GRASS

These instructions are for creating a self-contained Mac app, including all dependencies, with GRASS 7.9 and Anaconda. You can modify them if you don't need some of the steps. Some of the steps you won't need to repeat (e.g., creating an anaconda environment). You must first install the Anaconda package for Python. You will of course need to download a copy of the GRASS source code. You will also need to install Apple's XCode and the command-line tools to compile sofware. This workflow can be used with MacOS 10.14 and 10.15. It may work with other versions, but has not been tested.

You will need to work from the Mac terminal to compile GRASS of course. This tutorial assumes you are familiare with this command line interface.

This process also requires some patches and other supporting files. You can download these here: File:Grass macapp anaconda supporting files.zip

1. Create a Mac app shell (I have uploaded an example for GRASS 7.9 you can use as is, modified from shells created by Eric Hutton).

This can be modified serve for different versions of GRASS by altering the last line of the script that launches GRASS in the app shell. To do this, you will need to look inside the app by Ctrl-clicking on it and selecting '"show package contents"', which reveals the app as a folder with other folders and files inside.

For example, to make a shell for GRASS 7.8, copy the uploaded 'GRASS-7.9.app' and rename it to 'GRASS-7.8.app' Then show package contents for 'GRASS-7.8.app' and use a text editor to modify the last line of the file '../GRASS-7.8.app/Contents/MacOS/Grass.sh' to refer to GRASS 7.8

change: "$GRASS_PYTHON" "$GISBASE/bin/grass79" "-gui" "$@" to: "$GRASS_PYTHON" "$GISBASE/bin/grass78" "-gui" "$@"

Also, use a text editor to modify '../GRASS-7.9.app/Contents/Info.plist to change all references to 7.9 to 7.8.

2. Create an Anaconda environment for Python 3.6.10

This may not be strictly necessary, but I am doing it make sure that my builds use Python 3 instead of Python 2.7, which is still what ships with the Mac.

Anaconda defaults to Python 3.7.6, which will not compile GRASS properly. This makes it more complicated than needed, requiring some workarounds. I've tried this with multiple versions of Python 3 (anaconda has them all the way up to 3.8.2), but the highest version that seems to give consistently successful results is 3.6.10. This will probably change in the future so that you can use a higher version of Python.

Here is how to create an Anaconda environment for Python 3.6.10. You only need to do this once for each environment you want to use.

conda create -n anaconda_p36 python==3.6.10 anaconda

3. Activate the Anaconda Python 3.6.10 environment

conda activate anaconda_p36

You can activate any previously created Anaconda environment in this way

4. Copy the Mac app shell to /Applications

cp -r ../GRASS-7.9.app /Applications/GRASS-7.9.app  

5. Download the Python 3 miniconda installer and use it to install a minimal anaconda environment into the Mac app shell

Even if you are working in an Anaconda environment with Python 3.6.10, miniconda currently installs Python 3.7.6 into the Mac app shell. So you will need to downgrade this in step 7 below. This will probably change in the future so that miniconda installs a higher Python version.

curl https://repo.continuum.io/miniconda/Miniconda3-latest-MacOSX-x86_64.sh >  miniconda3.sh   
bash ./miniconda3.sh -b -f -p /Applications/GRASS-7.9.app/Contents/Resources    

6. Export the path to the GRASS Mac app shell so that subsequent operations use the environment of that shell

export PATH=/Applications/GRASS-7.9.app/Contents/Resources/bin:$PATH   

7. Install GRASS dependencies into Mac app shell, including Python 3.6.10

A requirements file can make all of these dependencies easier to install. One is included in the supporting files package. This will downgrade Python from v. 3.7.6 installed by miniconda to the more useable 3.6.10.

conda install --yes -p "/Applications/GRASS-7.9.app/Contents/Resources" --file=requirements_wxp4p36.txt -c conda-forge  

8. Create a symlink to Python so that pythonw can find it (may become unneeded in the future)

Grass launches Python through the pythonw script. But the script does not point to the correct location for Python (which is in fact in 2 different places). This seems to be a bug in Python < 3.7.4 or how anaconda installs it. It puts the python launch file in the wrong place. So a symlink is needed. Hopefully this will go away when we can use Python > 3.7.6, which seems to put python in the correct place, and the place that pythonw points to.

ln -sf /Applications/GRASS-7.8.app/Contents/Resources/python.app/pythonapp/Contents/* /Applications/GRASS-7.8.app/Contents/Resources/python.app/Contents

9. cd to the grass source folder and apply patches to configure and make files

Eric Hutton at CSDMS worked out these patches and I modified one needed to use Python 3. Maybe they can be worked into the source code so that they are no longer needed. I will upload the patches along with the app shell and requirements file.

patch -p0 < aclocal.m4.patch  
patch -p0 < configure.patch  
patch -p0 < install.make.patch  
patch -p0 < loader.py.patch  
patch -p0 < module.make.patch  
patch -p0 < platform.make.in.patch  
patch -p0 < rules.make.patch  
patch -p0 < shlib.make.patch

10. Configure, make, and install

An example configure script is included in the grass supporting files package. It flags all dependencies and sets the correct path so that they can be found. Important: this configure script has a hard path to the SDK (software developer kit) that comes with XCode. You can set different path if you've put you SDK in a different place.

bash ../configure.sh
make -j4  GDAL_DYNAMIC=
make -j4 install  

Assuming there were no errors, you should now be able to run and test the GRASS-7.9.app in your /Applications folder

11. Clean up

This removes unneeded anaconda package installer files and some other large files that are irrelevant for GRASS. It makes the app much smaller (though still large because of Python, wxPython, and other dependencies are packaged inside)

conda clean --all  -y  
rm -r /Applications/GRASS-7.9.app/Contents/Resources/mkspecs  
rm -r /Applications/GRASS-7.9.app/Contents/Resources/phrasebooks  
rm -r /Applications/GRASS-7.9.app/Contents/Resources/pkgs  
rm -r /Applications/GRASS-7.9.app/Contents/Resources/plugins  
rm -r /Applications/GRASS-7.9.app/Contents/Resources/qml  
rm -r /Applications/GRASS-7.9.app/Contents/Resources/resources  
rm -r /Applications/GRASS-7.9.app/Contents/Resources/translations 
rm /Applications/GRASS-7.9.app/Contents/Resources/lib/libQt*  

Optional: create a binary distribution package

12. Set up folder to make a binary package for distribution

Put a copy of the new GRASS app and a symlink to the /Applications folder into a new folder that will become the distribution package.

mkdir ../binary_pkg/grass-7.9
cd ../binary_pkg/grass-7.9
cp -R /Applications/GRASS-7.9.app ./
ln -s /Applications Applications  

13. Use Disk Utility to make a distribution package

  • Open disk Utility
  • From the make file/new image menu, select image from from folder
  • Point it to the folder with the app and symlink to /Applications and tell it to make an image 'GRASS-7.9.dmg'.