source: flex_extract.git/Source/Python/install.py @ f61e1df

ctbtodev
Last change on this file since f61e1df was f61e1df, checked in by anphi <anne.philipp@…>, 4 years ago

merge of language editing branch into master

  • Property mode set to 100755
File size: 24.5 KB
Line 
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#*******************************************************************************
4# @Author: Leopold Haimberger (University of Vienna)
5#
6# @Date: November 2015
7#
8# @Change History:
9#
10#    February 2018 - Anne Philipp (University of Vienna):
11#        - applied PEP8 style guide
12#        - added documentation
13#        - moved install_args_and_control in here
14#        - splitted code in smaller functions
15#        - delete fortran build files in here instead of compile job script
16#        - changed static path names to variables from config file
17#        - splitted install function into several smaller pieces
18#        - use of tarfile package in python
19#    June 2020 - Anne Philipp
20#        - renamed "convert" functions to "fortran" functions
21#        - reconfigured mk_tarball to select *.template files instead
22#          of *.nl and *.temp
23#        - added check for makefile settings
24#
25# @License:
26#    (C) Copyright 2014-2020.
27#    Anne Philipp, Leopold Haimberger
28#
29#    SPDX-License-Identifier: CC-BY-4.0
30#
31#    This work is licensed under the Creative Commons Attribution 4.0
32#    International License. To view a copy of this license, visit
33#    http://creativecommons.org/licenses/by/4.0/ or send a letter to
34#    Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
35#
36# @Methods:
37#    main
38#    get_install_cmdline_args
39#    install_via_gateway
40#    check_install_conditions
41#    mk_tarball
42#    un_tarball
43#    mk_env_vars
44#    mk_compilejob
45#    mk_job_template
46#    del_fortran_build
47#    mk_fortran_build
48#
49#*******************************************************************************
50'''This script installs the flex_extract program.
51
52Depending on the selected installation environment (locally or on the
53ECMWF server ecgate or cca) the program extracts the command line
54arguments and the CONTROL file parameter and prepares the corresponding
55environment.
56The necessary files are collected in a tar ball and placed
57at the target location. There, is is untared, the environment variables are
58set, and the Fortran code is compiled.
59If the ECMWF environment is selected, a job script is prepared and submitted
60for the remaining configurations after putting the tar ball on the
61target ECMWF server.
62
63Type: install.py --help
64to get information about command line parameters.
65Read the documentation for usage instructions.
66'''
67
68# ------------------------------------------------------------------------------
69# MODULES
70# ------------------------------------------------------------------------------
71from __future__ import print_function
72
73import os
74import sys
75import subprocess
76import tarfile
77from argparse import ArgumentParser, ArgumentDefaultsHelpFormatter
78
79# software specific classes and modules from flex_extract
80import _config
81from Classes.ControlFile import ControlFile
82from Classes.UioFiles import UioFiles
83from Mods.tools import (make_dir, put_file_to_ecserver, submit_job_to_ecserver,
84                        silent_remove, execute_subprocess, none_or_str)
85
86# ------------------------------------------------------------------------------
87# FUNCTIONS
88# ------------------------------------------------------------------------------
89def main():
90    '''Controls the installation process. Calls the installation function
91    if target is specified.
92
93    Parameters
94    ----------
95
96    Return
97    ------
98    '''
99
100    args = get_install_cmdline_args()
101    c = ControlFile(args.controlfile)
102    c.assign_args_to_control(args)
103    check_install_conditions(c)
104
105    if c.install_target.lower() != 'local': # ecgate or cca
106        install_via_gateway(c)
107    else: # local
108        install_local(c)
109
110    return
111
112def get_install_cmdline_args():
113    '''Decomposes the command line arguments and assigns them to variables.
114    Apply default values for arguments not present.
115
116    Parameters
117    ----------
118
119    Return
120    ------
121    args : Namespace
122        Contains the commandline arguments from script/program call.
123    '''
124    parser = ArgumentParser(description='Install flex_extract software '
125                                        'locally or on ECMWF machines',
126                            formatter_class=ArgumentDefaultsHelpFormatter)
127
128    parser.add_argument('--target', dest='install_target',
129                        type=none_or_str, default=None,
130                        help="Valid targets: local | ecgate | cca , \
131                        the latter two are at ECMWF")
132    parser.add_argument("--makefile", dest="makefile",
133                        type=none_or_str, default=None,
134                        help='Name of makefile for compiling the '
135                        'Fortran program')
136    parser.add_argument("--ecuid", dest="ecuid",
137                        type=none_or_str, default=None,
138                        help='User id at ECMWF')
139    parser.add_argument("--ecgid", dest="ecgid",
140                        type=none_or_str, default=None,
141                        help='Group id at ECMWF')
142    parser.add_argument("--gateway", dest="gateway",
143                        type=none_or_str, default=None,
144                        help='Name of the local gateway server')
145    parser.add_argument("--destination", dest="destination",
146                        type=none_or_str, default=None,
147                        help='ecaccess association, e.g. '
148                        'myUser@genericSftp')
149
150    parser.add_argument("--installdir", dest="installdir",
151                        type=none_or_str, default=None,
152                        help='Root directory of the '
153                        'flex_extract installation')
154
155    # arguments for job submission to ECMWF, only needed by submit.py
156    parser.add_argument("--job_template", dest='job_template',
157                        type=none_or_str, default="job.template",
158                        help='Rudimentary template file to create a batch '
159                        'job template for submission to ECMWF servers')
160
161    parser.add_argument("--controlfile", dest="controlfile",
162                        type=none_or_str, default='CONTROL_EA5',
163                        help="A file that contains all CONTROL parameters.")
164
165    args = parser.parse_args()
166
167    return args
168
169
170def install_via_gateway(c):
171    '''Prepare data transfer to remote gateway and submit a job script which will
172    install everything on the remote gateway.
173
174    Parameters
175    ----------
176    c : ControlFile
177        Contains all the parameters of CONTROL file and
178        command line.
179
180    Return
181    ------
182
183    '''
184
185    tarball_name = _config.FLEXEXTRACT_DIRNAME + '.tar'
186    tar_file = os.path.join(_config.PATH_FLEXEXTRACT_DIR, tarball_name)
187
188    mk_compilejob(c.makefile, c.ecuid, c.ecgid, c.installdir)
189
190    mk_job_template(c.ecuid, c.ecgid, c.installdir)
191
192    mk_env_vars(c.ecuid, c.ecgid, c.gateway, c.destination)
193
194    mk_tarball(tar_file, c.install_target)
195
196    put_file_to_ecserver(_config.PATH_FLEXEXTRACT_DIR, tarball_name,
197                         c.install_target, c.ecuid, c.ecgid)
198
199    submit_job_to_ecserver(c.install_target,
200                           os.path.join(_config.PATH_REL_JOBSCRIPTS,
201                                        _config.FILE_INSTALL_COMPILEJOB))
202
203    silent_remove(tar_file)
204
205    print('Job compilation script has been submitted to ecgate for ' +
206          'installation in ' + c.installdir +
207          '/' + _config.FLEXEXTRACT_DIRNAME)
208    print('You should get an email with subject "flexcompile" within ' +
209          'the next few minutes!')
210
211    return
212
213def install_local(c):
214    '''Perform the actual installation on a local machine.
215
216    Parameters
217    ----------
218    c : ControlFile
219        Contains all the parameters of CONTROL file and
220        command line.
221
222    Return
223    ------
224
225    '''
226
227    tar_file = os.path.join(_config.PATH_FLEXEXTRACT_DIR,
228                            _config.FLEXEXTRACT_DIRNAME + '.tar')
229
230    if c.installdir == _config.PATH_FLEXEXTRACT_DIR:
231        print('WARNING: installdir has not been specified')
232        print('flex_extract will be installed in here by compiling the ' +
233              'Fortran source in ' + _config.PATH_FORTRAN_SRC)
234        os.chdir(_config.PATH_FORTRAN_SRC)
235    else: # creates the target working directory for flex_extract
236        c.installdir = os.path.expandvars(os.path.expanduser(
237            c.installdir))
238        if os.path.abspath(_config.PATH_FLEXEXTRACT_DIR) != \
239           os.path.abspath(c.installdir):
240            mk_tarball(tar_file, c.install_target)
241            make_dir(os.path.join(c.installdir,
242                                  _config.FLEXEXTRACT_DIRNAME))
243            os.chdir(os.path.join(c.installdir,
244                                  _config.FLEXEXTRACT_DIRNAME))
245            un_tarball(tar_file)
246            os.chdir(os.path.join(c.installdir,
247                                  _config.FLEXEXTRACT_DIRNAME,
248                                  _config.PATH_REL_FORTRAN_SRC))
249
250    # Create Fortran executable
251    print('Install ' +  _config.FLEXEXTRACT_DIRNAME + ' software at ' +
252          c.install_target + ' in directory ' +
253          os.path.abspath(c.installdir) + '\n')
254
255    del_fortran_build('.')
256    mk_fortran_build('.', c.makefile)
257
258    os.chdir(_config.PATH_FLEXEXTRACT_DIR)
259    if os.path.isfile(tar_file):
260        os.remove(tar_file)
261
262    return
263
264
265def check_install_conditions(c):
266    '''Checks necessary attributes and conditions
267    for the installation, e.g. whether they exist and contain values.
268    Otherwise set default values.
269
270    Parameters
271    ----------
272    c : ControlFile
273        Contains all the parameters of CONTROL file and
274        command line.
275
276
277    Return
278    ------
279
280    '''
281
282    if c.install_target and \
283       c.install_target not in _config.INSTALL_TARGETS:
284        print('ERROR: unknown or missing installation target ')
285        print('target: ', c.install_target)
286        print('please specify correct installation target ' +
287              str(_config.INSTALL_TARGETS))
288        print('use -h or --help for help')
289        sys.exit(1)
290
291    if c.install_target and c.install_target != 'local':
292        if not c.ecgid or not c.ecuid:
293            print('Please enter your ECMWF user id and group id '
294                  ' with command line options --ecuid --ecgid')
295            print('Try "' + sys.argv[0].split('/')[-1] + \
296                  ' -h" to print usage information')
297            print('Please consult ecaccess documentation or ECMWF user '
298                  'support for further details.\n')
299            sys.exit(1)
300        if not c.gateway or not c.destination:
301            print('WARNING: Parameters GATEWAY and DESTINATION were '
302                  'not properly set for working on ECMWF server. \n'
303                  'There will be no transfer of output files to the '
304                  'local gateway server possible!')
305        if not c.installdir:
306            c.installdir = '${HOME}'
307    else: # local
308        if not c.installdir:
309            c.installdir = _config.PATH_FLEXEXTRACT_DIR
310
311    if not c.makefile:
312        print('WARNING: no makefile was specified.')
313        if c.install_target == 'local':
314            c.makefile = 'makefile_local_gfortran'
315            print('WARNING: default makefile selected: makefile_local_gfortan')
316        elif c.install_target == 'ecgate':
317            c.makefile = 'makefile_ecgate'
318            print('WARNING: default makefile selected: makefile_ecgate')
319        elif c.install_target == 'cca' or \
320             c.install_target == 'ccb':
321            c.makefile = 'makefile_cray'
322            print('WARNING: default makefile selected: makefile_cray')
323        else:
324            pass
325       
326    return
327
328
329def mk_tarball(tarball_path, target):
330    '''Creates a tarball with all necessary files which need to be sent to the
331    installation directory.
332    It does not matter whether this is local or remote.
333    Collects all Python files, the Fortran source and makefiles,
334    the ECMWF_ENV file, the CONTROL files as well as the
335    template files.
336
337    Parameters
338    ----------
339    tarball_path : str
340        The complete path to the tar file which will contain all
341        relevant data for flex_extract.
342
343    target : str
344        The queue where the job is submitted to.
345
346    Return
347    ------
348
349    '''
350
351    print('Create tarball ...')
352
353    # change to FLEXEXTRACT directory so that the tar can contain
354    # relative pathes to the files and directories
355    ecd = _config.PATH_FLEXEXTRACT_DIR + '/'
356    os.chdir(ecd)
357
358    # get lists of the files to be added to the tar file
359    if target == 'local':
360        ecmwf_env_file = []
361        runfile = [os.path.relpath(x, ecd)
362                   for x in UioFiles(_config.PATH_REL_RUN_DIR,
363                                     'run_local.sh').files]
364    else:
365        ecmwf_env_file = [_config.PATH_REL_ECMWF_ENV]
366        runfile = [os.path.relpath(x, ecd)
367                   for x in UioFiles(_config.PATH_REL_RUN_DIR,
368                                     'run.sh').files]
369
370    pyfiles = [os.path.relpath(x, ecd)
371               for x in UioFiles(_config.PATH_REL_PYTHON_SRC, '*py').files]
372    pytestfiles = [os.path.relpath(x, ecd)
373                   for x in UioFiles(_config.PATH_REL_PYTHONTEST_SRC, '*py').files]
374    controlfiles = [os.path.relpath(x, ecd)
375                    for x in UioFiles(_config.PATH_REL_CONTROLFILES,
376                                      'CONTROL*').files]
377    testfiles = [os.path.relpath(x, ecd)
378                 for x in UioFiles(_config.PATH_REL_TEST, '*').files]
379    tempfiles = [os.path.relpath(x, ecd)
380                 for x in UioFiles(_config.PATH_REL_TEMPLATES, '*.template').files]
381    gribtable = [os.path.relpath(x, ecd)
382                 for x in UioFiles(_config.PATH_REL_TEMPLATES, '*grib*').files]
383    ffiles = [os.path.relpath(x, ecd)
384              for x in UioFiles(_config.PATH_REL_FORTRAN_SRC, '*.f90').files]
385    hfiles = [os.path.relpath(x, ecd)
386              for x in UioFiles(_config.PATH_REL_FORTRAN_SRC, '*.h').files]
387    makefiles = [os.path.relpath(x, ecd)
388                 for x in UioFiles(_config.PATH_REL_FORTRAN_SRC, 'makefile*').files]
389    jobdir = [_config.PATH_REL_JOBSCRIPTS]
390
391    # concatenate single lists to one for a better looping
392    filelist = pyfiles + pytestfiles + controlfiles + tempfiles + \
393               ffiles + gribtable + hfiles + makefiles + ecmwf_env_file + \
394               runfile + jobdir + testfiles +\
395               ['CODE_OF_CONDUCT.md', 'LICENSE.md', 'README.md']
396
397    # create installation tar-file
398    exclude_files = [".ksh", ".tar"]
399    try:
400        with tarfile.open(tarball_path, "w:gz") as tar_handle:
401            for filename in filelist:
402                tar_handle.add(filename, recursive=False,
403                               filter=lambda tarinfo: None
404                               if os.path.splitext(tarinfo.name)[1]
405                               in exclude_files
406                               else tarinfo)
407    except tarfile.TarError as e:
408        print('... ERROR: ' + str(e))
409
410        sys.exit('\n... error occured while trying to create the tar-file ' +
411                 str(tarball_path))
412
413    return
414
415
416def un_tarball(tarball_path):
417    '''Extracts the given tarball into current directory.
418
419    Parameters
420    ----------
421    tarball_path : str
422        The complete path to the tar file which will contain all
423        relevant data for flex_extract.
424
425    Return
426    ------
427
428    '''
429
430    print('Untar ...')
431
432    try:
433        with tarfile.open(tarball_path) as tar_handle:
434            tar_handle.extractall()
435    except tarfile.TarError as e:
436        sys.exit('\n... error occured while trying to read tar-file ' +
437                 str(tarball_path))
438    except OSError as e:
439        print('... ERROR CODE: ' + str(e.errno))
440        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
441
442        sys.exit('\n... error occured while trying to read tar-file ' +
443                 str(tarball_path))
444
445    return
446
447def mk_env_vars(ecuid, ecgid, gateway, destination):
448    '''Creates a file named ECMWF_ENV which contains the
449    necessary environmental variables at ECMWF servers.
450    It is based on the template ECMWF_ENV.template.
451
452    Parameters
453    ----------
454    ecuid : str
455        The user id on ECMWF server.
456
457    ecgid : str
458        The group id on ECMWF server.
459
460    gateway : str
461        The gateway server the user is using.
462
463    destination : str
464        The remote destination which is used to transfer files
465        from ECMWF server to local gateway server.
466
467    Return
468    ------
469
470    '''
471    from genshi.template.text import NewTextTemplate
472    from genshi.template import  TemplateLoader
473    from genshi.template.eval import UndefinedError
474
475    try:
476        loader = TemplateLoader(_config.PATH_TEMPLATES, auto_reload=False)
477        ecmwfvars_template = loader.load(_config.TEMPFILE_USER_ENVVARS,
478                                         cls=NewTextTemplate)
479
480        stream = ecmwfvars_template.generate(user_name=ecuid,
481                                             user_group=ecgid,
482                                             gateway_name=gateway,
483                                             destination_name=destination
484                                            )
485    except UndefinedError as e:
486        print('... ERROR ' + str(e))
487
488        sys.exit('\n... error occured while trying to generate template ' +
489                 _config.PATH_ECMWF_ENV)
490    except OSError as e:
491        print('... ERROR CODE: ' + str(e.errno))
492        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
493
494        sys.exit('\n... error occured while trying to generate template ' +
495                 _config.PATH_ECMWF_ENV)
496
497    try:
498        with open(_config.PATH_ECMWF_ENV, 'w') as f:
499            f.write(stream.render('text'))
500    except OSError as e:
501        print('... ERROR CODE: ' + str(e.errno))
502        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
503
504        sys.exit('\n... error occured while trying to write ' +
505                 _config.PATH_ECMWF_ENV)
506
507    return
508
509def mk_compilejob(makefile, ecuid, ecgid, fp_root):
510    '''Modifies the original job template file so that it is specified
511    for the user and the environment were it will be applied. Result
512    is stored in a new file "job.temp" in the python directory.
513
514    Parameters
515    ----------
516    makefile : str
517<<<<<<< HEAD
518        Name of the makefile which should be used to compile FORTRAN
519=======
520        Name of the makefile which should be used to compile the Fortran
521>>>>>>> origin/task/language-editing
522        program.
523
524    ecuid : str
525        The user id on ECMWF server.
526
527    ecgid : str
528        The group id on ECMWF server.
529
530    fp_root : str
531       Path to the root directory of FLEXPART environment or flex_extract
532       environment.
533
534    Return
535    ------
536
537    '''
538    from genshi.template.text import NewTextTemplate
539    from genshi.template import  TemplateLoader
540    from genshi.template.eval import  UndefinedError
541
542    if fp_root == '../':
543        fp_root = '$HOME'
544
545    try:
546        loader = TemplateLoader(_config.PATH_TEMPLATES, auto_reload=False)
547        compile_template = loader.load(_config.TEMPFILE_INSTALL_COMPILEJOB,
548                                       cls=NewTextTemplate)
549
550        stream = compile_template.generate(
551            usergroup=ecgid,
552            username=ecuid,
553            version_number=_config._VERSION_STR,
554            fp_root_scripts=fp_root,
555            makefile=makefile,
556            fortran_program=_config.FORTRAN_EXECUTABLE
557        )
558    except UndefinedError as e:
559        print('... ERROR ' + str(e))
560
561        sys.exit('\n... error occured while trying to generate template ' +
562                 _config.TEMPFILE_INSTALL_COMPILEJOB)
563    except OSError as e:
564        print('... ERROR CODE: ' + str(e.errno))
565        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
566
567        sys.exit('\n... error occured while trying to generate template ' +
568                 _config.TEMPFILE_INSTALL_COMPILEJOB)
569
570    try:
571        compilejob = os.path.join(_config.PATH_JOBSCRIPTS,
572                                  _config.FILE_INSTALL_COMPILEJOB)
573
574        with open(compilejob, 'w') as f:
575            f.write(stream.render('text'))
576    except OSError as e:
577        print('... ERROR CODE: ' + str(e.errno))
578        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
579
580        sys.exit('\n... error occured while trying to write ' +
581                 compilejob)
582
583    return
584
585def mk_job_template(ecuid, ecgid, fp_root):
586    '''Modifies the original job template file so that it is specified
587    for the user and the environment were it will be applied. Result
588    is stored in a new file.
589
590    Parameters
591    ----------
592    ecuid : str
593        The user id on ECMWF server.
594
595    ecgid : str
596        The group id on ECMWF server.
597
598    fp_root : str
599       Path to the root directory of FLEXPART environment or flex_extract
600       environment.
601
602    Return
603    ------
604
605    '''
606    from genshi.template.text import NewTextTemplate
607    from genshi.template import  TemplateLoader
608    from genshi.template.eval import  UndefinedError
609
610    fp_root_path_to_python = os.path.join(fp_root,
611                                          _config.FLEXEXTRACT_DIRNAME,
612                                          _config.PATH_REL_PYTHON_SRC)
613    if '$' in fp_root_path_to_python:
614        ind = fp_root_path_to_python.index('$')
615        fp_root_path_to_python = fp_root_path_to_python[0:ind] + '$' + \
616                                 fp_root_path_to_python[ind:]
617
618    try:
619        loader = TemplateLoader(_config.PATH_TEMPLATES, auto_reload=False)
620        compile_template = loader.load(_config.TEMPFILE_INSTALL_JOB,
621                                       cls=NewTextTemplate)
622
623        stream = compile_template.generate(
624            usergroup=ecgid,
625            username=ecuid,
626            version_number=_config._VERSION_STR,
627            fp_root_path=fp_root_path_to_python,
628        )
629    except UndefinedError as e:
630        print('... ERROR ' + str(e))
631
632        sys.exit('\n... error occured while trying to generate template ' +
633                 _config.TEMPFILE_INSTALL_JOB)
634    except OSError as e:
635        print('... ERROR CODE: ' + str(e.errno))
636        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
637
638        sys.exit('\n... error occured while trying to generate template ' +
639                 _config.TEMPFILE_INSTALL_JOB)
640
641
642    try:
643        tempjobfile = os.path.join(_config.PATH_TEMPLATES,
644                                   _config.TEMPFILE_JOB)
645
646        with open(tempjobfile, 'w') as f:
647            f.write(stream.render('text'))
648    except OSError as e:
649        print('... ERROR CODE: ' + str(e.errno))
650        print('... ERROR MESSAGE:\n \t ' + str(e.strerror))
651
652        sys.exit('\n... error occured while trying to write ' +
653                 tempjobfile)
654
655    return
656
657def del_fortran_build(src_path):
658    '''Clean up the Fortran source directory and remove all
659    build files (e.g. \*.o, \*.mod and FORTRAN EXECUTABLE)
660
661    Parameters
662    ----------
663    src_path : str
664        Path to the fortran source directory.
665
666    Return
667    ------
668
669    '''
670
671    modfiles = UioFiles(src_path, '*.mod')
672    objfiles = UioFiles(src_path, '*.o')
673    exefile = UioFiles(src_path, _config.FORTRAN_EXECUTABLE)
674
675    modfiles.delete_files()
676    objfiles.delete_files()
677    exefile.delete_files()
678
679    return
680
681def mk_fortran_build(src_path, makefile):
682    '''Compiles the Fortran code and generates the executable.
683
684    Parameters
685    ----------
686    src_path : str
687        Path to the fortran source directory.
688
689    makefile : str
690        The name of the makefile which should be used.
691
692    Return
693    ------
694
695    '''
696
697    try:
698        print('Using makefile: ' + makefile)
699        p = subprocess.Popen(['make', '-f',
700                              os.path.join(src_path, makefile)],
701                             stdin=subprocess.PIPE,
702                             stdout=subprocess.PIPE,
703                             stderr=subprocess.PIPE,
704                             bufsize=1)
705        pout, perr = p.communicate()
706        print(pout.decode())
707        if p.returncode != 0:
708            print(perr.decode())
709            print('Please edit ' + makefile +
710                  ' or try another makefile in the src directory.')
711<<<<<<< HEAD
712            print('Most likely ECCODES_INCLUDE_DIR, ECCODES_LIB '
713=======
714            print('Most likely GRIB_API_INCLUDE_DIR, GRIB_API_LIB '
715>>>>>>> origin/task/language-editing
716                  'and EMOSLIB must be adapted.')
717            print('Available makefiles:')
718            print(UioFiles(src_path, 'makefile*'))
719            sys.exit('Compilation failed!')
720    except ValueError as e:
721<<<<<<< HEAD
722        print('ERROR: makefile call failed:')
723=======
724        print('ERROR: make of Fortran code failed:')
725>>>>>>> origin/task/language-editing
726        print(e)
727    else:
728        execute_subprocess(['ls', '-l', 
729                            os.path.join(src_path, _config.FORTRAN_EXECUTABLE)],
730                           error_msg='FORTRAN EXECUTABLE COULD NOT BE FOUND!')
731
732    return
733
734
735if __name__ == "__main__":
736    main()
Note: See TracBrowser for help on using the repository browser.
hosted by ZAMG