Skip to content

Commit a2493bd

Browse files
committed
reverting the workflow and CMake
1 parent 57ed0c8 commit a2493bd

File tree

5 files changed

+310
-59
lines changed

5 files changed

+310
-59
lines changed

.github/workflows/cleanliness.yml

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,9 @@ jobs:
3939
path: master
4040

4141
- name: Setup Ubuntu
42-
env:
43-
MFC_FFTW: "OFF" # disabling internal FFTW
44-
4542
run: |
4643
sudo apt update -y
47-
sudo apt install -y tar wget make cmake gcc g++ python3 python3-dev "openmpi-*" libopenmpi-dev libfftw3-dev
48-
49-
- name: Check CMake Version
50-
run: cmake --version
44+
sudo apt install -y tar wget make cmake gcc g++ python3 python3-dev "openmpi-*" libopenmpi-dev
5145
5246
- name: Build
5347
run: |

.github/workflows/test.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,23 +59,19 @@ jobs:
5959
6060
- name: Setup Ubuntu
6161
if: matrix.os == 'ubuntu' && matrix.intel == false
62-
env:
63-
MFC_FFTW: "OFF" # disabling internal FFTW
6462
run: |
6563
sudo apt update -y
6664
sudo apt install -y cmake gcc g++ python3 python3-dev hdf5-tools \
6765
libfftw3-dev libhdf5-dev openmpi-bin libopenmpi-dev
6866
6967
- name: Setup Ubuntu (Intel)
7068
if: matrix.os == 'ubuntu' && matrix.intel == true
71-
env:
72-
MFC_FFTW: "OFF" # disabling internal FFTW
7369
run: |
7470
wget https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB
7571
sudo apt-key add GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB
7672
sudo add-apt-repository "deb https://apt.repos.intel.com/oneapi all main"
7773
sudo apt-get update
78-
sudo apt-get install -y intel-oneapi-compiler-fortran intel-oneapi-mpi intel-oneapi-mpi-devel libfftw3-dev
74+
sudo apt-get install -y intel-oneapi-compiler-fortran intel-oneapi-mpi intel-oneapi-mpi-devel
7975
source /opt/intel/oneapi/setvars.sh
8076
printenv >> $GITHUB_ENV
8177

