{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<script async src=\"https://www.googletagmanager.com/gtag/js?id=UA-59152712-8\"></script>\n",
    "<script>\n",
    "  window.dataLayer = window.dataLayer || [];\n",
    "  function gtag(){dataLayer.push(arguments);}\n",
    "  gtag('js', new Date());\n",
    "\n",
    "  gtag('config', 'UA-59152712-8');\n",
    "</script>\n",
    "\n",
    "# BlackHoles@Home Tutorial: Compiling the `BOINC` libraries\n",
    "\n",
    "## Author: Leo Werneck\n",
    "\n",
    "## This tutorial notebook demonstrates how to compile the `BOINC` libraries on different platforms\n",
    "\n",
    "## Introduction:\n",
    "\n",
    "The [BlackHoles@Home](http://blackholesathome.net/) project allows users to volunteer CPU time so a large number of binary black holes simulations can be performed. The objective is to create a large catalog of [gravitational waveforms](https://en.wikipedia.org/wiki/Gravitational_wave), which can be used by observatories such as [LIGO](https://www.ligo.org), [VIRGO](https://www.virgo-gw.eu), and, in the future, [LISA](https://lisa.nasa.gov) in order to infer what was the source of a detected gravitational wave.\n",
    "\n",
    "BlackHoles@Home is destined to run on the [BOINC](https://boinc.berkeley.edu) infrastructure (alongside [Einstein@Home](https://einsteinathome.org/) and [many other great projects](https://boinc.berkeley.edu/projects.php)), enabling anyone with a computer to contribute to the construction of the largest numerical relativity gravitational wave catalogs ever produced.\n",
    "\n",
    "### Additional Reading Material:\n",
    "\n",
    "* [BOINC's Wiki page](https://boinc.berkeley.edu/trac/wiki)\n",
    "* [Debian's Wiki BOINC server guide](https://wiki.debian.org/BOINC/ServerGuide/Initialisation)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='toc'></a>\n",
    "\n",
    "# Table of Contents\n",
    "$$\\label{toc}$$\n",
    "\n",
    "This tutorial compiles the `BOINC` libraries. It will also install all needed dependencies. We test everything is okay by compiling and running a simple \"Hello World!\" application that makes use of the `BOINC` libraries. This tutorial is organized as follows:\n",
    "\n",
    "1. [Step 1](#loading_python_nrpy_modules): Loading necessary Python/NRPy+ modules\n",
    "1. [Step 2](#compilation_script): A simple script to compile the `BOINC` libraries\n",
    "1. [Step 3](#compilating_boinc_libraries): Compiling the `BOINC` libraries\n",
    "1. [Step 4](#compilation_test): Compiling a simple program\n",
    "1. [Step 5](#latex_pdf_output): Output this notebook to $\\LaTeX$-formatted PDF file"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='loading_python_nrpy_modules'></a>\n",
    "\n",
    "# Step 1: Loading needed Python/NRPy+ modules \\[Back to [top](#toc)\\]\n",
    "$$\\label{loading_python_nrpy_modules}$$\n",
    "\n",
    "We start by loading the necessary Python/NRPy+ moduels used by this tutorial notebook. We also set up the `BOINC` directory path (the default path is the current working directory)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Step 1: Import necessary modules - set directories.\n",
    "# Step 1.a: Import required Python modules\n",
    "import os,sys,shutil\n",
    "\n",
    "# Step 1.b: Add NRPy's root directory to the sys.path()\n",
    "sys.path.append(\"..\")\n",
    "\n",
    "# Step 1.c: Load NRPy+'s command line helper module\n",
    "import cmdline_helper as cmd\n",
    "\n",
    "# Step 1.d: Set directories needed by this tutorial notebook\n",
    "boinc_base_dir = \"/Users/werneck/bhah\" # os.getcwd()\n",
    "boinc_root_dir = os.path.join(boinc_base_dir,\"boinc\")\n",
    "boinc_lib_dir  = os.path.join(boinc_root_dir,\"lib\")\n",
    "boinc_api_dir  = os.path.join(boinc_root_dir,\"api\")\n",
    "boinc_zip_dir  = os.path.join(boinc_root_dir,\"zip\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='compilation_script'></a>\n",
    "\n",
    "# Step 2: A simple script to compile the `BOINC` libraries \\[Back to [top](#toc)\\]\n",
    "$$\\label{compilation_script}$$\n",
    "\n",
    "We will now write a simple `bash` script that will take care of downloading the `BOINC` source code and compiling the `BOINC` libraries.\n",
    "\n",
    "The script performs the following tasks:\n",
    "\n",
    "1. Install the necessary dependencies.\n",
    "1. Download the [`BOINC` source code](https://github.com/BOINC/boinc) (if necessary).\n",
    "1. Compile the `BOINC` core, api, and zip libraries.\n",
    "\n",
    "The `BOINC` library require the following packages:\n",
    "\n",
    "1. git\n",
    "1. make\n",
    "1. m4\n",
    "1. autoconf\n",
    "1. libtool\n",
    "1. A C++ compiler\n",
    "\n",
    "In Linux, we will be using the [GNU compilers](https://gcc.gnu.org) installed using [`aptitude`](http://manpages.ubuntu.com/manpages/bionic/man8/aptitude-curses.8.html) in Ubuntu and Debian. In Mac OS X, we will be using the [LLVM compilers](https://llvm.org) installed using [`homebrew`](https://brew.sh)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Step 2: Write down a script to compile the BOINC libraries\n",
    "# Step 2.a: Check the platform. This will adjust the way we install the dependencies.\n",
    "platform = sys.platform\n",
    "compiler_config = \"\"\n",
    "if platform == \"linux\":\n",
    "    # In Ubuntu/Debian, use aptitude to install the dependencies.\n",
    "    # To avoid issues where the user does not have sudo priviledges,\n",
    "    # such as in mybinder, we check if we find the command first.\n",
    "    dependency_list = [\"git\",\"make\",\"m4\",\"autoconf\",\"libtool\",\"g++\"]\n",
    "    dependencies    = \"\"\n",
    "    for dependency in dependency_list:\n",
    "        path = shutil.which(dependency)\n",
    "        if path is not None:\n",
    "            dependencies += dependency+\" \"\n",
    "        else:\n",
    "            print(dependency+\" found at \"+path)\n",
    "        \n",
    "        if dependencies != \"\":\n",
    "            install_dependencies = \"sudo apt install -y \"+dependencies\n",
    "elif platform == \"darwin\":\n",
    "    # In Mac OS X, use homebrew to install the dependencies.\n",
    "    install_dependencies = \"brew install git make m4 autoconf libtool llvm\"\n",
    "    # Because we use clang/clang++ instead of gcc/g++, we need\n",
    "    # to specify additional compilation flags to help the build\n",
    "    # script locate all the header files and libraries we will\n",
    "    # need. The paths below assume that the llvm compilers are\n",
    "    # in the default brew path /usr/local/opt. Adjust the\n",
    "    # variable 'path_to_llvm' if that's not the case.\n",
    "    path_to_llvm          = \"/usr/local/opt/llvm/\"\n",
    "    path_to_clang         = os.path.join(path_to_llvm,\"bin\",\"clang\")\n",
    "    path_to_clangpp       = os.path.join(path_to_llvm,\"bin\",\"clang++\")\n",
    "    path_to_clang_include = os.path.join(path_to_llvm,\"include\")\n",
    "    path_to_clang_library = os.path.join(path_to_llvm,\"lib\")\n",
    "    CPPFLAGS              = \"-I%s\"%(path_to_clang_include)\n",
    "    LDFLAGS               = \"-L%s\"%(path_to_clang_library)\n",
    "    compiler_config       = \"CC=%s CXX=%s OBJC=%s CPPFLAGS=\\\"%s\\\" LDFLAGS=\\\"%s\\\" \"%(path_to_clang,\n",
    "                                                                                    path_to_clangpp,\n",
    "                                                                                    path_to_clang,\n",
    "                                                                                    path_to_clang_include,\n",
    "                                                                                    path_to_clang_library)\n",
    "else:\n",
    "    print(\"Error: platform %s is currently not supported.\"%platform)\n",
    "    sys.exit(1)\n",
    "    \n",
    "with open(\"compile_BOINC_libraries.sh\",\"w\") as file:\n",
    "    file.write(r\"\"\"#!/bin/bash\n",
    "\n",
    "# Install all required software\n",
    "%s\n",
    "\n",
    "# Download BOINC repository (if necessary)\n",
    "boincdir=%s\n",
    "if [ -d \"$boincdir\" ]; then\n",
    "  echo \"BOINC directory found at $boincdir\"\n",
    "else\n",
    "  echo \"BOINC directory not found at $boincdir. Cloning from BOINC github repository...\"\n",
    "  git clone https://github.com/BOINC/boinc boinc\n",
    "fi\n",
    "\n",
    "# Now change directories to the BOINC directory\n",
    "cd \"$boincdir\"\n",
    "\n",
    "# Compile the core BOINC libraries\n",
    "./_autosetup -f\n",
    "%s./configure --disable-client --disable-manager --disable-server\n",
    "make\n",
    "\n",
    "# Now compile the boinc_zip library\n",
    "cd %s\n",
    "make\n",
    "\"\"\"%(install_dependencies,boinc_root_dir,compiler_config,boinc_zip_dir))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='compilating_boinc_libraries'></a>\n",
    "\n",
    "# Step 3: Compiling the `BOINC` libraries \\[Back to [top](#toc)\\]\n",
    "$$\\label{compilating_boinc_libraries}$$\n",
    "\n",
    "We now run the script above to compile the `BOINC` libraries. This tutorial notebook has been tested in the following systems:\n",
    "\n",
    "1. Ubuntu 20.04 LTS.\n",
    "1. Mac OS X Big Sur (version 11.2.3).\n",
    "\n",
    "If all the dependencies are already installed, then the script should finish running in just a few minutes. If not, then it will download the dependencies and the execution time may vary depending on your internet connection speed."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Now compiling BOINC libraries (can take a few minutes depending on your internet connection) ...\n",
      "Finished compiling BOINC libraries\n"
     ]
    }
   ],
   "source": [
    "# Step 3: Compile the BOINC libraries\n",
    "# FIXME: I don't think this is the best way of doing this.\n",
    "print(\"Now compiling BOINC libraries (can take a few minutes depending on your internet connection) ...\")\n",
    "status = os.system(\"source compile_BOINC_libraries.sh\")\n",
    "print(\"Finished compiling BOINC libraries\" if status == 0 \n",
    "      else \"Error: Something went wrong when compiling the BOINC libraries.\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='compilation_test'></a>\n",
    "\n",
    "# Step 4: Compiling a simple program \\[Back to [top](#toc)\\]\n",
    "$$\\label{compilation_test}$$\n",
    "\n",
    "We now compile a \"Hello World!\" program that makes use of the `BOINC` libraries. This will help us know if everything is okay with the compilation of the libraries."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Running hello_boinc program...\n",
      "Hello BOINC!\n",
      "All done!\n"
     ]
    }
   ],
   "source": [
    "# Step 3: Compiling a test program\n",
    "# Step 3.a: Write down a \"Hello World!\" program that uses the BOINC libraries\n",
    "outfile = \"hello_boinc.cpp\"\n",
    "with open(outfile,\"w\") as file:\n",
    "    file.write(r\"\"\"\n",
    "// Core C++ input/output header\n",
    "#include <iostream>\n",
    "\n",
    "// BOINC API header\n",
    "#include \"boinc_api.h\"\n",
    "\n",
    "// Main program\n",
    "int main(int argc, char** argv) {\n",
    "\n",
    "  // Initialize the boinc environment\n",
    "  int status = boinc_init();\n",
    "  \n",
    "  // Check everything is okay\n",
    "  if( status != 0 ) {\n",
    "    fprintf(stderr,\"Error: boinc_init() returned a non-zero value: %d. Terminating.\",status);\n",
    "    boinc_finish(status);\n",
    "  }\n",
    "  \n",
    "  // Print a message to the user\n",
    "  printf(\"Hello BOINC!\\n\");\n",
    "  \n",
    "  // Terminate the program with no errors\n",
    "  boinc_finish(0);\n",
    "  \n",
    "  // All done!\n",
    "}\n",
    "\"\"\")\n",
    "\n",
    "# Step 3.b: Add the base nrpytutorial directory to sys.path()\n",
    "import sys\n",
    "sys.path.append('..')\n",
    "\n",
    "# Step 3.c: Import the cmdline_helper NRPy+ module\n",
    "import cmdline_helper as cmd\n",
    "\n",
    "# Step 3.d: Prepare to compile\n",
    "# Step 3.d.i: Set the C++ compiler flags\n",
    "CXXFLAGS = \"-I%s -I%s \"%(boinc_api_dir,boinc_lib_dir)\n",
    "LDFLAGS  = \"-L%s -L%s -lboinc_api -lboinc \"%(boinc_api_dir,boinc_lib_dir)\n",
    "\n",
    "# Step 3.d.ii: Set the C++ compiler\n",
    "if platform == 'linux':\n",
    "    CXX_compiler = \"g++ \"\n",
    "elif platform == 'darwin':\n",
    "    CXX_compiler = \"clang++ \"\n",
    "    CXXFLAGS    += \"-I%s \"%(path_to_clang_include)\n",
    "    LDFLAGS     += \"-L%s \"%(path_to_clang_library)\n",
    "else:\n",
    "    print(\"Error: platform %s is currently not supported.\"%platform)\n",
    "    sys.exit(1)\n",
    "\n",
    "# Step 3.d.iii: Compile the program\n",
    "CXX_compile_string = CXX_compiler+CXXFLAGS+\"hello_boinc.cpp -o hello_boinc \"+LDFLAGS\n",
    "os.system(CXX_compile_string)\n",
    "\n",
    "# Step 3.d.iv: Run the program\n",
    "print(\"Running hello_boinc program...\")\n",
    "!./hello_boinc\n",
    "print(\"All done!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<a id='latex_pdf_output'></a>\n",
    "\n",
    "# Step 5: Output this notebook to $\\LaTeX$-formatted PDF file \\[Back to [top](#toc)\\]\n",
    "$$\\label{latex_pdf_output}$$\n",
    "\n",
    "The following code cell converts this Jupyter notebook into a proper, clickable $\\LaTeX$-formatted PDF file. After the cell is successfully run, the generated PDF may be found in the root NRPy+ tutorial directory, with filename\n",
    "[Tutorial-BlackHolesAtHome-Compiling_the_BOINC_libraries.pdf](Tutorial-BlackHolesAtHome-Compiling_the_BOINC_libraries.pdf) (Note that clicking on this link may not work; you may need to open the PDF file through another means.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2021-03-07T17:21:21.309338Z",
     "iopub.status.busy": "2021-03-07T17:21:21.308582Z",
     "iopub.status.idle": "2021-03-07T17:21:24.791779Z",
     "shell.execute_reply": "2021-03-07T17:21:24.792580Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Created Tutorial-BlackHolesAtHome-Compiling_the_BOINC_libraries.tex, and\n",
      "    compiled LaTeX file to PDF file Tutorial-BlackHolesAtHome-\n",
      "    Compiling_the_BOINC_libraries.pdf\n"
     ]
    }
   ],
   "source": [
    "!cp ../latex_nrpy_style.tplx .\n",
    "cmd.output_Jupyter_notebook_to_LaTeXed_PDF(\"Tutorial-BlackHolesAtHome-Compiling_the_BOINC_libraries\")\n",
    "!rm -f latex_nrpy_style.tplx"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}