build_openimageio.py 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986
  1. #!/usr/bin/env python3
  2. #
  3. # Copyright (c) Contributors to the Open 3D Engine Project.
  4. # For complete copyright and license terms please see the LICENSE at the root of this distribution.
  5. #
  6. # SPDX-License-Identifier: Apache-2.0 OR MIT
  7. #
  8. #
  9. '''
  10. Builds a version of OpenImageIO and OpenColorIO that include python bindings.
  11. The two libraries (OpenImageIO and OpenColorIO, aka oiio and ocio) are inter-dependent on each other
  12. so need to be built in a special sequence, some modules from each first, then some more modules from
  13. each after that.
  14. Notably, this script will build and fetch dependencies as needed, but is stricly limited to the
  15. supported set that works for Open 3D Engine.
  16. Notably, the following features are DISABLED and will not be present:
  17. - Camera RAW support
  18. - The image viewer application 'iv'
  19. - Jpeg2000 support
  20. - ffmpeg and any other potentially troublesome patent-laden components
  21. - OpenCV
  22. - WebP
  23. - OpenVDB
  24. - GIF
  25. - HEIV/AVIF
  26. - DICOM / DCMTK
  27. - Ptex
  28. - Field3D
  29. All of the above could get added support if necessary - it would involve updating the build
  30. script here to handle the dependent libs and also ensuring that their licenses are compatible and
  31. that there is no patent danger.
  32. '''
  33. import argparse
  34. import glob
  35. import json
  36. import os
  37. import platform
  38. import subprocess
  39. import sys
  40. import pathlib
  41. import shutil
  42. openimageio_repository_url = 'https://github.com/OpenImageIO/oiio.git'
  43. openimageio_repository_tag = 'v2.3.17.0'
  44. opencolorio_repository_url='https://github.com/AcademySoftwareFoundation/OpenColorIO.git'
  45. opencolorio_repository_tag='v2.1.1'
  46. boost_repository_url = 'https://github.com/boostorg/boost.git'
  47. boost_repository_tag = 'boost-1.76.0'
  48. libjpegturbo_repository_url ='https://github.com/libjpeg-turbo/libjpeg-turbo.git'
  49. libjpegturbo_repository_tag = '2.1.2'
  50. temp_folder_name = 'temp'
  51. source_folder_name = 'src'
  52. build_folder_name = 'bld'
  53. # the following are used for debugging and skip steps.
  54. # do not approve a pull request if they are not set to FALSE / None.
  55. SKIP_OPENCOLORIO = False
  56. SKIP_BOOST = False
  57. SKIP_LIBJPEGTURBO = False
  58. SKIP_OPENIMAGEIO = False
  59. SKIP_OPENCOLORIO_WITH_OPENIMAGEIO = False
  60. dependencies = {
  61. # dependencies format:
  62. # platformname :
  63. # package_short_name : (packagename, hashe)
  64. 'darwin' :
  65. {
  66. 'zlib' : ('zlib-1.2.11-rev5-mac', 'b6fea9c79b8bf106d4703b67fecaa133f832ad28696c2ceef45fb5f20013c096'),
  67. 'openexr' : ('OpenEXR-3.1.3-rev4-mac', '927b8ca6cc5815fa8ee4efe6ea2845487cba2540f7958d537692e7c9481a68fc'),
  68. 'python' : ('python-3.10.5-rev2-darwin', '46d7c74c64bf639279c53a68ff958d9955e01e08d293524958eb7ea7cac9c4c5'),
  69. 'tiff' : ('tiff-4.2.0.15-rev3-mac', 'c2615ccdadcc0e1d6c5ed61e5965c4d3a82193d206591b79b805c3b3ff35a4bf'),
  70. 'libpng' : ('png-1.6.37-rev2-mac', '515252226a6958c459f53d8598d80ec4f90df33d2f1637104fd1a636f4962f07'),
  71. 'expat' : ('expat-2.4.2-rev2-mac', '70f195977a17b08a4dc8687400fd7f2589e3b414d4961b562129166965b6f658'),
  72. 'freetype' : ('freetype-2.11.1-rev1-mac', 'b66107d3499f2e9c072bd88db26e0e5c1b8013128699393c6a8495afca3d2548')
  73. },
  74. 'windows' :
  75. {
  76. 'zlib' : ('zlib-1.2.11-rev5-windows', '8847112429744eb11d92c44026fc5fc53caa4a06709382b5f13978f3c26c4cbd'),
  77. 'openexr' : ('OpenEXR-3.1.3-rev5-windows', 'bff6dc78412bb1b04ded243753bee36e9229fdaf9a9e1fa85b1059238fba4c9b'),
  78. 'python' : ('python-3.10.5-rev1-windows', 'c012e7c8fd20e632446d2cd689a9472e4e4495da7534d484d0f1c63840222cbb'),
  79. 'tiff' : ('tiff-4.2.0.15-rev3-windows', 'c6000a906e6d2a0816b652e93dfbeab41c9ed73cdd5a613acd53e553d0510b60'),
  80. 'libpng' : ('png-1.6.37-rev2-windows', 'e16539a0fff26ac9ef80dd11ef0103eca91745519eacd41d41d96911c173589f'),
  81. 'expat' : ('expat-2.4.2-rev2-windows', '748d08f21f5339757059a7887e72b52d15e954c549245c638b0b05bd5961e307'),
  82. 'freetype' : ('freetype-2.11.1-rev1-windows', '861d059a5542cb8f58a5157f411eee2e78f69ac72e45117227ebe400efe49f61')
  83. },
  84. 'linux' :
  85. {
  86. 'zlib' : ('zlib-1.2.11-rev5-linux', '9be5ea85722fc27a8645a9c8a812669d107c68e6baa2ca0740872eaeb6a8b0fc'),
  87. 'openexr' : ('OpenEXR-3.1.3-rev4-linux', 'fcbac68cfb4e3b694580bc3741443e111aced5f08fde21a92e0c768e8803c7af'),
  88. 'python' : ('python-3.10.5-rev2-linux', 'eda1fdc9129fb70df2d63bd21d0876c83c4f7021864f22c85850f4a8ff8cf1bf'),
  89. 'tiff' : ('tiff-4.2.0.15-rev3-linux', '2377f48b2ebc2d1628d9f65186c881544c92891312abe478a20d10b85877409a'),
  90. 'libpng' : ('png-1.6.37-rev2-linux', '5c82945a1648905a5c4c5cee30dfb53a01618da1bf58d489610636c7ade5adf5'),
  91. 'expat' : ('expat-2.4.2-rev2-linux', '755369a919e744b9b3f835d1acc684f02e43987832ad4a1c0b6bbf884e6cd45b'),
  92. 'freetype' : ('freetype-2.11.1-rev1-linux', '28bbb850590507eff85154604787881ead6780e6eeee9e71ed09cd1d48d85983')
  93. },
  94. 'linux-aarch64' :
  95. {
  96. 'zlib' : ('zlib-1.2.11-rev5-linux-aarch64', 'ce9d1ed2883d77ffc69c7982c078595c1f89ca55ec19d89fe7e6beb05f774775'),
  97. 'openexr' : ('OpenEXR-3.1.3-rev4-linux-aarch64', 'c9a81050f0d550ab03d2f5801e2f67f9f02747c26f4b39647e9919278585ad6a'),
  98. 'python' : ('python-3.10.5-rev2-linux-aarch64', 'a02bfb612005af364872aac96e569cef1ad84ba65632d88d04b34a99d45b077c'),
  99. 'tiff' : ('tiff-4.2.0.15-rev3-linux-aarch64', '429461014b21a530dcad597c2d91072ae39d937a04b7bbbf5c34491c41767f7f'),
  100. 'libpng' : ('png-1.6.37-rev2-linux-aarch64', 'fcf646c1b1b4163000efdb56d7c8f086b6ce0a520da5c8d3ffce4e1329ae798a'),
  101. 'expat' : ('expat-2.4.2-rev2-linux-aarch64', '934a535c1492d11906789d7ddf105b1a530cf8d8fb126063ffde16c5caeb0179'),
  102. 'freetype' : ('freetype-2.11.1-rev1-linux-aarch64','b4e3069acdcdae2f977108679d0986fb57371b9a7d4a3a496ab16909feabcba6')
  103. }
  104. }
  105. script_folder = pathlib.Path(__file__).parent.absolute()
  106. temp_folder_path = script_folder / temp_folder_name
  107. build_folder_path = temp_folder_path / build_folder_name
  108. source_folder_path = temp_folder_path / source_folder_name
  109. repo_root_path = script_folder.parent.parent
  110. general_scripts_path = repo_root_path / 'Scripts' / 'extras'
  111. dependencies_folder_path = (temp_folder_path / 'dependencies').absolute().resolve()
  112. opencolorio_build_folder = build_folder_path / 'opencolorio_build'
  113. ocio_install_path = temp_folder_path / 'ocio_install'
  114. pystring_install_path = temp_folder_path / 'pystring_install'
  115. yamlcpp_install_path = temp_folder_path / 'yaml-cpp_install'
  116. boost_build_folder = build_folder_path / 'boost'
  117. boost_install_path = temp_folder_path / 'boost_install'
  118. openimageio_build_folder = build_folder_path / 'openimageio_build'
  119. oiio_install_path = temp_folder_path / 'oiio_install'
  120. libjpegturbo_install_path = temp_folder_path / 'libjpegturbo_install'
  121. test_script_folder = script_folder / 'test'
  122. sys.path.insert(1, str(general_scripts_path.absolute().resolve()) )
  123. from package_downloader import PackageDownloader
  124. def exec_and_exit_if_failed(invoke_params, cwd=script_folder, shell=False):
  125. # thin wrapper around subprocess.run,
  126. # includes output tracing and also allows non-str params to be entered (such as pathlib)
  127. # without issue. Otherwise things like str.join may fail
  128. invoke_params = [str(x) for x in invoke_params]
  129. cwd = str(cwd)
  130. friendly_args = ' '.join(invoke_params)
  131. print('Exec:')
  132. print(f' CMDLINE: {friendly_args}')
  133. print(f' CWD: {cwd}')
  134. print(f' SHELL: {shell}')
  135. print('Output:')
  136. if shell and args.platform != "windows": # for shell invocations on non-windows, join it all
  137. invoke_params = [friendly_args]
  138. result_value = subprocess.run(invoke_params, shell=shell, cwd=cwd)
  139. if result_value.returncode != 0:
  140. print(f"Exec: Failed with return code {result_value}")
  141. sys.exit(1)
  142. return result_value.returncode
  143. # only clones the repo if it doesn't exist. Otherwise cleans it.
  144. def clone_repo(url, tag, dest_folder):
  145. if pathlib.Path(dest_folder).exists():
  146. print(f"Not re-cloning {url} to {dest_folder} becuase it already exists (use --clean to clean fully)")
  147. exec_and_exit_if_failed(['git', 'fetch'], cwd=dest_folder)
  148. exec_and_exit_if_failed(['git', 'clean', '-f'], cwd=dest_folder)
  149. exec_and_exit_if_failed(['git', 'restore', '.'], cwd=dest_folder)
  150. exec_and_exit_if_failed(['git', 'reset', '--hard', tag], cwd=dest_folder)
  151. else:
  152. exec_and_exit_if_failed(['git', 'clone', '--depth=1', '--single-branch', '--depth=1', '--recursive',
  153. '-b', tag,
  154. url,
  155. dest_folder
  156. ])
  157. exec_and_exit_if_failed(['git', 'submodule', 'update', '--init', '--recursive'], cwd=dest_folder)
  158. def get_dependencies(deps):
  159. for dependency in deps:
  160. packagename, hash = deps[dependency]
  161. if not (dependencies_folder_path / packagename).exists():
  162. if not PackageDownloader.DownloadAndUnpackPackage(packagename, hash, str(dependencies_folder_path)):
  163. raise Exception("Failed to build!")
  164. else:
  165. print(f'{packagename} already in dependencies folder, skipping. Use --clean to refresh')
  166. def get_dependency_path(platform, depname):
  167. dependencies_package_name = dependencies[platform][depname][0]
  168. return (dependencies_folder_path / dependencies_package_name).absolute().resolve()
  169. # ------------------------------------ MAIN SCRIPT STARTS HERE ----------------------------------------------
  170. parser = argparse.ArgumentParser(description='Builds this package')
  171. parser.add_argument('--platform', default=platform.system().lower(), required=False, help=f'Platform to build (defaults to \"{platform.system().lower()}\")')
  172. parser.add_argument('--clean', action='store_true', required=False, help=f'Complete clean build, if true, will delete entire temp and refetch dependencies')
  173. if 'O3DE_PACKAGE_NAME' in os.environ:
  174. parser.add_argument('--package-name', default=os.environ["O3DE_PACKAGE_NAME"], help=f"Name of the package to build. Defaults to '{os.environ['O3DE_PACKAGE_NAME']}")
  175. else:
  176. parser.add_argument('--package-name', required=True, help=f'Name of the package to build.')
  177. args = parser.parse_args()
  178. if args.platform not in dependencies.keys():
  179. print(f"Platform {args.platform} not in the list of supported dependency platforms {dependencies.keys()}")
  180. sys.exit(1)
  181. # We build the python bindings for OpenImageIO and OpenColorIO against our
  182. # python-3.10.5 dependency, so if a different version of python runs this build
  183. # script, the test at the end which attempts to import the built python bindings
  184. # will fail, so we need to make sure the same version of python is running
  185. # this build script.
  186. expected_python_version = '3.10'
  187. if not sys.version.startswith(expected_python_version):
  188. print(f"Error: Build script needs to be run with python version {expected_python_version}, current version is {sys.version}")
  189. sys.exit(1)
  190. # Make sure the system has the nasm library installed before proceeding
  191. result = exec_and_exit_if_failed(['nasm', '-v'])
  192. if result != 0:
  193. print("Missing nasm install on system")
  194. if args.platform == "darwin":
  195. print("Please run: 'brew install nasm' and then run the build again")
  196. elif args.platform in ("linux", "linux-aarch64"):
  197. print("Please use your linux package manager to install the nasm package and then run the build again")
  198. else: # windows
  199. print("Please run: 'winget install nasm' or install from https://www.nasm.us/")
  200. print("NOTE: Neither install will add the folder to your PATH, so you will need to add to your PATH manually")
  201. # similar to CMAKE, we define these as blank or filled depending on platform.
  202. lib_prefix = ''
  203. if args.platform.lower() == 'windows':
  204. lib_suffix = '.lib'
  205. else:
  206. lib_suffix = '.a'
  207. lib_prefix = 'lib'
  208. # Determine our final package output path now since we
  209. # use the platform as a suffix
  210. final_package_image_root = temp_folder_path / f'package-{args.platform}'
  211. print(f"OpenImageIO / OpenColorIO Build Script")
  212. print(f"Script folder : {script_folder.relative_to(repo_root_path)}")
  213. print(f"Temp folder : {temp_folder_path.relative_to(repo_root_path)}")
  214. print(f"Build folder : {build_folder_path.relative_to(repo_root_path)}")
  215. print(f"Source folder : {source_folder_path.relative_to(repo_root_path)}")
  216. print(f"Dependencies : {dependencies_folder_path.relative_to(repo_root_path)}")
  217. print(f"Platform : {args.platform}")
  218. print(f"ocio install : {ocio_install_path}")
  219. print(f"boost install : {boost_install_path}")
  220. print(f"oiio install : {oiio_install_path}")
  221. print(f"jpg install : {libjpegturbo_install_path}")
  222. print(f"final package : {final_package_image_root}")
  223. print("\n---------------------------------- CLEANING ----------------------------------")
  224. if args.clean:
  225. if temp_folder_path.exists():
  226. print(f'\n--clean specified on command line - removing entire temp folder: {temp_folder_path}...')
  227. shutil.rmtree(str(temp_folder_path.resolve()), ignore_errors=True)
  228. else:
  229. print('\n--clean not specified on the command line.\nWill only clean build folders, not source or installs')
  230. os.makedirs(str(temp_folder_path), exist_ok=True)
  231. os.makedirs(str(build_folder_path), exist_ok=True)
  232. os.makedirs(str(source_folder_path), exist_ok=True)
  233. print("\n----------------------------- FETCH Dependencies -----------------------------")
  234. get_dependencies(dependencies[args.platform])
  235. # we can re-use this path string (semicolon seperated list of folders) for all finds.
  236. # use posix paths here so that the cmake configure doesn't get confused by different
  237. # path separators on windows
  238. module_path_string = ';'.join( [
  239. f'{get_dependency_path(args.platform, "openexr").as_posix()}',
  240. f'{get_dependency_path(args.platform, "expat").as_posix()}',
  241. f'{get_dependency_path(args.platform, "zlib").as_posix()}',
  242. f'{get_dependency_path(args.platform, "tiff").as_posix()}',
  243. f'{get_dependency_path(args.platform, "libpng").as_posix()}',
  244. f'{get_dependency_path(args.platform, "freetype").as_posix()}',
  245. # add a custom path for our custom find modules:
  246. ])
  247. # Our python dependency needs to be on the PATH on Windows,
  248. # or else one of the sub-dependencies (pystring) will fail
  249. # to find python even with the python build arguments
  250. # that we setup further down
  251. python_root = get_dependency_path(args.platform, "python")
  252. if args.platform == "windows":
  253. python_root /= "python"
  254. os.environ["PATH"] = f"{str(python_root.absolute().resolve())};{os.environ['PATH']}"
  255. # building opencolorIO is a function becuase we call it twice
  256. # once before we have OpenImageIO built, and once again with that dependency ready
  257. def BuildOpenColorIO(module_paths_to_use, release=True):
  258. build_type = 'Release' if release else 'Debug'
  259. # Only build the python bindings in Release
  260. build_python = 'ON' if release else 'OFF'
  261. opencolorio_configure_command = [
  262. 'cmake',
  263. f'-S',
  264. f'{source_folder_path / "opencolorio"}',
  265. f'-B',
  266. f'{opencolorio_build_folder}',
  267. f'-Dexpat_STATIC_LIBRARY=ON',
  268. f'-DCMAKE_INSTALL_PREFIX={ocio_install_path}',
  269. f'-DCMAKE_BUILD_TYPE={build_type}',
  270. f'-DBUILD_SHARED_LIBS=ON',
  271. f'-DCMAKE_CXX_STANDARD=17',
  272. f'-DOCIO_BUILD_APPS=ON',
  273. f'-DOCIO_BUILD_OPENFX=OFF',
  274. f'-DOCIO_BUILD_TESTS=OFF',
  275. f'-DOCIO_BUILD_GPU_TESTS=OFF',
  276. f'-DOCIO_BUILD_PYTHON={build_python}',
  277. f'-DCMAKE_CXX_VISIBILITY_PRESET=hidden',
  278. f'-DOCIO_BUILD_DOCS=OFF', # <---- TODO: we have to fix this maybe
  279. f'-DCMAKE_MODULE_PATH={module_paths_to_use}',
  280. ]
  281. # We want to use ninja on both darwin and linux
  282. if args.platform != "windows":
  283. opencolorio_configure_command += [
  284. '-G', 'Ninja'
  285. ]
  286. if args.platform == "darwin":
  287. opencolorio_configure_command += [
  288. f'-DCMAKE_TOOLCHAIN_FILE={repo_root_path / "Scripts/cmake/Platform/Mac/Toolchain_mac.cmake"}'
  289. ]
  290. elif args.platform == "windows":
  291. # Without this on windows we get a linker error: png_LIBRARY-NOTFOUND
  292. opencolorio_configure_command += [
  293. f'-Dpng_LIBRARY={get_dependency_path(args.platform, "libpng") / "png" / "lib" / "libpng16_static.lib"}'
  294. ]
  295. # Make sure our debug targets get a debug postfix, since by default
  296. # the OCIO build has the same output target names for release/debug
  297. debug_postfix = ""
  298. if not release:
  299. debug_postfix = "d"
  300. opencolorio_configure_command += [
  301. f'-DCMAKE_DEBUG_POSTFIX={debug_postfix}'
  302. ]
  303. # Add python-specific configure args
  304. python_root = get_dependency_path(args.platform, "python")
  305. if args.platform == "windows":
  306. python_root /= "python"
  307. python_lib = python_root / "libs" / "python310.lib"
  308. python_include = python_root / "include"
  309. python_exe = python_root / "python.exe"
  310. opencolorio_configure_command += [
  311. f'-DPython_LIBRARY={python_lib}',
  312. f'-DPython_INCLUDE_DIR={python_include}',
  313. f'-DPython_EXECUTABLE={python_exe}'
  314. ]
  315. elif args.platform == "darwin":
  316. python_root /= "Python.framework/Versions/3.10"
  317. python_exe = python_root / "bin/Python3"
  318. opencolorio_configure_command += [
  319. f'-DPython_ROOT={python_root}',
  320. f'-DPython_EXECUTABLE={python_exe}'
  321. ]
  322. else: # linux
  323. python_root /= "python"
  324. python_exe = python_root / "bin/python3"
  325. opencolorio_configure_command += [
  326. f'-DPython_ROOT={python_root}',
  327. f'-DPython_EXECUTABLE={python_exe}'
  328. ]
  329. exec_and_exit_if_failed(opencolorio_configure_command)
  330. opencolorio_build_command = [
  331. f'cmake',
  332. f'--build',
  333. f'{opencolorio_build_folder}',
  334. f'--parallel',
  335. f'--config',
  336. f'{build_type}',
  337. f'--target',
  338. f'install'
  339. ]
  340. exec_and_exit_if_failed(opencolorio_build_command)
  341. # opencolorio built statically only installs its own files, none of its dependencies
  342. # we need to actually also deploy its dependencies, namely
  343. # pystring::pystring
  344. # yaml-cpp (note, no namespace!)
  345. yaml_lib = "yaml-cpp"
  346. if args.platform == "windows":
  347. yaml_lib = "libyaml-cppmd"
  348. ocio_private_library_build_path = build_folder_path / 'opencolorio_build' / 'ext' / 'dist' / 'lib'
  349. ocio_private_libary_source_path = build_folder_path / 'opencolorio_build' / 'ext' / 'build'
  350. pystring_source_path = ocio_private_libary_source_path / 'pystring' / 'src' / 'pystring_install'
  351. yamlcpp_source_path = ocio_private_libary_source_path / 'yaml-cpp' / 'src' / 'yaml-cpp_install'
  352. os.makedirs(pystring_install_path / 'lib', exist_ok=True)
  353. os.makedirs(yamlcpp_install_path / 'lib', exist_ok=True)
  354. # The pystring lib ignores the CMAKE_DEBUG_POSTFIX, so the source will have the same name as the release,
  355. # but we need to differentiate when we copy it to the install path
  356. shutil.copy2(ocio_private_library_build_path / f'{lib_prefix}pystring{lib_suffix}', pystring_install_path / 'lib' / f'{lib_prefix}pystring{debug_postfix}{lib_suffix}', follow_symlinks=False)
  357. shutil.copy2(ocio_private_library_build_path / f'{lib_prefix}{yaml_lib}{debug_postfix}{lib_suffix}', yamlcpp_install_path / 'lib' / f'{lib_prefix}{yaml_lib}{debug_postfix}{lib_suffix}', follow_symlinks=False)
  358. shutil.copy2(pystring_source_path / 'LICENSE', pystring_install_path / 'LICENSE', follow_symlinks=False)
  359. shutil.copy2(yamlcpp_source_path / 'LICENSE', yamlcpp_install_path / 'LICENSE', follow_symlinks=False)
  360. if not SKIP_OPENCOLORIO:
  361. print("\n----------------------------- BUILD OpenColorIO ------------------------------")
  362. clone_repo(opencolorio_repository_url, opencolorio_repository_tag, source_folder_path / 'opencolorio')
  363. if opencolorio_build_folder.exists():
  364. shutil.rmtree(str(opencolorio_build_folder.resolve()), ignore_errors=True)
  365. # On windows only, we also need to do a debug build
  366. # We do the debug build before the release because the
  367. # executables use the same name so get overwritten, and
  368. # we want the release ones to ship
  369. if args.platform == "windows":
  370. print("\n----------------------------- BUILD OpenColorIO - Debug ------------------------------")
  371. BuildOpenColorIO(module_path_string, release=False)
  372. print("\n----------------------------- BUILD OpenColorIO - Release ------------------------------")
  373. BuildOpenColorIO(module_path_string)
  374. # the final install of OpenColorIO looks like this
  375. # (install folder)
  376. # - bin
  377. # (various programs)
  378. # - lib
  379. # - cmake
  380. # (the cmake files that define the target)
  381. # - pkgconfig
  382. # - python3.10/site-packages
  383. # - PyOpenColorIO.so
  384. # libOpenColorIO.a
  385. # libOpenColorIOoglapphelpers.a
  386. # libpystring.a
  387. # libyaml-cpp.a
  388. # now that we have openColorIO we can make openImageIO which uses it
  389. # then we can circle back into openColorIO and make any apps it was missing.
  390. def BuildBoost(release=True):
  391. # Use the right bootstrap script for windows/*nix
  392. if args.platform == "windows":
  393. bootstrap_script = "bootstrap.bat"
  394. build_b2 = "b2.exe"
  395. else:
  396. bootstrap_script = "./bootstrap.sh"
  397. build_b2 = "./b2"
  398. exec_and_exit_if_failed([bootstrap_script],
  399. cwd=source_folder_path / 'boost', shell=True)
  400. build_type = 'release' if release else 'debug'
  401. boost_build_command = [build_b2,
  402. f'--build-dir={boost_build_folder}',
  403. '--with-filesystem',
  404. '--with-atomic',
  405. '--with-thread',
  406. '--with-system',
  407. '--with-headers',
  408. '--with-date_time',
  409. '--with-chrono',
  410. f'link=static',
  411. f'threading=multi',
  412. f'--prefix={boost_install_path}',
  413. f'{build_type}',
  414. f'install',
  415. f'visibility=hidden',
  416. f'-j', '12']
  417. # on non-windows, make sure that the same visibility is set for building the library
  418. # as will be likely set for applications where it is used as a dependency.
  419. if args.platform.lower() != 'windows':
  420. print("(Using hidden visibility by default)")
  421. boost_build_command += ['cflags=-fPIC',
  422. 'cxxflags=-fvisibility=hidden',
  423. 'cxxflags=-fvisibility-inlines-hidden',
  424. 'cxxflags=-fPIC'
  425. ]
  426. exec_and_exit_if_failed(boost_build_command, cwd=source_folder_path / 'boost', shell=True)
  427. if not SKIP_BOOST:
  428. print("\n-------------------------------- BUILD BOOST ---------------------------------")
  429. clone_repo(boost_repository_url, boost_repository_tag, source_folder_path / 'boost')
  430. if boost_build_folder.exists():
  431. shutil.rmtree(str(boost_build_folder.resolve()), ignore_errors=True)
  432. BuildBoost()
  433. # On windows only, we also need to do a debug build
  434. if args.platform == "windows":
  435. print("\n-------------------------------- BUILD BOOST - Debug ---------------------------------")
  436. BuildBoost(release=False)
  437. # boost is now built, and lives in temp/boost_install (which contains the usual lib, include, etc)
  438. if not SKIP_LIBJPEGTURBO:
  439. print("\n---------------------------- BUILD libJPEGTurbo ------------------------------")
  440. clone_repo(libjpegturbo_repository_url, libjpegturbo_repository_tag, source_folder_path / 'libjpegturbo')
  441. libjpegturbo_build_path = build_folder_path / 'libjpegturbo_build'
  442. if libjpegturbo_build_path.exists():
  443. shutil.rmtree(str(libjpegturbo_build_path.resolve()), ignore_errors=True)
  444. libjpegturbo_configure_command = [
  445. 'cmake',
  446. f'-S',
  447. f'{source_folder_path / "libjpegturbo"}',
  448. f'-B',
  449. libjpegturbo_build_path,
  450. f'-DCMAKE_INSTALL_PREFIX={libjpegturbo_install_path}',
  451. f'-DCMAKE_BUILD_TYPE=Release',
  452. f'-DBUILD_SHARED_LIBS=OFF',
  453. f'-DENABLE_SHARED=OFF',
  454. f'-DWITH_JAVA=0',
  455. f'-DCMAKE_POSITION_INDEPENDENT_CODE=ON',
  456. f'-DCMAKE_CXX_STANDARD=17',
  457. f'-DPYTHON_VERSION={expected_python_version}',
  458. f'-DCMAKE_CXX_VISIBILITY_PRESET=hidden',
  459. f'-DCMAKE_MODULE_PATH={module_path_string}'
  460. ]
  461. # We want to use ninja on both darwin and linux
  462. if args.platform != "windows":
  463. libjpegturbo_configure_command += [
  464. '-G', 'Ninja'
  465. ]
  466. if args.platform == "darwin":
  467. libjpegturbo_configure_command += [
  468. f'-DCMAKE_TOOLCHAIN_FILE={repo_root_path / "Scripts/cmake/Platform/Mac/Toolchain_mac.cmake"}'
  469. ]
  470. exec_and_exit_if_failed(libjpegturbo_configure_command)
  471. libjpegturbo_build_command = [
  472. f'cmake',
  473. f'--build',
  474. libjpegturbo_build_path,
  475. f'--parallel',
  476. f'--config',
  477. f'Release',
  478. f'--target',
  479. f'install'
  480. ]
  481. exec_and_exit_if_failed(libjpegturbo_build_command)
  482. # add our custom find files here, not earlier - we only want to use these custom find files
  483. # in compiling OpenImageIO and etc.
  484. module_path_string_with_custom_find_files = module_path_string + f';{(script_folder / "custom_find_files").as_posix()}'
  485. def BuildOpenImageIO(release=True):
  486. build_type = 'Release' if release else 'Debug'
  487. # Only build the python bindings in Release
  488. build_python = 'ON' if release else 'OFF'
  489. openimageio_configure_command = [
  490. 'cmake',
  491. f'-S',
  492. f'{source_folder_path / "openimageio"}',
  493. f'-B',
  494. openimageio_build_folder,
  495. f'-DUSE_PYTHON={build_python}',
  496. f'-DBoost_ROOT={boost_install_path}',
  497. f'-Dpybind11_ROOT={temp_folder_path / "bld/opencolorio_build/ext/dist"}', #use pybind from the opencolorio build
  498. f'-DJPEG_ROOT={libjpegturbo_install_path}',
  499. f'-DJPEGTurbo_ROOT={libjpegturbo_install_path}',
  500. f'-DCMAKE_INSTALL_PREFIX={oiio_install_path}',
  501. f'-DPNG_ROOT={get_dependency_path(args.platform, "libpng") / "libpng"}',
  502. f'-DCMAKE_BUILD_TYPE={build_type}',
  503. f'-DBUILD_SHARED_LIBS=ON',
  504. f'-DCMAKE_CXX_STANDARD=17',
  505. f'-DPYTHON_VERSION={expected_python_version}',
  506. f'-DOIIO_BUILD_TESTS=OFF',
  507. f'-DBUILD_TESTING=OFF',
  508. f'-DLINKSTATIC=ON',
  509. f'-DCMAKE_CXX_VISIBILITY_PRESET=hidden',
  510. f'-DUSE_OpenGL=OFF',
  511. f'-DUSE_Qt5=OFF',
  512. f'-DUSE_BZip2=OFF',
  513. f'-DUSE_FFmpeg=OFF',
  514. f'-DUSE_Field3D=OFF',
  515. f'-DUSE_DCMTK=OFF',
  516. f'-DUSE_OpenJPEG=OFF',
  517. f'-DUSE_Libheif=OFF',
  518. f'-DUSE_Libsquish=OFF',
  519. f'-DUSE_Nuke=OFF',
  520. f'-DUSE_OpenCV=OFF',
  521. f'-DUSE_OpenVDB=OFF',
  522. f'-DUSE_Ptex=OFF',
  523. f'-DUSE_R3DSDK=OFF',
  524. f'-DUSE_WebP=OFF',
  525. f'-DUSE_TBB=OFF',
  526. f'-DCMAKE_MODULE_PATH={module_path_string_with_custom_find_files}',
  527. f'-DVERBOSE=ON' # reveals problems with library inclusion
  528. ]
  529. # Make sure our debug targets get a debug postfix, since by default
  530. # the OCIO build has the same output target names for release/debug
  531. debug_suffix = ""
  532. if not release:
  533. debug_suffix = "d"
  534. # We want to use ninja on both darwin and linux
  535. if args.platform != "windows":
  536. openimageio_configure_command += [
  537. '-G', 'Ninja'
  538. ]
  539. if args.platform == "darwin":
  540. # Make sure to use the mac toolchain
  541. # Also, we need to set the RPATH to use relative @loader_path, or
  542. # else the RPATH will contain absolute paths
  543. openimageio_configure_command += [
  544. f'-DCMAKE_TOOLCHAIN_FILE={repo_root_path / "Scripts/cmake/Platform/Mac/Toolchain_mac.cmake"}',
  545. f'-DCMAKE_INSTALL_RPATH=@loader_path;@loader_path/../..;@loader_path/lib',
  546. ]
  547. elif args.platform in ("linux", "linux-aarch64"):
  548. # We need to set the RPATH to use relative $ORIGIN, or
  549. # else the RPATH will contain absolute paths
  550. openimageio_configure_command += [
  551. f'-DCMAKE_INSTALL_RPATH=$ORIGIN;$ORIGIN/../..;$ORIGIN/lib',
  552. ]
  553. else: # windows
  554. # Without this on windows we get a linker error: yaml_cpp_LIBRARY-NOTFOUND
  555. openimageio_configure_command += [
  556. f'-Dyaml_cpp_LIBRARY={yamlcpp_install_path / "lib" / f"libyaml-cppmd{debug_suffix}.lib"}',
  557. f'-Dpystring_LIBRARY={pystring_install_path / "lib" / f"pystring{debug_suffix}.lib"}'
  558. ]
  559. # Add python-specific configure args
  560. python_root = get_dependency_path(args.platform, "python")
  561. if args.platform == "windows":
  562. python_root /= "python"
  563. python_lib = python_root / "libs" / "python310.lib"
  564. python_include = python_root / "include"
  565. python_exe = python_root / "python.exe"
  566. openimageio_configure_command += [
  567. f'-DPython_LIBRARY={python_lib}',
  568. f'-DPython_INCLUDE_DIR={python_include}',
  569. f'-DPython_EXECUTABLE={python_exe}'
  570. ]
  571. elif args.platform == "darwin":
  572. python_root /= "Python.framework/Versions/3.10"
  573. python_exe = python_root / "bin/Python3"
  574. openimageio_configure_command += [
  575. f'-DPython_ROOT={python_root}',
  576. f'-DPython_EXECUTABLE={python_exe}'
  577. ]
  578. else: # linux
  579. python_root /= "python"
  580. python_exe = python_root / "bin/python3"
  581. openimageio_configure_command += [
  582. f'-DPython_ROOT={python_root}',
  583. f'-DPython_EXECUTABLE={python_exe}'
  584. ]
  585. exec_and_exit_if_failed(openimageio_configure_command)
  586. openimageio_build_command = [
  587. f'cmake',
  588. f'--build',
  589. openimageio_build_folder,
  590. f'--parallel',
  591. f'--config',
  592. f'{build_type}',
  593. f'--target',
  594. f'install'
  595. ]
  596. exec_and_exit_if_failed(openimageio_build_command)
  597. if not SKIP_OPENIMAGEIO:
  598. print("\n----------------------------- BUILD OpenImageIO ------------------------------")
  599. oiio_source_path = source_folder_path / 'openimageio'
  600. clone_repo(openimageio_repository_url, openimageio_repository_tag, oiio_source_path)
  601. if openimageio_build_folder.exists():
  602. shutil.rmtree(str(openimageio_build_folder.resolve()), ignore_errors=True)
  603. # note that we have to clear the install folder for this to actually work as
  604. # otherwise it might try to add RPATHS to existing files.
  605. # We don't want to clear it on debug build though so we don't delete
  606. # the release install
  607. if oiio_install_path.exists():
  608. shutil.rmtree(oiio_install_path, ignore_errors=True)
  609. # openimageio looks for OpenColorIO in a way that is not compatible with generated configs.
  610. # remove its find file, allow it to just use the OpenColorIO_ROOT:
  611. os.remove(oiio_source_path / 'src' / 'cmake' / 'modules' / 'FindOpenColorIO.cmake' )
  612. # The OIIO cmake sets CMAKE_INSTALL_RPATH_USE_LINK_PATH to TRUE, which will
  613. # cause anything in the link path to be appended to the RPATH, which results
  614. # in absolute paths to boost and other dependencies that we dont' want in there
  615. # so we need to patch it to prevent that
  616. patch_file_path = script_folder / 'disable_rpath_use_link_path.patch'
  617. patch_cmd = ['git', 'apply', '--ignore-whitespace', str(patch_file_path.absolute())]
  618. exec_and_exit_if_failed(patch_cmd, cwd=oiio_source_path)
  619. # On Windows only, there is an issue using the boost stacktace module because it pulls in
  620. # the dbgeng.dll, which isn't backwards compatible with different versions of the Windows SDK.
  621. # And so unless the user is on the same Windows SDK version as the machine who built the package,
  622. # it could crash due to missing symbols. The OpenImageIO build doesn't have a build flag to disable
  623. # the stacktrace usage, it only looks to see if a the boost version is >= 106500 and always sets
  624. # OIIO_HAS_STACKTRACE to true, so we need to patch the source to prevent boost stacktrace from
  625. # being linked against.
  626. if args.platform == "windows":
  627. disable_stacktrace_patch_file_path = script_folder / 'disable_boost_stacktrace.patch'
  628. disable_stacktrace_patch_cmd = ['git', 'apply', '--ignore-whitespace', str(disable_stacktrace_patch_file_path.absolute())]
  629. exec_and_exit_if_failed(disable_stacktrace_patch_cmd, cwd=oiio_source_path)
  630. # On Linux only, we need to also patch to make sure the pthreads is linked
  631. # appropriately, otherwise specifically the testtex executable will fail to link
  632. elif args.platform in ("linux", "linux-aarch64"):
  633. pthread_patch_file_path = script_folder / 'linux_pthreads_fix.patch'
  634. pthread_patch_cmd = ['git', 'apply', '--ignore-whitespace', str(pthread_patch_file_path.absolute())]
  635. exec_and_exit_if_failed(pthread_patch_cmd, cwd=oiio_source_path)
  636. # On windows only, we also need to do a debug build
  637. # We do the debug build before the release because the
  638. # executables use the same name so get overwritten, and
  639. # we want the release ones to ship
  640. if args.platform == "windows":
  641. print("\n----------------------------- BUILD OpenImageIO - Debug ------------------------------")
  642. BuildOpenImageIO(release=False)
  643. print("\n----------------------------- BUILD OpenImageIO - Release ------------------------------")
  644. BuildOpenImageIO()
  645. # ----------------- BUILD OpenColorIO again but this time with OpenImageIO support ----------------
  646. if not SKIP_OPENCOLORIO_WITH_OPENIMAGEIO:
  647. print("\n------------------ BUILD OpenColorIO with OpenImageIO support ----------------")
  648. if opencolorio_build_folder.exists():
  649. shutil.rmtree(str(opencolorio_build_folder.resolve()), ignore_errors=True)
  650. # On windows only, we also need to do a debug build
  651. # We do the debug build before the release because the
  652. # executables use the same name so get overwritten, and
  653. # we want the release ones to ship
  654. if args.platform == "windows":
  655. print("\n------------------ BUILD OpenColorIO with OpenImageIO support - Debug ----------------")
  656. BuildOpenColorIO(module_path_string_with_custom_find_files, release=False)
  657. print("\n------------------ BUILD OpenColorIO with OpenImageIO support - Debug ----------------")
  658. BuildOpenColorIO(module_path_string_with_custom_find_files)
  659. # -------------------------------- Make final installation image --------------------------------------
  660. # note that we will have to include static libs for things like boost, other deps.
  661. # and make a FIND FILE that declares openimageio depending on opencolorio
  662. # as well as on the various 3p libs that it requires.
  663. print("\n------------------------- Create final package image -------------------------")
  664. private_deps_folder = final_package_image_root / 'privatedeps'
  665. print("Cleaning previous package folder...")
  666. shutil.rmtree(final_package_image_root, ignore_errors=True)
  667. os.makedirs(final_package_image_root, exist_ok=True)
  668. os.makedirs(private_deps_folder, exist_ok=True)
  669. print("Copying OpenImageIO")
  670. shutil.copytree(src=oiio_install_path, dst=final_package_image_root / 'OpenImageIO', symlinks=True)
  671. shutil.copy2(src=script_folder / 'distribution' / 'FindOpenImageIO.cmake', dst=final_package_image_root / 'FindOpenImageIO.cmake')
  672. print("Copying OpenColorIO")
  673. shutil.copytree(src=ocio_install_path, dst=final_package_image_root / 'OpenColorIO', symlinks=True)
  674. shutil.copy2(src=script_folder / 'distribution' / 'FindOpenColorIO.cmake', dst=final_package_image_root / 'FindOpenColorIO.cmake')
  675. print("Cleaning unnecessary/private files")
  676. # note that we delete the cmake and pkgconfig files since they contain absolute paths to the machine
  677. # that they were built on, and won't be useful anyway
  678. # Ignore errors when removing the pkgconfig folders since they won't be present on windows
  679. shutil.rmtree(path=final_package_image_root / 'OpenColorIO' / 'lib' / 'pkgconfig', ignore_errors=True)
  680. shutil.rmtree(path=final_package_image_root / 'OpenColorIO' / 'lib' / 'cmake')
  681. shutil.rmtree(path=final_package_image_root / 'OpenColorIO' / 'share')
  682. shutil.rmtree(path=final_package_image_root / 'OpenImageIO' / 'lib' / 'pkgconfig', ignore_errors=True)
  683. shutil.rmtree(path=final_package_image_root / 'OpenImageIO' / 'lib' / 'cmake')
  684. # Remove the fonts from OpenImageIO since they just bloat the package
  685. shutil.rmtree(path=final_package_image_root / 'OpenImageIO' / 'share' / 'fonts')
  686. # On Windows only, the OpenImageIO install includes several MSVC runtime dlls we don't want to ship:
  687. # concrt, msvc*, and vcruntime*
  688. # So look through the OpenImageIO package bin directory and remove any dlls that aren't
  689. # explicitly from the OpenImageIO library itself
  690. if args.platform == "windows":
  691. oiio_dlls = final_package_image_root / 'OpenImageIO' / 'bin' / '*.dll'
  692. for file_path in glob.glob(oiio_dlls.as_posix()):
  693. file_name = pathlib.Path(file_path).name
  694. if not file_name.startswith('OpenImageIO'):
  695. os.remove(file_path)
  696. # Generate our PackageInfo.json dynamically for the platform, and pretty
  697. # print the JSON so that it's human readable
  698. print("Generating PackageInfo.json")
  699. package_name = args.package_name
  700. package_info = {
  701. "PackageName" : f"{package_name}",
  702. "URL" : "https://github.com/OpenImageIO/oiio and https://opencolorio.org/",
  703. "License" : "BSD-3-Clause",
  704. "LicenseFile" : "LICENSE.TXT"
  705. }
  706. package_info_file = open(final_package_image_root / 'PackageInfo.json', "w")
  707. pretty_json = json.dumps(package_info, indent=4)
  708. package_info_file.write(pretty_json)
  709. package_info_file.close()
  710. print("Copying License and package files")
  711. shutil.copy2(src=script_folder / 'distribution' / 'LICENSE.TXT', dst=final_package_image_root / 'LICENSE.TXT')
  712. # note that we're copying the distribution license, ie, the one that goes with the package, not the
  713. # license thats in THIS repo, to the root of the built package.
  714. # we also have to include other license files when the install step for the package doesn't do it themselves
  715. shutil.copy2(src=source_folder_path / 'opencolorio' / 'LICENSE', dst=final_package_image_root / 'OpenColorIO' / 'LICENSE')
  716. shutil.copy2(src=source_folder_path / 'opencolorio' / 'THIRD-PARTY.md', dst=final_package_image_root / 'OpenColorIO' / 'THIRD-PARTY.md')
  717. os.makedirs(private_deps_folder / 'Boost', exist_ok=True)
  718. shutil.copy2(src=source_folder_path / 'boost' / 'LICENSE_1_0.txt', dst=private_deps_folder / 'Boost')
  719. shutil.copy2(src=source_folder_path / 'boost' / 'README.md', dst=private_deps_folder / 'Boost')
  720. os.makedirs(private_deps_folder / 'LibJPEGTurbo', exist_ok=True)
  721. shutil.copy2(src=libjpegturbo_install_path / 'share' / 'doc' / 'libjpeg-turbo' / 'LICENSE.md', dst=private_deps_folder / 'LibJPEGTurbo')
  722. os.makedirs(private_deps_folder / 'pystring', exist_ok=True)
  723. shutil.copy2(src=pystring_install_path / 'LICENSE', dst=private_deps_folder / 'pystring')
  724. os.makedirs(private_deps_folder / 'yaml-cpp', exist_ok=True)
  725. shutil.copy2(src=yamlcpp_install_path / 'LICENSE', dst=private_deps_folder / 'yaml-cpp')
  726. print("\n----------------------------- Test package image -----------------------------")
  727. if args.platform == 'darwin':
  728. shared_lib_suffix = '.dylib'
  729. elif args.platform == 'windows':
  730. shared_lib_suffix = '.dll'
  731. else: # linux
  732. shared_lib_suffix = '.so'
  733. test_shared_libs_dir = None
  734. def TestOpenImageIO(release=True):
  735. build_type = 'Release' if release else 'Debug'
  736. test_configure_command = [
  737. 'cmake',
  738. f'-S',
  739. f'{script_folder / "test"}',
  740. f'-B',
  741. test_build_folder,
  742. f'-DCMAKE_BUILD_TYPE={build_type}',
  743. f'-DCMAKE_CXX_STANDARD=17',
  744. f'-DCMAKE_CXX_VISIBILITY_PRESET=hidden',
  745. f'-DCMAKE_MODULE_PATH={module_path_string_with_package_folder}',
  746. ]
  747. # We want to use ninja on both darwin and linux
  748. if args.platform != "windows":
  749. test_configure_command += [
  750. '-G', 'Ninja'
  751. ]
  752. if args.platform == "darwin":
  753. test_configure_command += [
  754. f'-DCMAKE_TOOLCHAIN_FILE={repo_root_path / "Scripts/cmake/Platform/Mac/Toolchain_mac.cmake"}'
  755. ]
  756. exec_and_exit_if_failed(test_configure_command)
  757. test_build_command = [
  758. f'cmake',
  759. f'--build',
  760. test_build_folder,
  761. f'--parallel',
  762. f'--config',
  763. f'{build_type}',
  764. ]
  765. exec_and_exit_if_failed(test_build_command)
  766. test_executable_path = ''
  767. if args.platform == 'darwin':
  768. test_executable_path = test_build_folder / 'test_OpenImageIO.app' / 'Contents' / 'MacOS' / 'test_OpenImageIO'
  769. elif args.platform == 'windows':
  770. test_executable_path = test_build_folder / f'{build_type}' / 'test_OpenImageIO.exe'
  771. else: # linux
  772. test_executable_path = test_build_folder / 'test_OpenImageIO'
  773. test_exec_command = [
  774. test_executable_path
  775. ]
  776. # Manual copy of runtime dependencies (the OCIO/OIIO shared libs) to the
  777. # test executable folder so that the executable can run
  778. test_executable_dir = test_executable_path.parent
  779. ocio_debug = ''
  780. oiio_debug = ''
  781. if not release:
  782. ocio_debug = 'd'
  783. oiio_debug = '_d'
  784. # On Windows the shared libraries are in the 'bin' directory,
  785. # but on Linux/Darwin they're in the 'lib' directory
  786. if args.platform == 'windows':
  787. shared_lib_dir = 'bin'
  788. else:
  789. shared_lib_dir = 'lib'
  790. ocio_libs = final_package_image_root / 'OpenColorIO' / shared_lib_dir / f'*{shared_lib_suffix}*'
  791. oiio_libs = final_package_image_root / 'OpenImageIO' / shared_lib_dir / f'*{shared_lib_suffix}*'
  792. # Copy all of the OIIO and OCIO shared libs into the test directory to simulate
  793. # being copied as runtime dependencies
  794. for shared_lib in [ocio_libs, oiio_libs]:
  795. for file_path in glob.glob(shared_lib.as_posix()):
  796. shutil.copy2(src=file_path, dst=test_executable_dir, follow_symlinks=False)
  797. # For the release build only, we will re-use this test_executable_dir to
  798. # test the python bindings later since we've already copied the shared libs into it
  799. if release:
  800. global test_shared_libs_dir
  801. test_shared_libs_dir = test_executable_dir
  802. exec_and_exit_if_failed(test_exec_command, cwd=test_script_folder)
  803. module_path_string_with_package_folder = module_path_string + f';{final_package_image_root.as_posix()}'
  804. test_build_folder = build_folder_path / 'test_openimageio'
  805. if test_build_folder.exists():
  806. shutil.rmtree(str(test_build_folder.resolve()), ignore_errors=True)
  807. # Test the release build
  808. print("\n----------------------------- Test package image - Release -----------------------------")
  809. TestOpenImageIO()
  810. # Test the debug build (windows only)
  811. if args.platform == "windows":
  812. print("\n----------------------------- Test package image - Debug -----------------------------")
  813. TestOpenImageIO(release=False)
  814. # Test the OIIO and OCIO python libraries
  815. oiio_site_packages = final_package_image_root / 'OpenImageIO' / 'lib' / 'python3.10' / 'site-packages'
  816. if args.platform == 'windows':
  817. ocio_site_packages = final_package_image_root / 'OpenColorIO' / 'lib' / 'site-packages'
  818. else:
  819. ocio_site_packages = final_package_image_root / 'OpenColorIO' / 'lib' / 'python3.10' / 'site-packages'
  820. # Copy our site-packages pyd's for OIIO/OCIO into our test directory where
  821. # we've already copied all the OIIO/OCIO shared libraries to simulate
  822. # them being copied to the bin directory as runtime dependencies
  823. for site_packages_dir in [oiio_site_packages, ocio_site_packages]:
  824. for file_path in glob.glob((site_packages_dir / '*.*').as_posix()):
  825. shutil.copy2(src=file_path, dst=test_shared_libs_dir)
  826. # Insert our test scripts folder into the sys.path so that we can import the tests
  827. # As well as the test_shared_libs_dir that has the python bindings
  828. sys.path.insert(1, str(test_script_folder.absolute().resolve()))
  829. sys.path.insert(1, str(test_shared_libs_dir.absolute().resolve()))
  830. from python_tests import test_OpenImageIO, test_OpenColorIO
  831. if not test_OpenImageIO():
  832. print("OpenImageIO python test failed")
  833. exit(-1)
  834. if not test_OpenColorIO():
  835. print("OpenColorIO python test failed")
  836. exit(-1)
  837. print(f"Build and test complete! Folder image created in {final_package_image_root}")