def _main_func(description=None): ############################################################################### args = parse_command_line(sys.argv, description) test_data = get_tests_from_xml( xml_machine=args.xml_machine, xml_category=args.xml_category, xml_compiler=args.xml_compiler, xml_testlist=args.xml_testlist, ) expect( test_data, "No tests found with the following options (where 'None' means no subsetting on that attribute):\n" "\tMachine = %s\n\tCategory = %s\n\tCompiler = %s\n\tTestlist = %s" % (args.xml_machine, args.xml_category, args.xml_compiler, args.xml_testlist), ) if args.count: count_test_data(test_data) elif args.list_type: list_test_data(test_data, args.list_type) else: print_test_data(test_data, args.show_options, args.define_testtypes)
def _get_compilers_for_suite(suite_name, machine_name): test_data = get_tests_from_xml(xml_machine=machine_name, xml_category=suite_name) if not test_data: raise RuntimeError('No tests found for suite {} on machine {}'.format( suite_name, machine_name)) compilers = sorted({one_test['compiler'] for one_test in test_data}) logger.info("Running with compilers: %s", compilers) return compilers
def parse_command_line(args, description): ############################################################################### parser = argparse.ArgumentParser( description=description, formatter_class=RawTextHelpFormatter ) model = CIME.utils.get_model() CIME.utils.setup_standard_logging_options(parser) config = get_cime_config() parser.add_argument( "--no-run", action="store_true", help="Do not run generated tests" ) parser.add_argument( "--no-build", action="store_true", help="Do not build generated tests, implies --no-run", ) parser.add_argument( "--no-setup", action="store_true", help="Do not setup generated tests, implies --no-build and --no-run", ) parser.add_argument( "-u", "--use-existing", action="store_true", help="Use pre-existing case directories they will pick up at the " "\nlatest PEND state or re-run the first failed state. Requires test-id", ) default = get_default_setting(config, "SAVE_TIMING", False, check_main=False) parser.add_argument( "--save-timing", action="store_true", default=default, help="Enable archiving of performance data.", ) parser.add_argument( "--no-batch", action="store_true", help="Do not submit jobs to batch system, run locally." "\nIf false, this will default to machine setting.", ) parser.add_argument( "--single-exe", action="store_true", default=False, help="Use a single build for all cases. This can " "\ndrastically improve test throughput but is currently use-at-your-own risk." "\nIt's up to the user to ensure that all cases are build-compatible." "\nE3SM tests belonging to a suite with share enabled will always share exes.", ) default = get_default_setting(config, "SINGLE_SUBMIT", False, check_main=False) parser.add_argument( "--single-submit", action="store_true", default=default, help="Use a single interactive allocation to run all the tests. This can " "\ndrastically reduce queue waiting but only makes sense on batch machines.", ) default = get_default_setting(config, "TEST_ROOT", None, check_main=False) parser.add_argument( "-r", "--test-root", default=default, help="Where test cases will be created. The default is output root" "\nas defined in the config_machines file", ) default = get_default_setting(config, "OUTPUT_ROOT", None, check_main=False) parser.add_argument( "--output-root", default=default, help="Where the case output is written." ) default = get_default_setting(config, "BASELINE_ROOT", None, check_main=False) parser.add_argument( "--baseline-root", default=default, help="Specifies a root directory for baseline datasets that will " "\nbe used for Bit-for-bit generate and/or compare testing.", ) default = get_default_setting(config, "CLEAN", False, check_main=False) parser.add_argument( "--clean", action="store_true", default=default, help="Specifies if tests should be cleaned after run. If set, all object" "\nexecutables and data files will be removed after the tests are run.", ) default = get_default_setting(config, "MACHINE", None, check_main=True) parser.add_argument( "-m", "--machine", default=default, help="The machine for creating and building tests. This machine must be defined" "\nin the config_machines.xml file for the given model. The default is to " "\nto match the name of the machine in the test name or the name of the " "\nmachine this script is run on to the NODENAME_REGEX field in " "\nconfig_machines.xml. WARNING: This option is highly unsafe and should " "\nonly be used if you are an expert.", ) default = get_default_setting(config, "MPILIB", None, check_main=True) parser.add_argument( "--mpilib", default=default, help="Specify the mpilib. To see list of supported MPI libraries for each machine, " "\ninvoke ./query_config. The default is the first listing .", ) if model in ["cesm", "ufs"]: parser.add_argument( "-c", "--compare", help="While testing, compare baselines against the given compare directory. ", ) parser.add_argument( "-g", "--generate", help="While testing, generate baselines in the given generate directory. " "\nNOTE: this can also be done after the fact with bless_test_results", ) parser.add_argument( "--xml-machine", help="Use this machine key in the lookup in testlist.xml. " "\nThe default is all if any --xml- argument is used.", ) parser.add_argument( "--xml-compiler", help="Use this compiler key in the lookup in testlist.xml. " "\nThe default is all if any --xml- argument is used.", ) parser.add_argument( "--xml-category", help="Use this category key in the lookup in testlist.xml. " "\nThe default is all if any --xml- argument is used.", ) parser.add_argument( "--xml-testlist", help="Use this testlist to lookup tests.The default is specified in config_files.xml", ) parser.add_argument( "--xml-driver", choices=("mct", "nuopc", "moab"), help="Override driver specified in tests and use this one.", ) parser.add_argument( "testargs", nargs="*", help="Tests to run. Testname form is TEST.GRID.COMPSET[.MACHINE_COMPILER]", ) else: parser.add_argument( "testargs", nargs="+", help="Tests or test suites to run." " Testname form is TEST.GRID.COMPSET[.MACHINE_COMPILER]", ) parser.add_argument( "-b", "--baseline-name", help="If comparing or generating baselines, use this directory under baseline root. " "\nDefault will be current branch name.", ) parser.add_argument( "-c", "--compare", action="store_true", help="While testing, compare baselines", ) parser.add_argument( "-g", "--generate", action="store_true", help="While testing, generate baselines. " "\nNOTE: this can also be done after the fact with bless_test_results", ) default = get_default_setting(config, "COMPILER", None, check_main=True) parser.add_argument( "--compiler", default=default, help="Compiler for building cime. Default will be the name in the " "\nTestname or the default defined for the machine.", ) parser.add_argument( "-n", "--namelists-only", action="store_true", help="Only perform namelist actions for tests", ) parser.add_argument( "-p", "--project", help="Specify a project id for the case (optional)." "\nUsed for accounting and directory permissions when on a batch system." "\nThe default is user or machine specified by PROJECT." "\nAccounting (only) may be overridden by user or machine specified CHARGE_ACCOUNT.", ) parser.add_argument( "-t", "--test-id", help="Specify an 'id' for the test. This is simply a string that is appended " "\nto the end of a test name. If no test-id is specified, a time stamp plus a " "\nrandom string will be used (ensuring a high probability of uniqueness). " "\nIf a test-id is specified, it is the user's responsibility to ensure that " "\neach run of create_test uses a unique test-id. WARNING: problems will occur " "\nif you use the same test-id twice on the same file system, even if the test " "\nlists are completely different.", ) default = get_default_setting(config, "PARALLEL_JOBS", None, check_main=False) parser.add_argument( "-j", "--parallel-jobs", type=int, default=default, help="Number of tasks create_test should perform simultaneously. The default " "\n is min(num_cores, num_tests).", ) default = get_default_setting(config, "PROC_POOL", None, check_main=False) parser.add_argument( "--proc-pool", type=int, default=default, help="The size of the processor pool that create_test can use. The default is " "\nMAX_MPITASKS_PER_NODE + 25 percent.", ) default = os.getenv("CIME_GLOBAL_WALLTIME") if default is None: default = get_default_setting(config, "WALLTIME", None, check_main=True) parser.add_argument( "--walltime", default=default, help="Set the wallclock limit for all tests in the suite. " "\nUse the variable CIME_GLOBAL_WALLTIME to set this for all tests.", ) default = get_default_setting(config, "JOB_QUEUE", None, check_main=True) parser.add_argument( "-q", "--queue", default=default, help="Force batch system to use a certain queue", ) parser.add_argument( "-f", "--testfile", help="A file containing an ascii list of tests to run" ) default = get_default_setting( config, "ALLOW_BASELINE_OVERWRITE", False, check_main=False ) parser.add_argument( "-o", "--allow-baseline-overwrite", action="store_true", default=default, help="If the --generate option is given, then an attempt to overwrite " "\nan existing baseline directory will raise an error. WARNING: Specifying this " "\noption will allow existing baseline directories to be silently overwritten.", ) default = get_default_setting(config, "WAIT", False, check_main=False) parser.add_argument( "--wait", action="store_true", default=default, help="On batch systems, wait for submitted jobs to complete", ) default = get_default_setting(config, "ALLOW_PNL", False, check_main=False) parser.add_argument( "--allow-pnl", action="store_true", default=default, help="Do not pass skip-pnl to case.submit", ) parser.add_argument( "--check-throughput", action="store_true", help="Fail if throughput check fails. Requires --wait on batch systems", ) parser.add_argument( "--check-memory", action="store_true", help="Fail if memory check fails. Requires --wait on batch systems", ) parser.add_argument( "--ignore-namelists", action="store_true", help="Do not fail if there namelist diffs", ) parser.add_argument( "--ignore-memleak", action="store_true", help="Do not fail if there's a memleak" ) default = get_default_setting(config, "FORCE_PROCS", None, check_main=False) parser.add_argument( "--force-procs", type=int, default=default, help="For all tests to run with this number of processors", ) default = get_default_setting(config, "FORCE_THREADS", None, check_main=False) parser.add_argument( "--force-threads", type=int, default=default, help="For all tests to run with this number of threads", ) default = get_default_setting(config, "INPUT_DIR", None, check_main=True) parser.add_argument( "-i", "--input-dir", default=default, help="Use a non-default location for input files", ) default = get_default_setting(config, "PESFILE", None, check_main=True) parser.add_argument( "--pesfile", default=default, help="Full pathname of an optional pes specification file. The file" "\ncan follow either the config_pes.xml or the env_mach_pes.xml format.", ) default = get_default_setting(config, "RETRY", 0, check_main=False) parser.add_argument( "--retry", type=int, default=default, help="Automatically retry failed tests. >0 implies --wait", ) parser.add_argument( "-N", "--non-local", action="store_true", help="Use when you've requested a machine that you aren't on. " "Will reduce errors for missing directories etc.", ) if config and config.has_option("main", "workflow"): workflow_default = config.get("main", "workflow") else: workflow_default = "default" parser.add_argument( "--workflow", default=workflow_default, help="A workflow from config_workflow.xml to apply to this case. ", ) parser.add_argument( "--chksum", action="store_true", help="Verifies input data checksums." ) srcroot_default = utils.get_src_root() parser.add_argument( "--srcroot", default=srcroot_default, help="Alternative pathname for source root directory. " f"The default is {srcroot_default}", ) CIME.utils.add_mail_type_args(parser) args = CIME.utils.parse_args_and_handle_standard_logging_options(args, parser) CIME.utils.resolve_mail_type_args(args) # generate and compare flags may not point to the same directory if model in ["cesm", "ufs"]: if args.generate is not None: expect( not (args.generate == args.compare), "Cannot generate and compare baselines at the same time", ) if args.xml_testlist is not None: expect( not ( args.xml_machine is None and args.xml_compiler is None and args.xml_category is None ), "If an xml-testlist is present at least one of --xml-machine, " "--xml-compiler, --xml-category must also be present", ) else: expect( not ( args.baseline_name is not None and (not args.compare and not args.generate) ), "Provided baseline name but did not specify compare or generate", ) expect( not (args.compare and args.generate), "Tried to compare and generate at same time", ) expect( not (args.namelists_only and not (args.generate or args.compare)), "Must provide either --compare or --generate with --namelists-only", ) if args.retry > 0: args.wait = True if args.parallel_jobs is not None: expect( args.parallel_jobs > 0, "Invalid value for parallel_jobs: %d" % args.parallel_jobs, ) if args.use_existing: expect(args.test_id is not None, "Must provide test-id of pre-existing cases") if args.no_setup: args.no_build = True if args.no_build: args.no_run = True # Namelist-only forces some other options: if args.namelists_only: expect(not args.no_setup, "Cannot compare namelists without setup") args.no_build = True args.no_run = True args.no_batch = True expect( not (args.non_local and not args.no_build), "Cannot build on non-local machine" ) if args.single_submit: expect( not args.no_run, "Doesn't make sense to request single-submit if no-run is on", ) args.no_build = True args.no_run = True args.no_batch = True if args.test_id is None: args.test_id = "%s_%s" % (CIME.utils.get_timestamp(), CIME.utils.id_generator()) else: expect( CIME.utils.check_name(args.test_id, additional_chars="."), "invalid test-id argument provided", ) if args.testfile is not None: with open(args.testfile, "r") as fd: args.testargs.extend( [ line.strip() for line in fd.read().splitlines() if line.strip() and not line.startswith("#") ] ) # Propagate `srcroot` to `GenericXML` to resolve $SRCROOT # See call to `Machines` below utils.GLOBAL["SRCROOT"] = args.srcroot # Compute list of fully-resolved test_names test_extra_data = {} if model in ["cesm", "ufs"]: machine_name = args.xml_machine if args.machine is None else args.machine # If it's still unclear what machine to use, look at test names if machine_name is None: for test in args.testargs: testsplit = CIME.utils.parse_test_name(test) if testsplit[4] is not None: if machine_name is None: machine_name = testsplit[4] else: expect( machine_name == testsplit[4], "ambiguity in machine, please use the --machine option", ) mach_obj = Machines(machine=machine_name) if args.testargs: args.compiler = ( mach_obj.get_default_compiler() if args.compiler is None else args.compiler ) test_names = get_tests.get_full_test_names( args.testargs, mach_obj.get_machine_name(), args.compiler ) else: expect( not ( args.xml_machine is None and args.xml_compiler is None and args.xml_category is None and args.xml_testlist is None ), "At least one of --xml-machine, --xml-testlist, " "--xml-compiler, --xml-category or a valid test name must be provided.", ) test_data = get_tests_from_xml( xml_machine=args.xml_machine, xml_category=args.xml_category, xml_compiler=args.xml_compiler, xml_testlist=args.xml_testlist, machine=machine_name, compiler=args.compiler, driver=args.xml_driver, ) test_names = [item["name"] for item in test_data] for test_datum in test_data: test_extra_data[test_datum["name"]] = test_datum logger.info("Testnames: %s" % test_names) else: if args.machine is None: args.machine = get_tests.infer_machine_name_from_tests(args.testargs) mach_obj = Machines(machine=args.machine) args.compiler = ( mach_obj.get_default_compiler() if args.compiler is None else args.compiler ) test_names = get_tests.get_full_test_names( args.testargs, mach_obj.get_machine_name(), args.compiler ) expect( mach_obj.is_valid_compiler(args.compiler), "Compiler %s not valid for machine %s" % (args.compiler, mach_obj.get_machine_name()), ) if not args.wait and mach_obj.has_batch_system() and not args.no_batch: expect( not args.check_throughput, "Makes no sense to use --check-throughput without --wait", ) expect( not args.check_memory, "Makes no sense to use --check-memory without --wait" ) # Normalize compare/generate between the models baseline_cmp_name = None baseline_gen_name = None if args.compare or args.generate: if model in ["cesm", "ufs"]: if args.compare is not None: baseline_cmp_name = args.compare if args.generate is not None: baseline_gen_name = args.generate else: baseline_name = ( args.baseline_name if args.baseline_name else CIME.utils.get_current_branch(repo=CIME.utils.get_cime_root()) ) expect( baseline_name is not None, "Could not determine baseline name from branch, please use -b option", ) if args.compare: baseline_cmp_name = baseline_name elif args.generate: baseline_gen_name = baseline_name if args.input_dir is not None: args.input_dir = os.path.abspath(args.input_dir) # sanity check for name in test_names: dot_count = name.count(".") expect(dot_count > 1 and dot_count <= 4, "Invalid test Name, '{}'".format(name)) # for e3sm, sort by walltime if model == "e3sm": if args.walltime is None: # Longest tests should run first test_names.sort(key=get_tests.key_test_time, reverse=True) else: test_names.sort() return ( test_names, test_extra_data, args.compiler, mach_obj.get_machine_name(), args.no_run, args.no_build, args.no_setup, args.no_batch, args.test_root, args.baseline_root, args.clean, baseline_cmp_name, baseline_gen_name, args.namelists_only, args.project, args.test_id, args.parallel_jobs, args.walltime, args.single_submit, args.proc_pool, args.use_existing, args.save_timing, args.queue, args.allow_baseline_overwrite, args.output_root, args.wait, args.force_procs, args.force_threads, args.mpilib, args.input_dir, args.pesfile, args.retry, args.mail_user, args.mail_type, args.check_throughput, args.check_memory, args.ignore_namelists, args.ignore_memleak, args.allow_pnl, args.non_local, args.single_exe, args.workflow, args.chksum, )