99import pprint
1010import subprocess
1111
12- def _is_truthy_envvar (s : str ):
13- sl = s .lower ()
14- return sl in {"on" , "true" , "yes" , "1" }
12+ def _envvar_as_tristate (key : str , default = None ):
13+ value = os .getenv (key , default )
14+ if value is None :
15+ return None
16+ else :
17+ return value .lower () in {"on" , "true" , "yes" , "1" }
1518
1619def _run (s : str , extra_env_vars = {}):
1720 logging .info (f"running: { s } " )
1821 subprocess .run (s , check = True , env = {** extra_env_vars , ** os .environ })
1922
20- def _run_cmake_configure (source_dir , binary_dir , generator , architecture , cache_variables , extra_env_vars = {}):
21- all_cache_variables = ' ' .join ([f'-D{ k } ={ v } ' for k , v in cache_variables .items ()])
22- maybe_arch_flag = f'-A { architecture } ' if 'Visual Studio' in generator else ''
23- _run (f'cmake -S { source_dir } -B { binary_dir } -G "{ generator } " { maybe_arch_flag } { all_cache_variables } ' , extra_env_vars )
24-
25- def _run_cmake_build (binary_dir , config , concurrency , target = None ):
26- maybe_target_flag = f'--target { target } ' if target else ''
27- _run (f'cmake --build { binary_dir } --verbose --config { config } -j{ concurrency } { maybe_target_flag } ' )
28-
2923def _log_dir_contents (path : str ):
3024 logging .info (f"listing { path } " )
3125 for el in os .listdir (path ):
3226 logging .info (f"{ path } /{ el } " )
3327
34- class BuildConfiguration :
28+ def _default_generator ():
29+ return 'Visual Studio 17 2022'
3530
36- def __init__ (
37- self ,
38- * ,
39- base_build_type = "Release" ,
40- build_dir = os .curdir ,
41- build_concurrency = multiprocessing .cpu_count (),
42- build_target = "package" ,
43- build_docs = "OFF" ,
44- system_version = None ,
45- build_skip_osc = False ):
46-
47- self .base_build_type = os .getenv ("OSC_BASE_BUILD_TYPE" , base_build_type )
48- self .osc_deps_build_type = os .getenv ("OSC_DEPS_BUILD_TYPE" )
49- self .osc_build_type = os .getenv ("OSC_BUILD_TYPE" )
50- self .concurrency = int (os .getenv ("OSC_BUILD_CONCURRENCY" , build_concurrency ))
51- self .build_target = os .getenv ("OSC_BUILD_TARGET" , build_target )
52- self .build_docs = _is_truthy_envvar (os .getenv ("OSC_BUILD_DOCS" , build_docs ))
53- self .generator = 'Visual Studio 17 2022'
54- self .architecture = 'x64'
55- self .system_version = os .getenv ('OSC_SYSTEM_VERSION' , system_version )
56- self .build_dir = build_dir
57- self .skip_osc = build_skip_osc
31+ def _generator_requires_architecture_flag (generator ):
32+ return 'Visual Studio' in generator
33+
34+ def _is_multi_configuration_generator (generator ):
35+ return 'Visual Studio' in generator or 'Xcode' in generator
36+
37+ def _run_cmake_configure (source_dir , binary_dir , generator , architecture , cache_variables , extra_env_vars = {}):
38+ args = ['-S' , source_dir , '-B' , binary_dir ] # base arguments
39+
40+ # append generator (-G) argument (if specified)
41+ if generator :
42+ args += ['-G' , generator ]
43+ # append architecture (-A) argument (if necessary)
44+ if _generator_requires_architecture_flag (generator ):
45+ args += ['-A' , architecture if architecture else 'x64' ]
46+ # append cache variables (-DK=V)
47+ for k , v in cache_variables .items ():
48+ args += [f'-D{ k } ={ v } ' ]
49+
50+ _run (f'cmake { " " .join (args )} ' , extra_env_vars )
51+
52+ def _run_cmake_build (binary_dir , generator , build_type , concurrency , target = None , extra_env_vars = {}):
53+ args = ['--build' , binary_dir , '--verbose' , '-j' , str (concurrency )] # base arguments
54+
55+ # append --config argument (if necessary)
56+ if _is_multi_configuration_generator (generator ):
57+ args += ['--config' , build_type ]
58+ # append --target argument (if specified)
59+ if target :
60+ args += ['--target' , target ]
61+
62+ _run (f'cmake { " " .join (args )} ' , extra_env_vars )
63+
64+ def _run_ctest (test_dir , concurrency , excluded_tests = [], extra_env_vars = {}):
65+ args = ['--test-dir' , test_dir , '-j' , str (concurrency )] # base arguments
66+ if excluded_tests :
67+ args += ['-E' , '|' .join (excluded_tests )]
68+ _run (f'ctest { " " .join (args )} ' , extra_env_vars )
69+
70+ # Represents the top-level, potentially caller-controlled, build configuration.
71+ class BuildConfiguration :
72+ def __init__ (self ):
73+ self .base_build_type = os .getenv ("OSC_BASE_BUILD_TYPE" , "Release" )
74+ self .concurrency = int (os .getenv ("OSC_BUILD_CONCURRENCY" , multiprocessing .cpu_count ()))
75+ self .build_target = os .getenv ("OSC_BUILD_TARGET" , "package" )
76+ self .build_docs = _envvar_as_tristate ("OSC_BUILD_DOCS" )
77+ self .generator = _default_generator ()
78+ self .architecture = None
79+ self .system_version = os .getenv ('OSC_SYSTEM_VERSION' )
80+ self .build_dir = os .curdir
81+ self .codesign_enabled = None
82+ self .skip_osc = False
83+ self .exclude_tests_that_use_windowing_system = True
84+ self .headless_mode = True
85+
86+ # these can be `None`, meaning "fall back to `base_build_type` at runtime"
87+ self ._osc_deps_build_type = os .getenv ("OSC_DEPS_BUILD_TYPE" )
88+ self ._osc_build_type = os .getenv ("OSC_BUILD_TYPE" )
5889
5990 def __repr__ (self ):
6091 return pprint .pformat (vars (self ))
6192
6293 def get_dependencies_build_dir (self ):
63- return os .path .join (self .build_dir , f"third_party-build-{ self .osc_deps_build_type } " )
94+ return os .path .join (self .build_dir , f"third_party-build-{ self .get_osc_deps_build_type () } " )
6495
6596 def get_dependencies_install_dir (self ):
66- return os .path .join (self .build_dir , f"third_party-install-{ self .osc_deps_build_type } " )
97+ return os .path .join (self .build_dir , f"third_party-install-{ self .get_osc_deps_build_type () } " )
6798
6899 def get_osc_build_dir (self ):
69- # note: clangd usually expects that the build directory is located at `build/`
70- return os .path .join (self .build_dir , "build" )
100+ return os .path .join (self .build_dir , f"build/{ self .get_osc_build_type ()} " )
71101
72102 def get_osc_deps_build_type (self ):
73- return self .osc_deps_build_type or self .base_build_type
103+ return self ._osc_deps_build_type or self .base_build_type
74104
75105 def get_osc_build_type (self ):
76- return self .osc_build_type or self .base_build_type
106+ return self ._osc_build_type or self .base_build_type
107+
108+ def get_dependencies_cmake_cache_variables (self ):
109+ rv = {
110+ 'CMAKE_BUILD_TYPE' : self .get_osc_deps_build_type (),
111+ 'CMAKE_INSTALL_PREFIX' : self .get_dependencies_install_dir (),
112+ }
113+ if self .system_version :
114+ rv ['CMAKE_SYSTEM_VERSION' ] = self .system_version
77115
116+ return rv
117+
118+ def get_osc_cmake_cache_variables (self ):
119+ # calculate cache variables
120+ rv = {
121+ 'CMAKE_BUILD_TYPE' : self .get_osc_build_type (),
122+ 'CMAKE_PREFIX_PATH' : os .path .abspath (self .get_dependencies_install_dir ()),
123+ }
124+ if self .build_docs is not None :
125+ rv ['OSC_BUILD_DOCS' ] = 'ON' if self .build_docs else 'OFF'
126+ if self .codesign_enabled is not None :
127+ rv ['OSC_CODESIGN_ENABLED' ] = 'ON' if self .codesign_enabled else 'OFF'
128+ if self .system_version :
129+ rv ['CMAKE_SYSTEM_VERSION' ] = self .system_version
130+
131+ return rv
132+
133+ def get_osc_ctest_extra_environment_variables (self ):
134+ rv = {}
135+ if self .headless_mode :
136+ rv ['OSC_INTERNAL_HIDE_WINDOW' ] = '1'
137+ return rv
138+
139+ def get_excluded_tests (self ):
140+ if not self .exclude_tests_that_use_windowing_system :
141+ return [] # no tests excluded
142+ else :
143+ return [
144+ 'Graphics' ,
145+ 'MeshDepthWritingMaterialFixture' ,
146+ 'MeshNormalVectorsMaterialFixture' ,
147+ 'ShaderTest' ,
148+ 'MaterialTest' ,
149+ 'RegisteredDemoTabsTest' ,
150+ 'RegisteredOpenSimCreatorTabs' ,
151+ 'AddComponentPopup' ,
152+ 'LoadingTab' ,
153+ ]
154+
155+ # Represents a section (grouping, substep) of the build
78156class Section :
79157 def __init__ (self , name ):
80158 self .name = name
@@ -89,26 +167,19 @@ def log_build_params(conf: BuildConfiguration):
89167
90168def build_osc_dependencies (conf : BuildConfiguration ):
91169 with Section ("build osc dependencies" ):
92- # calculate cache variables
93- cache_variables = {
94- 'CMAKE_BUILD_TYPE' : conf .get_osc_deps_build_type (),
95- 'CMAKE_INSTALL_PREFIX' : conf .get_dependencies_install_dir (),
96- }
97- if conf .system_version :
98- cache_variables ['CMAKE_SYSTEM_VERSION' ] = conf .system_version
99-
100170 # configure
101171 _run_cmake_configure (
102172 source_dir = 'third_party' ,
103173 binary_dir = conf .get_dependencies_build_dir (),
104174 generator = conf .generator ,
105175 architecture = conf .architecture ,
106- cache_variables = cache_variables
176+ cache_variables = conf . get_dependencies_cmake_cache_variables ()
107177 )
108178 # build
109179 _run_cmake_build (
110180 binary_dir = conf .get_dependencies_build_dir (),
111- config = conf .get_osc_deps_build_type (),
181+ generator = conf .generator ,
182+ build_type = conf .get_osc_deps_build_type (),
112183 concurrency = conf .concurrency
113184 )
114185
@@ -118,54 +189,37 @@ def build_osc_dependencies(conf: BuildConfiguration):
118189
119190def build_osc (conf : BuildConfiguration ):
120191 if conf .skip_osc :
121- logging .info ("--skip-osc was provided : skipping the OSC build" )
192+ logging .info ("skip_osc was set : skipping the OSC build" )
122193 return
123194
124195 with Section ("build osc" ):
125- # calculate cache variables
126- cache_variables = {
127- 'CMAKE_BUILD_TYPE' : conf .get_osc_build_type (),
128- 'CMAKE_PREFIX_PATH' : os .path .abspath (conf .get_dependencies_install_dir ()),
129- 'OSC_BUILD_DOCS' : 'ON' if conf .build_docs else 'OFF'
130- }
131- if conf .system_version :
132- cache_variables ['CMAKE_SYSTEM_VERSION' ] = conf .system_version
133-
134196 # configure
135197 _run_cmake_configure (
136198 source_dir = '.' ,
137199 binary_dir = conf .get_osc_build_dir (),
138200 generator = conf .generator ,
139201 architecture = conf .architecture ,
140- cache_variables = cache_variables
202+ cache_variables = conf . get_osc_cmake_cache_variables ()
141203 )
142-
143204 # build
144205 _run_cmake_build (
145206 binary_dir = conf .get_osc_build_dir (),
146- config = conf .get_osc_build_type (),
207+ generator = conf .generator ,
208+ build_type = conf .get_osc_build_type (),
147209 concurrency = conf .concurrency
148210 )
149-
150211 # test
151- excluded_tests = [ # necessary in CI: no windowing system available
152- 'Graphics' ,
153- 'MeshDepthWritingMaterialFixture' ,
154- 'MeshNormalVectorsMaterialFixture' ,
155- 'ShaderTest' ,
156- 'MaterialTest' ,
157- 'RegisteredDemoTabsTest' ,
158- 'RegisteredOpenSimCreatorTabs' ,
159- 'AddComponentPopup' ,
160- 'LoadingTab' ,
161- ]
162- excluded_tests_str = '|' .join (excluded_tests )
163- _run (f'ctest --test-dir { conf .get_osc_build_dir ()} -j { conf .concurrency } -E { excluded_tests_str } ' )
164-
165- # ensure final target is built - even if it isn't in ALL (e.g. `package`)
212+ _run_ctest (
213+ test_dir = conf .get_osc_build_dir (),
214+ concurrency = conf .concurrency ,
215+ excluded_tests = conf .get_excluded_tests (),
216+ extra_env_vars = conf .get_osc_ctest_extra_environment_variables ()
217+ )
218+ # build final target (which might not be in ALL, e.g. `package`)
166219 _run_cmake_build (
167220 binary_dir = conf .get_osc_build_dir (),
168- config = conf .get_osc_build_type (),
221+ generator = conf .generator ,
222+ build_type = conf .get_osc_build_type (),
169223 concurrency = conf .concurrency ,
170224 target = conf .build_target
171225 )
@@ -184,6 +238,7 @@ def main():
184238 parser .add_argument ('--generator' , '-G' , help = 'set the build generator for cmake' , type = str , default = conf .generator )
185239 parser .add_argument ('--build-type' , help = 'the type of build to produce (CMake string: Debug, Release, RelWithDebInfo, etc.)' , type = str , default = conf .base_build_type )
186240 parser .add_argument ('--system-version' , help = 'specify the value of CMAKE_SYSTEM_VERSION (e.g. "10.0.26100.0", a specific Windows SDK)' , type = str , default = conf .system_version )
241+ parser .add_argument ('--codesign-enabled' , help = 'enable signing resulting binaries/package' , default = conf .codesign_enabled , action = 'store_true' )
187242
188243 # overwrite build configuration with any CLI args
189244 args = parser .parse_args ()
@@ -193,6 +248,7 @@ def main():
193248 conf .generator = args .generator
194249 conf .base_build_type = args .build_type
195250 conf .system_version = args .system_version
251+ conf .codesign_enabled = args .codesign_enabled
196252
197253 log_build_params (conf )
198254 build_osc_dependencies (conf )
0 commit comments