toolchain/build.py

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
import os, typing, hashlib, dataclasses
2+
3+
from .case import Case
4+
from .printer import cons
5+
from .common import MFCException, system, delete_directory, create_directory, \
6+
format_list_to_string
7+
from .state import ARG, CFG
8+
from .run import input
9+
10+
@dataclasses.dataclass
11+
class MFCTarget:
12+
@dataclasses.dataclass
13+
class Dependencies:
14+
all: typing.List
15+
cpu: typing.List
16+
gpu: typing.List
17+
18+
def compute(self) -> typing.Set:
19+
r = self.all[:]
20+
r += self.gpu[:] if ARG("gpu") else self.cpu[:]
21+
22+
return r
23+
24+
name: str # Name of the target
25+
flags: typing.List[str] # Extra flags to pass to CMakeMFCTarget
26+
isDependency: bool # Is it a dependency of an MFC target?
27+
isDefault: bool # Should it be built by default? (unspecified -t | --targets)
28+
isRequired: bool # Should it always be built? (no matter what -t | --targets is)
29+
requires: Dependencies # Build dependencies of the target
30+
runOrder: int # For MFC Targets: Order in which targets should logically run
31+
32+
def __hash__(self) -> int:
33+
return hash(self.name)
34+
35+
def get_slug(self, case: Case ) -> str:
36+
if self.isDependency:
37+
return self.name
38+
39+
m = hashlib.sha256()
40+
m.update(self.name.encode())
41+
m.update(CFG().make_slug().encode())
42+
m.update(case.get_fpp(self, False).encode())
43+
44+
if case.params.get('chemistry', 'F') == 'T':
45+
m.update(case.get_cantera_solution().name.encode())
46+
47+
return m.hexdigest()[:10]
48+
49+
# Get path to directory that will store the build files
50+
def get_staging_dirpath(self, case: Case ) -> str:
51+
return os.sep.join([os.getcwd(), "build", "staging", self.get_slug(case) ])
52+
53+
# Get the directory that contains the target's CMakeLists.txt
54+
def get_cmake_dirpath(self) -> str:
55+
# The CMakeLists.txt file is located:
56+
# * Regular: <root>/CMakelists.txt
57+
# * Dependency: <root>/toolchain/dependencies/CMakelists.txt
58+
return os.sep.join([
59+
os.getcwd(),
60+
os.sep.join(["toolchain", "dependencies"]) if self.isDependency else "",
61+
])
62+
63+
def get_install_dirpath(self, case: Case ) -> str:
64+
# The install directory is located <root>/build/install/<slug>
65+
return os.sep.join([os.getcwd(), "build", "install", self.get_slug(case)])
66+
67+
def get_install_binpath(self, case: Case ) -> str:
68+
# <root>/install/<slug>/bin/<target>
69+
return os.sep.join([self.get_install_dirpath(case), "bin", self.name])
70+
71+
def is_configured(self, case: Case ) -> bool:
72+
# We assume that if the CMakeCache.txt file exists, then the target is
73+
# configured. (this isn't perfect, but it's good enough for now)
74+
return os.path.isfile(
75+
os.sep.join([self.get_staging_dirpath(case), "CMakeCache.txt"])
76+
)
77+
78+
def get_configuration_txt(self, case: Case ) -> typing.Optional[dict]:
79+
if not self.is_configured(case):
80+
return None
81+
82+
configpath = os.path.join(self.get_staging_dirpath(case), "configuration.txt")
83+
if not os.path.exists(configpath):
84+
return None
85+
86+
with open(configpath) as f:
87+
return f.read()
88+
89+
def is_buildable(self) -> bool:
90+
if ARG("no_build"):
91+
return False
92+
93+
if self.isDependency and ARG(f"sys_{self.name}", False):
94+
return False
95+
96+
return True
97+
98+
def configure(self, case: Case):
99+
build_dirpath = self.get_staging_dirpath(case)
100+
cmake_dirpath = self.get_cmake_dirpath()
101+
install_dirpath = self.get_install_dirpath(case)
102+
103+
install_prefixes = ';'.join([
104+
t.get_install_dirpath(case) for t in self.requires.compute()
105+
])
106+
107+
flags: list = self.flags.copy() + [
108+
# Disable CMake warnings intended for developers (us).
109+
# See: https://cmake.org/cmake/help/latest/manual/cmake.1.html.
110+
"-Wno-dev",
111+
# Disable warnings about unused command line arguments. This is
112+
# useful for passing arguments to CMake that are not used by the
113+
# current target.
114+
"--no-warn-unused-cli",
115+
# Save a compile_commands.json file with the compile commands used to
116+
# build the configured targets. This is mostly useful for debugging.
117+
# See: https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html.
118+
"-DCMAKE_EXPORT_COMPILE_COMMANDS=ON",
119+
# Set build type (e.g Debug, Release, etc.).
120+
# See: https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html
121+
f"-DCMAKE_BUILD_TYPE={'Debug' if ARG('debug') else 'Release'}",
122+
# Used by FIND_PACKAGE (/FindXXX) to search for packages, with the
123+
# second highest level of priority, still letting users manually
124+
# specify <PackageName>_ROOT, which has precedence over CMAKE_PREFIX_PATH.
125+
# See: https://cmake.org/cmake/help/latest/command/find_package.html.
126+
f"-DCMAKE_PREFIX_PATH={install_prefixes}",
127+
# First directory that FIND_LIBRARY searches.
128+
# See: https://cmake.org/cmake/help/latest/command/find_library.html.
129+
f"-DCMAKE_FIND_ROOT_PATH={install_prefixes}",
130+
# First directory that FIND_PACKAGE searches.
131+
# See: https://cmake.org/cmake/help/latest/variable/CMAKE_FIND_PACKAGE_REDIRECTS_DIR.html.
132+
f"-DCMAKE_FIND_PACKAGE_REDIRECTS_DIR={install_prefixes}",
133+
# Location prefix to install bin/, lib/, include/, etc.
134+
# See: https://cmake.org/cmake/help/latest/command/install.html.
135+
f"-DCMAKE_INSTALL_PREFIX={install_dirpath}",
136+
f"-DMFC_SINGLE_PRECISION={'ON' if ARG('single') else 'OFF'}"
137+
]
138+
139+
if ARG("verbose"):
140+
flags.append('--debug-find')
141+
142+
if not self.isDependency:
143+
flags.append(f"-DMFC_MPI={ 'ON' if ARG('mpi') else 'OFF'}")
144+
flags.append(f"-DMFC_OpenACC={'ON' if ARG('gpu') else 'OFF'}")
145+
flags.append(f"-DMFC_GCov={ 'ON' if ARG('gcov') else 'OFF'}")
146+
flags.append(f"-DMFC_Unified={'ON' if ARG('unified') else 'OFF'}")
147+
148+
command = ["cmake"] + flags + ["-S", cmake_dirpath, "-B", build_dirpath]
149+
150+
delete_directory(build_dirpath)
151+
create_directory(build_dirpath)
152+
153+
case.generate_fpp(self)
154+
155+
if system(command).returncode != 0:
156+
raise MFCException(f"Failed to configure the [bold magenta]{self.name}[/bold magenta] target.")
157+
158+
cons.print(no_indent=True)
159+
160+
def build(self, case: input.MFCInputFile):
161+
case.generate_fpp(self)
162+
163+
command = ["cmake", "--build", self.get_staging_dirpath(case),
164+
"--target", self.name,
165+
"--parallel", ARG("jobs"),
166+
"--config", 'Debug' if ARG('debug') else 'Release']
167+
if ARG('verbose'):
168+
command.append("--verbose")
169+
170+
if system(command).returncode != 0:
171+
raise MFCException(f"Failed to build the [bold magenta]{self.name}[/bold magenta] target.")
172+
173+
cons.print(no_indent=True)
174+
175+
def install(self, case: input.MFCInputFile):
176+
command = ["cmake", "--install", self.get_staging_dirpath(case)]
177+
178+
if system(command).returncode != 0:
179+
raise MFCException(f"Failed to install the [bold magenta]{self.name}[/bold magenta] target.")
180+
181+
cons.print(no_indent=True)
182+
183+
# name flags isDep isDef isReq dependencies run order
184+
FFTW = MFCTarget('fftw', ['-DMFC_FFTW=ON'], True, False, False, MFCTarget.Dependencies([], [], []), -1)
185+
HDF5 = MFCTarget('hdf5', ['-DMFC_HDF5=ON'], True, False, False, MFCTarget.Dependencies([], [], []), -1)
186+
SILO = MFCTarget('silo', ['-DMFC_SILO=ON'], True, False, False, MFCTarget.Dependencies([HDF5], [], []), -1)
187+
HIPFORT = MFCTarget('hipfort', ['-DMFC_HIPFORT=ON'], True, False, False, MFCTarget.Dependencies([], [], []), -1)
188+
PRE_PROCESS = MFCTarget('pre_process', ['-DMFC_PRE_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([], [], []), 0)
189+
SIMULATION = MFCTarget('simulation', ['-DMFC_SIMULATION=ON'], False, True, False, MFCTarget.Dependencies([], [FFTW], [HIPFORT]), 1)
190+
POST_PROCESS = MFCTarget('post_process', ['-DMFC_POST_PROCESS=ON'], False, True, False, MFCTarget.Dependencies([FFTW, HDF5, SILO], [], []), 2)
191+
SYSCHECK = MFCTarget('syscheck', ['-DMFC_SYSCHECK=ON'], False, False, True, MFCTarget.Dependencies([], [], [HIPFORT]), -1)
192+
DOCUMENTATION = MFCTarget('documentation', ['-DMFC_DOCUMENTATION=ON'], False, False, False, MFCTarget.Dependencies([], [], []), -1)
193+
194+
TARGETS = { FFTW, HDF5, SILO, HIPFORT, PRE_PROCESS, SIMULATION, POST_PROCESS, SYSCHECK, DOCUMENTATION }
195+
196+
DEFAULT_TARGETS = { target for target in TARGETS if target.isDefault }
197+
REQUIRED_TARGETS = { target for target in TARGETS if target.isRequired }
198+
DEPENDENCY_TARGETS = { target for target in TARGETS if target.isDependency }
199+
200+
TARGET_MAP = { target.name: target for target in TARGETS }
201+
202+
def get_target(target: typing.Union[str, MFCTarget]) -> MFCTarget:
203+
if isinstance(target, MFCTarget):
204+
return target
205+
206+
if target in TARGET_MAP:
207+
return TARGET_MAP[target]
208+
209+
raise MFCException(f"Target '{target}' does not exist.")
210+
211+
212+
def get_targets(targets: typing.List[typing.Union[str, MFCTarget]]) -> typing.List[MFCTarget]:
213+
return [ get_target(t) for t in targets ]
214+
215+
216+
def __build_target(target: typing.Union[MFCTarget, str], case: input.MFCInputFile, history: typing.Set[str] = None):
217+
if history is None:
218+
history = set()
219+
220+
target = get_target(target)
221+
222+
if target.name in history or not target.is_buildable():
223+
return
224+
225+
history.add(target.name)
226+
227+
for dep in target.requires.compute():
228+
# If we have already built and installed this target,
229+
# do not do so again. This can be inferred by whether
230+
# the target requesting this dependency is already configured.
231+
if dep.isDependency and target.is_configured(case):
232+
continue
233+
234+
build([dep], case, history)
235+
236+
if not target.is_configured(case):
237+
target.configure(case)
238+
239+
target.build(case)
240+
target.install(case)
241+
242+
243+
def get_configured_targets(case: input.MFCInputFile) -> typing.List[MFCTarget]:
244+
return [ target for target in TARGETS if target.is_configured(case) ]
245+
246+
247+
def __generate_header(case: input.MFCInputFile, targets: typing.List):
248+
feature_flags = [
249+
'Build',
250+
format_list_to_string([ t.name for t in get_targets(targets) ], 'magenta')
251+
]
252+
if ARG("case_optimization"):
253+
feature_flags.append(f"Case Optimized: [magenta]{ARG('input')}[/magenta]")
254+
if case.params.get('chemistry', 'F') == 'T':
255+
feature_flags.append(f"Chemistry: [magenta]{case.get_cantera_solution().source}[/magenta]")
256+
257+
return f"[bold]{' | '.join(feature_flags or ['Generic'])}[/bold]"
258+
259+
260+
def build(targets = None, case: input.MFCInputFile = None, history: typing.Set[str] = None):
261+
if history is None:
262+
history = set()
263+
if isinstance(targets, (MFCTarget, str)):
264+
targets = [ targets ]
265+
if targets is None:
266+
targets = ARG("targets")
267+
268+
targets = get_targets(list(REQUIRED_TARGETS) + targets)
269+
case = case or input.load(ARG("input"), ARG("--"), {})
270+
case.validate_params()
271+
272+
if len(history) == 0:
273+
cons.print(__generate_header(case, targets))
274+
cons.print(no_indent=True)
275+
276+
for target in targets:
277+
__build_target(target, case, history)
278+
279+
if len(history) == 0:
280+
cons.print(no_indent=True)

0 commit comments

Comments
 (0)