-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsetup
executable file
·201 lines (169 loc) · 9.58 KB
/
setup
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2022 Kohei Noda [email protected]
import argparse
import logging
import os
import shutil
import subprocess
from pathlib import Path
def define_args() -> "argparse.Namespace":
parser = argparse.ArgumentParser(description="This script is used as a setup script for this project.")
parser.add_argument("--build", action="store_true", help="Build the project after the setup. [default: False]")
parser.add_argument("--build-type", type=str, help="Specify the build type. (release, debug can be specified.) [default: release]")
parser.add_argument("--fc", type=str, help="The path to the Fortran compiler.(If not specified, the default compiler will be used.)")
parser.add_argument("--flags", type=str, help="The compiler flags.(If not specified, the default flags will be used.)")
parser.add_argument(
"-j",
"--parallel",
type=int,
nargs="?", # 0 or 1 argument (https://docs.python.org/3/library/argparse.html#nargs)
const=-1,
default=1,
help="The maximum number of concurrent processes to use when building. This option is used only when the --build option is specified. If [Parallel] is omitted the native build tool's default number is used. [default: 1]",
)
parser.add_argument("--mpi", action="store_true", help="Enable MPI support. The path to the MPI compiler should be specified in the --fc option.[default compiler: mpiifort]")
parser.add_argument("--no-mkl", action="store_true", help="Disable MKL support. The path to the BLAS/LAPACK library should be specified in the --flags option.")
parser.add_argument("--omp", "--openmp", action="store_true", help="Enable OpenMP support.")
parser.add_argument("--prefix", type=str, metavar="INSTALL_DIR", help="Specify the installation directory. [default: /usr/local/bin]")
return parser.parse_args()
def preprocess(args: "argparse.Namespace", exec_path: Path, script_path: Path) -> str:
def check_cmake_version() -> None:
logging.info("Checking if CMake is installed and the version is 3.14 or later...")
try:
# Run the CMake command.
cmd = "cmake --version"
process = subprocess.run(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True)
# Get the stdout of the process.
stdout_decoded = process.stdout.decode("utf-8")
# Check if the CMake version is 3.14 or later.
cmake_version = stdout_decoded.split("\n")[0].split(" ")[2] # (e.g.) 3.20.0-rc3
cmake_major_version = int(cmake_version.split(".")[0]) # (e.g.) 3
cmake_minor_version = int(cmake_version.split(".")[1]) # (e.g.) 20
logging.info(f"Your CMake version is {cmake_version}")
if cmake_major_version < 3 or (cmake_major_version == 3 and cmake_minor_version < 14):
logging.error("Your CMake version is too old!")
logging.error("CMake version 3.14 or later is required.")
logging.error("Please install CMake 3.14 or later.")
raise ValueError("CMake version 3.14 or later is required.")
logging.info(f"CMake is set up correctly! {stdout_decoded}")
except subprocess.CalledProcessError as e:
# If the cmake --version command fails, print the error message and exit the script with an error code.
stderr_decoded = e.stderr.decode("utf-8") if isinstance(e.stderr, bytes) else e.stderr
logging.error(f"Failed command : {cmd}")
logging.error(f"Error message : {stderr_decoded}")
raise # raise the CalledProcessError exception to exit the script with an error code.
except FileNotFoundError as e:
# cmake command could not be found.
logging.error("CMake is not installed!")
logging.error(f"Error message : {e}")
raise # raise the FileNotFoundError exception to exit the script with an error code.
def clean_dir() -> None:
"""Remove build and bin directories before configuring the source code."""
logging.info("Cleaning the build directory...")
remove_dirs_name = ["build", "bin"]
for rmdir_name in remove_dirs_name:
rmdir = (script_path / rmdir_name).resolve()
logging.info(f"Checking if {rmdir} exists...")
if rmdir.exists():
shutil.rmtree(rmdir)
logging.info(f"Removed the directory {rmdir}")
def set_compiler() -> str:
if args.fc is not None:
# CMAKE_<LANG>_COMPILER: https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER.html#variable:CMAKE_%3CLANG%3E_COMPILER
return f" -DCMAKE_Fortran_COMPILER={args.fc}"
if args.mpi and args.fc is None:
return " -DCMAKE_Fortran_COMPILER=mpiifort"
return "" # If --fc option is not specified, the first Fortran compiler that CMake finds will be used.
def set_build_type() -> str:
# If --build-type option is not specified, set it to release.
if args.build_type is None:
logging.info('Build type is "release"')
return " -DCMAKE_BUILD_TYPE=release"
# --build-type option is specified.
build_type: str = args.build_type.lower()
if build_type == "release" or build_type == "debug":
logging.info(f'Build type is "{build_type}"')
return f" -DCMAKE_BUILD_TYPE={build_type}"
else:
msg = f'Invalid build type "{build_type}". The build type must be "release" or "debug".'
logging.error(msg)
raise ValueError(msg)
def set_install_dir() -> str:
if args.prefix is not None: # User specified the installation directory.
prefix = Path(args.prefix).expanduser().resolve()
logging.info(f"Installation directory is {prefix}")
return f" -DCMAKE_INSTALL_PREFIX={prefix}"
# User did not specify the installation directory.
# If --prefix option is not specified, the default installation directory defined in CMakeLists.txt will be used.
return ""
def set_cmake_flags() -> str:
cmake_flags = set_compiler() + set_build_type() + set_install_dir()
if args.flags is not None:
# Additional compiler flags specified by the user.
# CMAKE_<LANG>_FLAGS: https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_FLAGS.html#variable:CMAKE_%3CLANG%3E_FLAGS
cmake_flags += f" -DCMAKE_Fortran_FLAGS='{args.flags}'"
if args.no_mkl:
cmake_flags += " -DMKL=off"
if args.mpi:
cmake_flags += " -DMPI=ON"
if args.omp:
cmake_flags += " -DOPENMP=ON"
return cmake_flags
check_cmake_version()
clean_dir()
logging.info("Configuring cmake build flags...")
cmake_flags = set_cmake_flags()
return cmake_flags
def configure_source(cmake_flags: str, script_path: Path) -> None:
logging.info(f"CMake flags are {cmake_flags}")
build_dir = (script_path / "build").resolve()
build_dir.mkdir(exist_ok=True)
command = f"cmake -B {build_dir} {cmake_flags}"
with open("cmake_build_command.log", "w") as f:
f.write(command)
logging.info(f"BUILD COMMAND is {command}")
logging.info(f"Build command is saved in {script_path}/cmake_build_command.log")
subprocess.run(command.split(), check=True)
logging.info("Successfully configured the source code.")
def build(args: "argparse.Namespace") -> None:
num_of_process = args.parallel
if num_of_process == -1:
# const value of -j option, which means the native build tool's default number is used.
# (https://cmake.org/cmake/help/latest/manual/cmake.1.html#cmdoption-cmake-build-j)
cmd = "cmake --build build -j"
else:
cmd = f"cmake --build build -j {num_of_process}"
subprocess.run(cmd.split(), check=True)
logging.info("Successfully built the project.")
def main():
logging.info("Start dirac_caspt2 setup script.")
exec_path = Path.cwd().resolve()
script_path = Path(__file__).parent.resolve()
os.chdir(script_path)
args = define_args()
cmake_flags = preprocess(args, exec_path, script_path)
configure_source(cmake_flags, script_path)
if not args.build:
finish_message = "Finished the setup script.\n\n If you want to build the project, please run the following command: \n\tcmake --build build\n or\n\tmake -C build\n After building the project successfully, you should run tests!\n"
else:
build(args)
finish_message = "Finished the setup script.\n"
logging.info(finish_message)
mpi_option_message = " --mpi=[number of MPI processes]" if args.mpi else ""
omp_option_message = " --omp=[number of OpenMP threads per one process]" if args.omp else ""
enable_or_disable_mpi = "set" if args.mpi else "do not set"
logging.info(
f"[Note] About tests\n\
We recommend you to run tests to check if the build is successful.\n\
Before running tests, if you don't have pytest, please install it by running the following command:\n\n\
\tpip install pytest\n\n\
You {enable_or_disable_mpi} --mpi option. Therefore, you should run one of the following tests:\n\
- Run all tests:\n\
\tpytest --all{mpi_option_message}{omp_option_message}\n\
- Run tests excluding the tests long time to run:\n\
\tpytest{mpi_option_message}{omp_option_message}\n\
For more information, please read the {script_path}/README.md file."
)
if __name__ == "__main__":
logging.basicConfig(format="%(levelname)s : %(message)s", level=logging.INFO)
main()