Source code for pymake.pymake_build_apps

"""Function to build MODFLOW-based models and other utility software based on
targets defined in the usgsprograms database (usgsprograms.txt). The
usgsprograms database can be queried using functions in the usgsprograms
module. An example of using :code:`pymake.build_apps()` to build MODFLOW 6 is:

.. code-block:: python

    import pymake
    pymake.build_apps(["mf6",])

which will download the latest MODFLOW 6 software release, compile the code,
and delete the downloaded files after successfully building the application.
Multiple applications can be built by adding additional targets to the tuple
in :code:`pymake.build_apps()`. For example, MODFLOW 6 and MODFLOW-2005 could
be built by specifying:

.. code-block:: python

    import pymake
    pymake.build_apps(["mf6","mf2005"]))

Applications are built in the order they are listed in the list. All valid
USGS applications are built if no list is passed to
:code:`pymake.build_apps()`.

"""

import os
import shutil
import sys
from datetime import datetime

from .pymake import Pymake
from .pymake_base import get_temporary_directories
from .utils.usgsprograms import usgs_program_data


[docs] def build_apps( targets=None, pymake_object=None, download_dir=None, appdir=None, verbose=None, release_precision=True, meson=False, mesondir=".", clean=True, ): """Build all of the current targets or a subset of targets. Parameters ---------- targets : str or list of str targets to build. If targets is None, all current targets will be build. Default is None pymake_object : Pymake() Pymake object created outside of build_apps download_dir : str download directory path appdir : str target path release_precision : bool boolean indicating if only the release precision version should be build. If release_precision is False, then the release precision version will be compiled along with a double precision version of the program for programs where the standard_switch and double_switch in usgsprograms.txt is True. default is True. meson : bool boolean indicating that the executable should be built using the meson build system. (default is False) mesondir : str Main meson.build file path clean : bool boolean determining of final download should be removed Returns ------- returncode : int integer value indicating successful completion (0) or failure (>0) """ start_time = datetime.now() # intercept all string (":") from make-program if isinstance(targets, str): if targets == ":": targets = None # set targets if targets is None: targets = usgs_program_data.get_keys(current=True) else: if isinstance(targets, str): targets = [targets] code_dict = {} if pymake_object is None: pmobj = Pymake() else: if isinstance(pymake_object, Pymake): pmobj = pymake_object else: msg = ( f"pymake_object ({type(pymake_object)}) " + f"is not of type {type(Pymake())}" ) raise TypeError(msg) # set base path for temporary directories if appdir is None: base_pth = "." else: base_pth = os.path.dirname(appdir) # set meson variable if a pymake object was not passed in if pymake_object is None: pmobj.meson = meson pmobj.mesondir = mesondir else: if pmobj.meson != meson: pmobj.meson = meson if pmobj.mesondir != mesondir: pmobj.mesondir = mesondir # clean any existing temporary directories temp_pths = get_temporary_directories(base_pth) for pth in temp_pths: if os.path.isdir(pth): shutil.rmtree(pth) # set object to clean after each build pmobj.makeclean = True # reset variables based on passed args if download_dir is not None: pmobj.download_dir = download_dir if appdir is not None: pmobj.appdir = appdir if verbose is not None: pmobj.verbose = verbose for idt, target in enumerate(targets): start_downcomp = datetime.now() code_dict[target] = usgs_program_data.get_target(target) # write system information if idt == 0: if pmobj.verbose: print( f"{target} will be built " + f'for the "{sys.platform}" operating system\n' ) # save initial compiler settings if idt == 0: fc0 = pmobj.fc cc0 = pmobj.cc fflags0 = pmobj.fflags cflags0 = pmobj.cflags syslibs0 = pmobj.syslibs # reset fortran, c/c++, and syslib flags else: pmobj.fflags = fflags0 pmobj.cflags = cflags0 pmobj.syslibs = syslibs0 # reset compilers if target in ("gridgen",): pmobj.fc = "none" if pmobj.cc in ("gcc",): pmobj.cc = "g++" elif pmobj.cc in ("clang",): pmobj.cc = "clang++" elif target in ("triangle",): pmobj.fc = "none" elif target in ("mf6", "libmf6"): pmobj.cc = "none" else: pmobj.fc = fc0 pmobj.cc = cc0 # set sharedobject if target in ("libmf6",): pmobj.sharedobject = True else: pmobj.sharedobject = False # reset srcdir2 - TODO make more robust if target not in ("libmf6",): pmobj.srcdir2 = None # reset extrafiles for instances with more than one target if idt > 0: pmobj.extrafiles = None # set double precision flag and whether the executable name # can be modified if target in ("swtv4",): update_target_name = False else: update_target_name = True # set download information if download_dir is None: download_dir = "temp" download_verify = True timeout = 30 # reset meson if target in ("prms",): pmobj.meson = True pmobj.inplace = True # set target and srcdir pmobj.target = target.replace("dev", "") pmobj.srcdir = os.path.join( download_dir, code_dict[target].dirname, code_dict[target].srcdir ) # determine if single, double, or both should be built precision = usgs_program_data.get_precision(target) # just build the first precision in precision list if # standard_precision is True if release_precision: precision = precision[0:1] for double in precision: # set double flag pmobj.double = double # determine if the target should be built build_target = pmobj.set_build_target_bool( target=pmobj.update_target( target, modify_target=update_target_name ) ) # setup download for target pmobj.download_setup( target, download_path=download_dir, verify=download_verify, timeout=timeout, ) # build the code if build_target: pmobj.build(modify_exe_name=update_target_name) # add target to build_targets list, if necessary else: pmobj.update_build_targets() # calculate download and compile time end_downcomp = datetime.now() elapsed = end_downcomp - start_downcomp if pmobj.verbose: print( "elapsed download and compile time (hh:mm:ss.ms): " + f"{elapsed}\n" ) end_time = datetime.now() elapsed = end_time - start_time if pmobj.verbose: print(f"elapsed time (hh:mm:ss.ms): {elapsed}\n") # compress targets if pmobj.returncode == 0: pmobj.compress_targets() # execute final Pymake object operations if clean: pmobj.finalize() return pmobj.returncode