@@ -12,6 +12,7 @@ import subprocess
12
12
import logging
13
13
14
14
from cam_config import ConfigCAM # CAM's configure structure
15
+ from atm_musica_config import MUSICA_CCPP_SCHEME_NAME
15
16
from atm_musica_config import MUSICA_REPO_URL , MUSICA_TAG
16
17
from atm_musica_config import CHEMISTRY_DATA_REPO_URL , CHEMISTRY_DATA_TAG
17
18
@@ -25,11 +26,12 @@ sys.path.append(os.path.join(__CIMEROOT, "CIME", "Tools"))
25
26
#pylint: disable=wrong-import-position
26
27
# CIME imports
27
28
from CIME .case import Case
28
- from CIME .utils import run_cmd , expect , symlink_force
29
+ from CIME .utils import run_cmd , expect
29
30
from CIME .utils import stop_buffering_output
30
31
from CIME .buildlib import parse_input
31
32
from CIME .build import get_standard_makefile_args
32
33
from CIME .Tools .standard_script_setup import check_minimum_python_version
34
+ from CIME .locked_files import lock_file , unlock_file
33
35
#pylint: enable=wrong-import-position
34
36
35
37
check_minimum_python_version (3 , 7 ) #CAM requires version 3.7 or greater
@@ -76,10 +78,9 @@ def _build_cam():
76
78
# Re-run source generator in case registry, CCPP suites, or
77
79
# generator scripts have been modified, and
78
80
# to extract required source code paths:
79
- config .generate_cam_src (gen_indent )
81
+ scheme_names = config .generate_cam_src (gen_indent )
80
82
81
83
dycore = config .get_value ('dyn' )
82
- phys_suites = config .get_value ('physics_suites' )
83
84
reg_dir = config .get_value ('reg_dir' )
84
85
init_dir = config .get_value ('init_dir' )
85
86
phys_dirs_str = config .get_value ('phys_dirs' )
@@ -172,17 +173,23 @@ def _build_cam():
172
173
if len (optional_mpas_features ) > 0 :
173
174
cmd += " OPTIONAL_MPAS_FEATURES=\" " + " " .join (optional_mpas_features ) + "\" "
174
175
175
- # If the physics suite is MUSICA, build the MUSICA library and get configuration
176
- if phys_suites == "musica" :
177
- _build_musica_library ( case )
176
+ # If MUSICA-CCPP scheme is used in a suite, download
177
+ # the MUSICA configuration and build the MUSICA library
178
+ if MUSICA_CCPP_SCHEME_NAME in scheme_names :
178
179
_download_musica_configuration (caseroot )
180
+ musica_install_path = _build_musica (caseroot )
181
+
182
+ cam_linked_libs = case .get_value ("CAM_LINKED_LIBS" )
183
+ musica_libs = "-lmusica-fortran -lmusica -lyaml-cpp"
184
+ if not musica_libs in cam_linked_libs :
185
+ _set_musica_lib_path (musica_install_path , caseroot )
179
186
180
187
cmd += ' USER_INCLDIR="' \
181
- f'-I{ os .path .join (bldroot , "musica" , "include" , "micm" )} ' \
182
- f'-I{ os .path .join (bldroot , "musica" , "include" , "musica" )} ' \
183
- f'-I{ os .path .join (bldroot , "musica" , "include" , "musica" , "micm" )} ' \
184
- f'-I{ os .path .join (bldroot , "musica" , "include" , "musica" , "tuvx" )} ' \
185
- f'-I{ os .path .join (bldroot , "musica" , "include" , "musica" , "fortran" )} ' \
188
+ f'-I{ os .path .join (musica_install_path , "include" , "micm" )} ' \
189
+ f'-I{ os .path .join (musica_install_path , "include" , "musica" )} ' \
190
+ f'-I{ os .path .join (musica_install_path , "include" , "musica" , "micm" )} ' \
191
+ f'-I{ os .path .join (musica_install_path , "include" , "musica" , "tuvx" )} ' \
192
+ f'-I{ os .path .join (musica_install_path , "include" , "musica" , "fortran" )} ' \
186
193
'"'
187
194
188
195
retcode , out , err = run_cmd (cmd )
@@ -242,74 +249,68 @@ def _copy2_as_needed(src: str, dst: str) -> None:
242
249
shutil .copy2 (src , dst )
243
250
244
251
###############################################################################
245
- def _build_musica_library ( case : Case ) -> None :
252
+ def _build_musica ( clone_dest : str ) -> str :
246
253
###############################################################################
247
254
"""
248
255
Builds and installs the MUSICA library.
249
256
250
257
Args:
251
- case (Case)
258
+ clone_dest: destination where the repository will be cloned
252
259
253
260
Raises:
254
261
Exception: If configuring the CMake MUSICA project fails or
255
262
the MUSICA library build fails, an exception is raised.
256
263
264
+ Returns:
265
+ musica_install_path: path to the MUSICA installation directory
257
266
"""
258
- install_dir_name = "install"
259
- build_type = "Release"
260
- caseroot = os .path .normpath (case .get_value ("CASEROOT" ))
261
- _clone_and_checkout (MUSICA_REPO_URL , MUSICA_TAG , caseroot )
267
+ _clone_and_checkout (MUSICA_REPO_URL , MUSICA_TAG , clone_dest )
262
268
263
- bld_path = os .path .join (caseroot , "musica" , "build" )
269
+ bld_path = os .path .join (clone_dest , "musica" , "build" )
264
270
if os .path .exists (bld_path ):
265
271
shutil .rmtree (bld_path )
266
272
os .makedirs (bld_path )
267
273
268
- # To install the target, the working directory must be the build directory.
274
+ # To install the target, the working directory must be the build (CMake binary) directory.
269
275
current_dir = os .getcwd ()
270
276
os .chdir (bld_path )
271
277
278
+ install_dir = "install"
272
279
command = [
273
280
"cmake" ,
274
- f"-DCMAKE_INSTALL_PREFIX= { install_dir_name } " ,
275
- f"-DCMAKE_BUILD_TYPE= { build_type } " ,
276
- "-DMUSICA_ENABLE_TESTS =OFF" ,
277
- "-DMUSICA_BUILD_FORTRAN_INTERFACE =ON" ,
281
+ f"-D CMAKE_INSTALL_PREFIX= { install_dir } " ,
282
+ "-D CMAKE_BUILD_TYPE=Release " ,
283
+ "-D MUSICA_ENABLE_TESTS =OFF" ,
284
+ "-D MUSICA_BUILD_FORTRAN_INTERFACE =ON" ,
278
285
".."
279
286
]
280
- result = subprocess .run (command , stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True , check = False )
281
- if result .returncode != 0 :
282
- raise Exception (f"Unable to configure the MUSICA CMake project. Error: { result .stderr } " )
287
+ try :
288
+ subprocess .run (command , stdout = subprocess .PIPE , stderr = subprocess .PIPE ,
289
+ text = True , check = False )
290
+ except subprocess .CalledProcessError as e :
291
+ raise subprocess .CalledProcessError (e .returncode , e .cmd , "The subprocess \
292
+ for cmake to configure the MUSICA CMake project failed." ) from e
293
+ except FileNotFoundError as e :
294
+ raise FileNotFoundError ("The 'cmake' command was not found." ) from e
295
+ except OSError as e :
296
+ raise OSError ("An error occurred while executing the 'cmake' command." ) from e
283
297
284
298
command = ["cmake" , "--build" , "." , "--target" , "install" ]
285
- result = subprocess .run (command , stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True , check = False )
286
- if result .returncode != 0 :
287
- raise Exception (f"Unable to build the MUSICA library. Error: { result .stderr } " )
288
-
289
- # Create symlinks pointing to the MUSICA libraries in the ATM build directory
290
- atm_bld_root = os .path .join (case .get_value ("EXEROOT" ), "atm" , "obj" )
291
- musica_lib_root = os .path .join (atm_bld_root , "musica" , "lib" )
292
- os .makedirs (musica_lib_root , exist_ok = True )
293
-
294
- installed_lib_path = os .path .join (bld_path , install_dir_name , "lib64" )
295
- root , _ , files = next (os .walk (installed_lib_path , topdown = True ))
296
- for file in files :
297
- symlink_force (os .path .join (root , file ), os .path .join (musica_lib_root , file ))
298
-
299
- # Create symlinks pointing to the MUSICA include files in the ATM build directory
300
- musica_include_root = os .path .join (atm_bld_root , "musica" , "include" )
301
- os .makedirs (musica_include_root , exist_ok = True )
302
-
303
- installed_include_path = os .path .join (bld_path , install_dir_name , "include" )
304
- for root , _ , files in os .walk (installed_include_path ):
305
- rel_path = os .path .relpath (root , installed_include_path )
306
- dest_dir = os .path .join (musica_include_root , rel_path )
307
-
308
- os .makedirs (dest_dir , exist_ok = True )
309
- for file in files :
310
- symlink_force (os .path .join (root , file ), os .path .join (dest_dir , file ))
299
+ try :
300
+ subprocess .run (command , stdout = subprocess .PIPE , stderr = subprocess .PIPE ,
301
+ text = True , check = False )
302
+ except subprocess .CalledProcessError as e :
303
+ raise subprocess .CalledProcessError (e .returncode , e .cmd , "The subprocess \
304
+ for cmake to build the MUSICA library failed." ) from e
305
+ except FileNotFoundError as e :
306
+ raise FileNotFoundError ("The 'cmake' command was not found." ) from e
307
+ except OSError as e :
308
+ raise OSError ("An error occurred while executing the 'cmake' command." ) from e
311
309
312
310
os .chdir (current_dir )
311
+ musica_install_path = os .path .join (bld_path , install_dir )
312
+
313
+ return musica_install_path
313
314
314
315
###############################################################################
315
316
def _download_musica_configuration (download_dest : str ) -> None :
@@ -319,7 +320,7 @@ def _download_musica_configuration(download_dest: str) -> None:
319
320
directory to match the name in the MUSICA-CCPP configuration.
320
321
321
322
Args:
322
- download_dest: destination where configuration will be downloaded.
323
+ download_dest: destination where configuration will be downloaded
323
324
324
325
Raises:
325
326
Exception: If the directory to be renamed is not found or
@@ -334,14 +335,14 @@ def _download_musica_configuration(download_dest: str) -> None:
334
335
renamed_dir = os .path .join (download_dest , "cam-sima-chemistry-data" , musica_config_dir_name )
335
336
try :
336
337
os .rename (original_dir , renamed_dir )
337
- except FileNotFoundError :
338
- raise FileNotFoundError (f"The directory '{ original_dir } ' was not found." )
339
- except FileExistsError :
340
- raise FileExistsError (f"The destination directory '{ renamed_dir } ' already exists." )
341
- except PermissionError :
342
- raise PermissionError (f"Permission denied to rename '{ original_dir } '." )
338
+ except FileNotFoundError as e :
339
+ raise FileNotFoundError (f"The directory '{ original_dir } ' was not found." ) from e
340
+ except FileExistsError as e :
341
+ raise FileExistsError (f"The destination directory '{ renamed_dir } ' already exists." ) from e
342
+ except PermissionError as e :
343
+ raise PermissionError (f"Permission denied to rename '{ original_dir } '." ) from e
343
344
except OSError as e :
344
- raise OSError (f "An error occurred while renaming: { e } " )
345
+ raise OSError ("An error occurred while renaming." ) from e
345
346
346
347
musica_config_path = os .path .join (download_dest , musica_config_dir_name )
347
348
if os .path .exists (musica_config_path ):
@@ -350,15 +351,58 @@ def _download_musica_configuration(download_dest: str) -> None:
350
351
shutil .move (renamed_dir , download_dest )
351
352
352
353
###############################################################################
353
- def _clone_and_checkout (repo_url : str , tag_name : str , clone_dest : str ):
354
+ def _set_musica_lib_path (musica_install_path : str , caseroot : str ) -> None :
355
+ ###############################################################################
356
+ """
357
+ Sets the MUSICA libraries path to CAM_LINKED_LIBS, allowing the libraries
358
+ to be linked during the CESM build process.
359
+
360
+ Args:
361
+ musica_install_path: path to the MUSICA installation directory
362
+ caseroot: CASEROOT where the xmlchange command is located
363
+
364
+ Raises:
365
+ Exception: If the subprocess for the xmlchange command fails,
366
+ an exception is raised with the error message.
367
+ """
368
+
369
+ current_dir = os .getcwd ()
370
+ os .chdir (caseroot )
371
+
372
+ unlock_file ("env_build.xml" , caseroot )
373
+
374
+ command = [
375
+ "./xmlchange" ,
376
+ "--append" ,
377
+ # The libraries must be on the same line because CIME flags an
378
+ # error for multi-character arguments preceded by a single dash
379
+ f"CAM_LINKED_LIBS=-L{ os .path .join (musica_install_path , 'lib64' )} -lmusica-fortran -lmusica -lyaml-cpp"
380
+ ]
381
+ try :
382
+ subprocess .run (command , stdout = subprocess .PIPE , stderr = subprocess .PIPE ,
383
+ text = True , check = False )
384
+ except subprocess .CalledProcessError as e :
385
+ raise subprocess .CalledProcessError (e .returncode , e .cmd , "The subprocess \
386
+ for xmlchange to set the MUSICA library path failed." ) from e
387
+ except FileNotFoundError as e :
388
+ raise FileNotFoundError ("The 'xmlchange' command was not found." ) from e
389
+ except OSError as e :
390
+ raise OSError ("An error occurred while executing the 'xmlchange' command." ) from e
391
+
392
+ os .chdir (current_dir )
393
+
394
+ lock_file ("env_build.xml" , caseroot )
395
+
396
+ ###############################################################################
397
+ def _clone_and_checkout (repo_url : str , tag_name : str , clone_dest : str ) -> None :
354
398
###############################################################################
355
399
"""
356
400
Clones a Git repository from the URL and checks out a specific branch.
357
401
358
402
Args:
359
- repo_url: The URL of the Git repository to clone
360
- tag_name: The tag name to check out
361
- clone_dest: destination where the repository will be cloned.
403
+ repo_url: URL of the Git repository to clone
404
+ tag_name: tag name to check out
405
+ clone_dest: destination where the repository will be cloned
362
406
363
407
Raises:
364
408
Exception: If the `git clone` or `git checkout` commands fail,
@@ -370,17 +414,27 @@ def _clone_and_checkout(repo_url: str, tag_name: str, clone_dest: str):
370
414
if os .path .exists (repo_path ):
371
415
shutil .rmtree (repo_path )
372
416
373
- result = subprocess .run (["git" , "clone" , repo_url , repo_path ],
374
- stderr = subprocess .PIPE , text = True , check = False )
375
- if result .returncode != 0 :
376
- raise Exception (f"Unable to clone the repository: { repo_url } . \
377
- Error: { result .stderr } " )
378
-
379
- result = subprocess .run (["git" , "-C" , repo_path , "checkout" , tag_name ],
380
- stderr = subprocess .PIPE , text = True , check = False )
381
- if result .returncode != 0 :
382
- raise Exception (f"Unable to checkout the branch: { tag_name } . \
383
- Error: { result .stderr } " )
417
+ try :
418
+ subprocess .run (["git" , "clone" , repo_url , repo_path ],
419
+ stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True , check = False )
420
+ except subprocess .CalledProcessError as e :
421
+ raise subprocess .CalledProcessError (e .returncode , e .cmd , f"The subprocess \
422
+ for git to clone the repository { repo_url } failed." ) from e
423
+ except FileNotFoundError as e :
424
+ raise FileNotFoundError ("The 'git' command was not found." ) from e
425
+ except OSError as e :
426
+ raise OSError ("An error occurred while executing the 'git' command." ) from e
427
+
428
+ try :
429
+ subprocess .run (["git" , "-C" , repo_path , "checkout" , tag_name ],
430
+ stdout = subprocess .PIPE , stderr = subprocess .PIPE , text = True , check = False )
431
+ except subprocess .CalledProcessError as e :
432
+ raise subprocess .CalledProcessError (e .returncode , e .cmd , f"The subprocess \
433
+ for git to checkout the branch { tag_name } failed." ) from e
434
+ except FileNotFoundError as e :
435
+ raise FileNotFoundError ("The 'git' command was not found." ) from e
436
+ except OSError as e :
437
+ raise OSError ("An error occurred while executing the 'git' command." ) from e
384
438
385
439
###############################################################################
386
440
0 commit comments