15
15
from setuptools .command .build_py import build_py as _build_py
16
16
17
17
18
+ def get_env_boolean (name : str , default_value : bool = False ) -> bool :
19
+ svalue = os .getenv (name )
20
+ if svalue is None :
21
+ return default_value
22
+ svalue = svalue .upper ()
23
+ if svalue in ["1" , "ON" , "TRUE" ]:
24
+ return True
25
+ elif svalue in ["0" , "OFF" , "FALSE" ]:
26
+ return False
27
+ else :
28
+ print (f"WARNING: { name } env var cannot be interpreted as a boolean value" )
29
+ return default_value
30
+
31
+
32
+ def get_env_cmake_option (name : str , default_value : bool = False ) -> str :
33
+ svalue = os .getenv (name )
34
+ if not svalue :
35
+ svalue = "ON" if default_value else "OFF"
36
+ return f"-D{ name } ={ svalue } "
37
+
38
+
39
+ def add_env_cmake_setting (args , env_name : str , cmake_name = None ) -> str :
40
+ svalue = os .getenv (env_name )
41
+ if svalue is not None :
42
+ if not cmake_name :
43
+ cmake_name = env_name
44
+ args .append (f"-D{ cmake_name } ={ svalue } " )
45
+
46
+
18
47
# This file can be generated into the build directory to allow an arbitrary
19
48
# CMake built version of the project to be installed into a venv for development.
20
49
# This can be detected if the CPP_PREBUILT global contains the string
@@ -30,23 +59,33 @@ def is_cpp_prebuilt():
30
59
return CPP_PREBUILT == "TRUE"
31
60
32
61
62
+ DEV_MODE = False
63
+
33
64
if is_cpp_prebuilt ():
34
- print ("setup.py running in pre-built mode:" , file = sys . stderr )
65
+ print ("setup.py running in pre-built mode:" )
35
66
SOURCE_DIR = Path (CPP_PREBUILT_SOURCE_DIR )
36
67
BINARY_DIR = Path (CPP_PREBUILT_BINARY_DIR )
68
+ CMAKE_BUILD_DIR = BINARY_DIR
37
69
else :
38
- print ("setup.py running in cmake build mode:" , file = sys . stderr )
70
+ print ("setup.py running in cmake build mode:" )
39
71
# setup.py is in the source directory.
40
72
SOURCE_DIR = Path (SETUPPY_DIR )
41
- BINARY_DIR = Path (os .path .join (SETUPPY_DIR , "build" , "b" ))
73
+ BINARY_DIR = Path (os .path .join (SETUPPY_DIR , "build" ))
74
+ # TODO: Should build default and tracing version to different dirs.
75
+ CMAKE_BUILD_DIR = BINARY_DIR / "cmake"
76
+ DEV_MODE = get_env_boolean ("SHORTFIN_DEV_MODE" )
42
77
43
- print (f" SOURCE_DIR = { SOURCE_DIR } " , file = sys .stderr )
44
- print (f" BINARY_DIR = { BINARY_DIR } " , file = sys .stderr )
78
+ print (f" SOURCE_DIR = { SOURCE_DIR } " )
79
+ print (f" BINARY_DIR = { BINARY_DIR } " )
80
+
81
+ if DEV_MODE :
82
+ print (f" DEV MODE ENABLED: Building debug with clang/lld and other dev settings" )
45
83
46
84
# Due to a quirk of setuptools, that package_dir map must only contain
47
85
# paths relative to the directory containing setup.py. Why? No one knows.
48
86
REL_SOURCE_DIR = SOURCE_DIR .relative_to (SETUPPY_DIR , walk_up = True )
49
87
REL_BINARY_DIR = BINARY_DIR .relative_to (SETUPPY_DIR , walk_up = True )
88
+ REL_CMAKE_BUILD_DIR = CMAKE_BUILD_DIR .relative_to (SETUPPY_DIR , walk_up = True )
50
89
51
90
52
91
class CMakeExtension (Extension ):
@@ -98,7 +137,7 @@ def maybe_nuke_cmake_cache(cmake_build_dir):
98
137
# Mismatch or not found. Clean it.
99
138
cmake_cache_file = os .path .join (cmake_build_dir , "CMakeCache.txt" )
100
139
if os .path .exists (cmake_cache_file ):
101
- print ("Removing CMakeCache.txt because Python version changed" , file = sys . stderr )
140
+ print ("Removing CMakeCache.txt because Python version changed" )
102
141
os .remove (cmake_cache_file )
103
142
104
143
# And write.
@@ -115,67 +154,63 @@ def run(self):
115
154
if not is_cpp_prebuilt ():
116
155
117
156
# Build extension using cmake.
118
- print ("*****************************" , file = sys .stderr )
119
- print ("* Building libshortfin *" , file = sys .stderr )
120
- print ("*****************************" , file = sys .stderr )
121
-
122
- cfg = os .getenv ("SHORTFIN_CMAKE_BUILD_TYPE" , "Release" )
123
-
124
- CMAKE_BUILD_DIR = BINARY_DIR
157
+ print ("Building libshortfin" )
158
+ cfg = os .getenv (
159
+ "SHORTFIN_CMAKE_BUILD_TYPE" , "Debug" if DEV_MODE else "Release"
160
+ )
125
161
126
162
# Configure CMake.
127
- os .makedirs (BINARY_DIR , exist_ok = True )
128
- maybe_nuke_cmake_cache (CMAKE_BUILD_DIR )
129
- print (f"CMake build dir: { CMAKE_BUILD_DIR } " , file = sys .stderr )
163
+ os .makedirs (CMAKE_BUILD_DIR , exist_ok = True )
164
+ if not DEV_MODE :
165
+ maybe_nuke_cmake_cache (CMAKE_BUILD_DIR )
166
+ print (f"CMake build dir: { CMAKE_BUILD_DIR } " )
130
167
cmake_args = [
131
168
"-GNinja" ,
132
169
"--log-level=VERBOSE" ,
133
170
"-DSHORTFIN_BUNDLE_DEPS=ON" ,
134
171
f"-DCMAKE_BUILD_TYPE={ cfg } " ,
135
172
"-DSHORTFIN_BUILD_PYTHON_BINDINGS=ON" ,
136
- # TODO: This shouldn't be hardcoded... but shortfin doesn't
137
- # compile without it.
138
- "-DCMAKE_C_COMPILER=clang" ,
139
- "-DCMAKE_CXX_COMPILER=clang++" ,
173
+ f"-DPython3_EXECUTABLE={ sys .executable } " ,
140
174
]
141
175
176
+ if DEV_MODE :
177
+ cmake_args .extend (
178
+ [
179
+ "-DCMAKE_C_COMPILER=clang" ,
180
+ "-DCMAKE_CXX_COMPILER=clang++" ,
181
+ "-DCMAKE_LINKER_TYPE=LLD" ,
182
+ ]
183
+ )
184
+
185
+ add_env_cmake_setting (cmake_args , "SHORTFIN_IREE_SOURCE_DIR" )
186
+ add_env_cmake_setting (cmake_args , "SHORTFIN_ENABLE_ASAN" )
187
+
142
188
# Only do a from-scratch configure if not already configured.
143
189
cmake_cache_file = os .path .join (CMAKE_BUILD_DIR , "CMakeCache.txt" )
144
190
if not os .path .exists (cmake_cache_file ):
145
- print (f"Configuring with: { cmake_args } " , file = sys . stderr )
191
+ print (f"Configuring with: { cmake_args } " )
146
192
subprocess .check_call (
147
193
["cmake" , SOURCE_DIR ] + cmake_args , cwd = CMAKE_BUILD_DIR
148
194
)
149
195
else :
150
- print (f"Not re-configing (already configured)" , file = sys . stderr )
196
+ print (f"Not re-configing (already configured)" )
151
197
152
198
# Build.
153
199
subprocess .check_call (["cmake" , "--build" , "." ], cwd = CMAKE_BUILD_DIR )
154
- print ("Build complete." , file = sys . stderr )
200
+ print ("Build complete." )
155
201
156
- # We only take _shortfin_default from the build.
157
- target_dir = os .path .join (
158
- os .path .abspath (self .build_lib ), "_shortfin_default"
159
- )
160
- print (f"Building in target: { target_dir } " , file = sys .stderr )
161
- os .makedirs (target_dir , exist_ok = True )
162
- print ("Copying build to target." , file = sys .stderr )
163
- if os .path .exists (target_dir ):
164
- shutil .rmtree (target_dir )
165
- shutil .copytree (
166
- os .path .join (
167
- CMAKE_BUILD_DIR ,
168
- "bindings" ,
169
- "python" ,
170
- "_shortfin_default" ,
171
- ),
172
- target_dir ,
173
- symlinks = False ,
174
- )
202
+ # Optionally run CTests.
203
+ if get_env_boolean ("SHORTFIN_RUN_CTESTS" , False ):
204
+ print ("Running ctests..." )
205
+ subprocess .check_call (
206
+ ["ctest" , "--timeout" , "30" , "--output-on-failure" ],
207
+ cwd = CMAKE_BUILD_DIR ,
208
+ )
175
209
176
210
177
- PYTHON_SOURCE_DIR = REL_SOURCE_DIR / "bindings" / "python"
178
- PYTHON_BINARY_DIR = REL_BINARY_DIR / "bindings" / "python"
211
+ PYTHON_SOURCE_DIR = REL_SOURCE_DIR / "python"
212
+ # TODO: Need multiple binary dirs for different build variants.
213
+ PYTHON_DEFAULT_BINARY_DIR = REL_CMAKE_BUILD_DIR / "python"
179
214
180
215
# We need some directories to exist before setup.
181
216
def populate_built_package (abs_dir ):
@@ -190,7 +225,7 @@ def populate_built_package(abs_dir):
190
225
pass
191
226
192
227
193
- populate_built_package (os .path .join (PYTHON_BINARY_DIR / "_shortfin_default" ))
228
+ populate_built_package (os .path .join (PYTHON_DEFAULT_BINARY_DIR / "_shortfin_default" ))
194
229
195
230
setup (
196
231
name = "shortfin" ,
@@ -206,7 +241,7 @@ def populate_built_package(abs_dir):
206
241
zip_safe = False ,
207
242
package_dir = {
208
243
"_shortfin" : str (PYTHON_SOURCE_DIR / "_shortfin" ),
209
- "_shortfin_default" : str (PYTHON_BINARY_DIR / "_shortfin_default" ),
244
+ "_shortfin_default" : str (PYTHON_DEFAULT_BINARY_DIR / "_shortfin_default" ),
210
245
# TODO: Conditionally map additional native library variants.
211
246
"shortfin" : str (PYTHON_SOURCE_DIR / "shortfin" ),
212
247
},
0 commit comments