Skip to content

Developer Notes

Alex Reustle edited this page Aug 21, 2024 · 16 revisions

Local ScienceTools Build Guide for Developers

Here I will document my steps to prepare a development evironment for the Sciencetools with an eye towards releasing a new version of the conda Fermitools package.

Check out the code and submodules

This meta-package of the user-facing Fermi ScienceTools employs git submodules to link to component packages. A git submodule is another git repository which is linked to by this repository. To also fetch these submodules on clone run:

git clone --recurse-submodules -j8 [email protected]:fermi-lat/ScienceTools.git

The --recurse-submodules -j8 flag tells git to also get the attached submodules and do so in parallel with 8 jobs. The repo url [email protected]:fermi-lat/ScienceTools.git uses an ssh protocol checkout which requires you as a developer to setup ssh keys with github in advance.

If you have cloned the top level repo already you can also grab all the submodule repositories with this command from within the ScienceTools directory: git submodule update --init .

Further Update the submodules

Submodules defined here are checked out as detached HEAD repositories under ScienceTools/src/, To reattach their heads efficiently, cd to ScienceTools and execute:

git submodule foreach 'git checkout master ||:'

To make the submodules all use the ssh git protocol:

git submodule foreach 'git remote -v set-url origin [email protected]:fermi-lat/$(basename $(pwd)).git ||:'

Pull and merge any upstream changes, this is probably more useful later on.

git submodule update --remote --merge --init

Note: It is possible to update the Sciencetools repo to use the git ssh protocol by default for submodules for all users. Please do not commit such a change; It will prevent future users who do not have git ssh keys set-up from cloning the submodules. This also holds for the CI pipeline.

Create a conda development environment & Install required dependencies

With a fully set up conda installation you can install the needed development dependencies for your operating system from the provided conda environment files in ScienceTools/environments/.

For example on linux:

conda env create -n fermi-dev -f environments/fermitools-develop-linux.yml
conda activate fermi-dev
Caveat for pyenv virtualenv. It's possible to run `conda` from within a pyenv virtual environment and still compile the fermitools. However the CMake path resolver has difficulty with multiple levels of virtual environment activation, such as having both a pyenv virtualenv and a conda env active simultaneously. This can be overcome by only using the pyenv virtualenv feature. In this case the environment creation and activation steps are as follows.
pyenv activate miniconda3-latest
conda env create -n fermi-dev -f environments/fermitools-develop-linux.yml
pyenv activate miniconda3-latest/envs/fermi-dev
conda activate fermi-dev
Build-only dependencies. System specific environment files also exist for just building the tools, but neither running nor testing them once built.
conda env create -n fermi-build -f environments/fermitools-build-linux-x86.yml
conda activate fermi-build
Generating dependency environment files. A new environment file candidate can be created from the command line. I say candidate because the YAML needs to me modified to actually be installable. You need to remove the 'prefix:' seciton and probably add a '-fermi' to the channel list.
conda env export -n fermi-build --from-history >  environments/fermitools-build.yml

CMake Build

The Fermitools has moved to using CMAKE for its build system. When paired with conda for dependency package management this provides robust and well-supported system for cross-compiling the tools on differing host and target systems.

The build process is split into two steps. Generation is similar to a configure step; The host system is polled for needed attributes, programs, and libraries and a Makefile system is created. The Build stage then executes the generated Makefile system to compile all the Fermitools targets.

Assuming your dependencies are installed and system compilers are all in the local $PATH you can generate a build system by executing the following command from within the ScienceTools directory:

cmake -S . \
-B RelWithDebInfo \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DCMAKE_INSTALL_PREFIX="</install/destination/path>" \
-DCMAKE_PREFIX_PATH="</dependency/search/path/>"

If your CMAKE_INSTALL_PREFIX is the location of your dependencies you can optionally exclude CMAKE_PREFIX_PATH. I recommend specifying -DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX.

Other useful CMake flags. TODO: Flesh out useful flags.
  • -DCMAKE_EXPORT_COMPILE_COMMANDS=ON: output a compile_commands.json file in the build tree. Useful for LSP programs like clangd.
  • --graphviz "<dir/prefix_string>": generate dot files plotting the dependency graphs of the CMake targets.
  • Sanitizer flags.
  • -G Ninja Use the Ninja build system, or some other build system generator.

This locates needed dependencies and prepares a release build with debug symbols in the 'RelWithDebInfo' directory. To compile the tools now run:

cmake --build RelWithDebInfo --parallel

You can install the tools in your CMAKE_INSTALL_PREFIX with

cmake --build RelWithDebInfo --parallel --target=install

Run and Test the tools locally

The Fermitools relies on a properly setup shell environment to populate needed path information for certain directories, like CALDB. In a conda build this is handled by the activate function, but as a developer working locally you can instead source the provided scripts/activate.sh. Simply source that file with the argument for your build directory like so:

source scripts/activate.sh RelWithDebInfo

This populates your environment with variables determined relative to your CMAKE_INSTALL_PREFIX, including PATH

You can now run your compiled code!

Develop in a submodule

To publish commits in a submodule and have it tracked by the Sciencetools you need to add the commit and push it in the submodule, then cd to ScienceTools and commit and push the new changes there too. This way, ScienceTools knows that it needs the submodule to track the new SHA, rather than the old one that it's already tracking. A git status in ScienceTools will let you know if your submodule changes are out-of-sync with what the ScienceTools is meant to track.

If you do not commit and push new tracking changes for ScienceTools submodules, later git clone attempts to pull the tools won't track your changes, even if they're on your target branch. To learn more about git submodule see here: https://git-scm.com/book/en/v2/Git-Tools-Submodules

Hurray!

Add a new submodule package

Let's add modelEditor as a submodule be sure to use the https protocol so members of the public and the CI pipeline can checkout the code too:

git submodule add https://github.com/fermi-lat/modelEditor.git src/modelEditor

Next let's add a new branch to modelEditor and push it back to github.

cd src/modelEditor
git checkout -b cmake-update

Next we edit modelEditor and push some commits.

<software engineering goes here>
<git commit stuff>

Make the local git protocoal ssh and push the new branch back to github.

git remote -v set-url origin [email protected]:fermi-lat/modelEditor.git
git push --set-upstream origin cmake-update

Let's set the ScienceTools repo to track the cmake-update branch of modelEditor.

cd ../../
git submodule set-branch --branch cmake-update src/modelEditor

Now let's push all the submodule changes and our top-level changes to ScienceTools

git commit -am "Add modelEditor"
git push  --recurse-submodules=on-demand

CMake Error: Could NOT find Python3 (missing: Python3_NumPy_INCLUDE_DIRS NumPy) (found suitable version "3.12.5", minimum required is "3.7")

This annoying issue crops up sometimes with conda not reading implicit values in the environment correctly.

One way to fix it is to manually set the numpy and python paths with the following cmake options.

-DPython3_NumPy_INCLUDE_DIRS=$(python -c "import numpy; print(numpy.get_include())") -DPython3_EXECUTABLE=$(which python)

so a full command might look like

$ cmake -S . \                                                                                                                                                                                                    
-B Release \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX \
-DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DPython3_NumPy_INCLUDE_DIRS=$(python -c "import numpy; print(numpy.get_include())") \
-DPython3_EXECUTABLE=$(which python) \
-G Ninja