conanfile.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. from conan import ConanFile
  2. from conan.tools.apple import is_apple_os
  3. from conan.tools.cmake import CMakeToolchain
  4. from conan.tools.files import save
  5. from conan.tools.microsoft import is_msvc
  6. from glob import glob
  7. import os
  8. required_conan_version = ">=2.13.0"
  9. class VCMI(ConanFile):
  10. settings = "os", "compiler", "build_type", "arch"
  11. generators = "CMakeDeps"
  12. _libRequires = [
  13. "boost/[^1.69]",
  14. "luajit/2.1.0-beta3",
  15. "minizip/[^1.2.12]",
  16. "zlib/[^1.2.12]",
  17. ]
  18. _clientRequires = [
  19. "onetbb/[^2021.7]",
  20. "sdl_image/[^2.8.2]",
  21. "sdl_mixer/[^2.8.0]",
  22. "sdl_ttf/[^2.0.18]",
  23. ]
  24. _launcherRequires = [
  25. "xz_utils/[^5.2.5]", # innoextract
  26. ]
  27. requires = _libRequires + _clientRequires + _launcherRequires
  28. options = {
  29. "with_ffmpeg": [True, False],
  30. }
  31. default_options = {
  32. "with_ffmpeg": True,
  33. "boost/*:shared": True,
  34. "bzip2/*:shared": True,
  35. "libiconv/*:shared": True,
  36. "libpng/*:shared": True,
  37. "minizip/*:shared": True,
  38. "ogg/*:shared": True,
  39. "opus/*:shared": True,
  40. "xz_utils/*:shared": True,
  41. "zlib/*:shared": True,
  42. }
  43. def configure(self):
  44. self.options["ffmpeg"].shared = is_msvc(self) # MSVC static build requires static runtime, but VCMI uses dynamic runtime
  45. # self.options["freetype"].shared = self.settings.os == "Android" # TODO https://github.com/conan-io/conan-center-index/issues/26020
  46. # static Qt for iOS is the only viable option at the moment
  47. self.options["qt"].shared = self.settings.os != "iOS"
  48. if self.settings.os == "Android":
  49. self.options["qt"].android_sdk = os.getenv("ANDROID_HOME") # , default=""
  50. # we need only the following Boost parts:
  51. # date_time filesystem iostreams locale program_options system
  52. # some other parts are also enabled because they're dependents
  53. # see e.g. conan-center-index/recipes/boost/all/dependencies
  54. self.options["boost"].without_context = True
  55. self.options["boost"].without_contract = True
  56. self.options["boost"].without_coroutine = True
  57. self.options["boost"].without_fiber = True
  58. self.options["boost"].without_graph = True
  59. self.options["boost"].without_graph_parallel = True
  60. self.options["boost"].without_json = True
  61. self.options["boost"].without_log = True
  62. self.options["boost"].without_math = True
  63. self.options["boost"].without_mpi = True
  64. self.options["boost"].without_nowide = True
  65. self.options["boost"].without_process = True
  66. self.options["boost"].without_python = True
  67. self.options["boost"].without_serialization = True
  68. self.options["boost"].without_stacktrace = True
  69. self.options["boost"].without_test = True
  70. self.options["boost"].without_timer = True
  71. self.options["boost"].without_type_erasure = True
  72. self.options["boost"].without_wave = True
  73. self.options["boost"].without_url = True
  74. self.options["ffmpeg"].disable_all_bitstream_filters = True
  75. self.options["ffmpeg"].disable_all_decoders = True
  76. self.options["ffmpeg"].disable_all_demuxers = True
  77. self.options["ffmpeg"].disable_all_encoders = True
  78. self.options["ffmpeg"].disable_all_filters = True
  79. self.options["ffmpeg"].disable_all_hardware_accelerators = True
  80. self.options["ffmpeg"].disable_all_muxers = True
  81. self.options["ffmpeg"].disable_all_parsers = True
  82. self.options["ffmpeg"].disable_all_protocols = True
  83. self.options["ffmpeg"].with_asm = False
  84. self.options["ffmpeg"].with_freetype = False
  85. self.options["ffmpeg"].with_libaom = False
  86. self.options["ffmpeg"].with_libdav1d = False
  87. self.options["ffmpeg"].with_libiconv = False
  88. self.options["ffmpeg"].with_libmp3lame = False
  89. self.options["ffmpeg"].with_libsvtav1 = False
  90. self.options["ffmpeg"].with_libvpx = False
  91. self.options["ffmpeg"].with_libwebp = False
  92. self.options["ffmpeg"].with_libx264 = False
  93. self.options["ffmpeg"].with_libx265 = False
  94. self.options["ffmpeg"].with_lzma = True
  95. self.options["ffmpeg"].with_openh264 = False
  96. self.options["ffmpeg"].with_openjpeg = False
  97. self.options["ffmpeg"].with_programs = False
  98. self.options["ffmpeg"].with_sdl = False
  99. self.options["ffmpeg"].with_ssl = False
  100. self.options["ffmpeg"].with_vorbis = False
  101. # option not available on Android
  102. if self.settings.os != "Android":
  103. self.options["ffmpeg"].with_libfdk_aac = False
  104. self.options["ffmpeg"].avcodec = True
  105. self.options["ffmpeg"].avdevice = False
  106. self.options["ffmpeg"].avfilter = False
  107. self.options["ffmpeg"].avformat = True
  108. self.options["ffmpeg"].postproc = False
  109. self.options["ffmpeg"].swresample = True # For resampling of audio in 'planar' formats
  110. self.options["ffmpeg"].swscale = True # For video scaling
  111. # We want following options supported:
  112. # H3:SoD - .bik and .smk
  113. # H3:HD - ogg container / theora video / vorbis sound (not supported by vcmi at the moment, but might be supported in future)
  114. # and for mods - webm container / vp8 or vp9 video / opus sound
  115. # TODO: add av1 support for mods (requires enabling libdav1d which currently fails to build via Conan)
  116. self.options["ffmpeg"].enable_protocols = "file"
  117. self.options["ffmpeg"].enable_demuxers = ",".join([
  118. "bink",
  119. "binka",
  120. "ogg",
  121. "smacker",
  122. "webm_dash_manifest",
  123. ])
  124. self.options["ffmpeg"].enable_parsers = ",".join([
  125. "opus",
  126. "vorbis",
  127. "vp8",
  128. "vp9",
  129. "webp",
  130. ])
  131. self.options["ffmpeg"].enable_decoders = ",".join([
  132. "bink",
  133. "binkaudio_dct",
  134. "binkaudio_rdft",
  135. "opus",
  136. "smackaud",
  137. "smacker",
  138. "theora",
  139. "vorbis",
  140. "vp8",
  141. "vp9",
  142. ])
  143. #optionally, for testing - enable ffplay/ffprobe binaries in conan package:
  144. #if self.settings.os == "Windows":
  145. # self.options["ffmpeg"].with_programs = True
  146. # self.options["ffmpeg"].avfilter = True
  147. # self.options["ffmpeg"].with_sdl = True
  148. # self.options["ffmpeg"].enable_filters = "aresample,scale"
  149. self.options["onetbb"].tbbbind = False
  150. self.options["onetbb"].tbbmalloc = False
  151. self.options["onetbb"].tbbproxy = False
  152. self.options["sdl"].iconv = True
  153. self.options["sdl"].sdl2main = self.settings.os != "iOS"
  154. self.options["sdl"].vulkan = False
  155. # bmp, png are the only ones that needs to be supported
  156. # dds support may be useful for HD edition, but not supported by sdl_image at the moment
  157. self.options["sdl_image"].gif = False
  158. self.options["sdl_image"].lbm = False
  159. self.options["sdl_image"].pnm = False
  160. self.options["sdl_image"].pcx = False
  161. self.options["sdl_image"].qoi = False
  162. self.options["sdl_image"].svg = False
  163. self.options["sdl_image"].tga = False
  164. self.options["sdl_image"].with_libjpeg = False
  165. self.options["sdl_image"].with_libtiff = False
  166. self.options["sdl_image"].with_libwebp = False
  167. self.options["sdl_image"].xcf = False
  168. self.options["sdl_image"].xpm = False
  169. self.options["sdl_image"].xv = False
  170. if is_apple_os(self):
  171. self.options["sdl_image"].imageio = True
  172. # mp3, ogg and wav are the only ones that needs to be supported, flac is a bonus
  173. self.options["sdl_mixer"].mad = False
  174. self.options["sdl_mixer"].mikmod = False
  175. self.options["sdl_mixer"].modplug = False
  176. self.options["sdl_mixer"].nativemidi = False
  177. self.options["sdl_mixer"].tinymidi = False
  178. # static on "single app" platforms
  179. isSdlShared = self.settings.os != "iOS" and self.settings.os != "Android"
  180. self.options["sdl"].shared = isSdlShared
  181. self.options["sdl_image"].shared = isSdlShared
  182. self.options["sdl_mixer"].shared = isSdlShared
  183. self.options["sdl_ttf"].shared = isSdlShared
  184. def _disableQtOptions(disableFlag, options):
  185. return " ".join([f"-{disableFlag}-{tool}" for tool in options])
  186. _qtOptions = [
  187. _disableQtOptions("no", [
  188. "gif",
  189. "ico",
  190. ]),
  191. _disableQtOptions("no-feature", [
  192. # xpm format is required for Drag'n'Drop support
  193. "imageformat_bmp",
  194. "imageformat_jpeg",
  195. "imageformat_ppm",
  196. "imageformat_xbm",
  197. # we need only win/macdeployqt
  198. # TODO: disabling these doesn't disable generation of CMake targets
  199. # TODO: in Qt 6.3 it's a part of qtbase
  200. # "assistant",
  201. # "designer",
  202. # "distancefieldgenerator",
  203. # "kmap2qmap",
  204. # "linguist",
  205. # "makeqpf",
  206. # "pixeltool",
  207. # "qdbus",
  208. # "qev",
  209. # "qtattributionsscanner",
  210. # "qtdiag",
  211. # "qtpaths",
  212. # "qtplugininfo",
  213. ]),
  214. ]
  215. self.options["qt"].config = " ".join(_qtOptions)
  216. self.options["qt"].essential_modules = False
  217. self.options["qt"].qttools = True
  218. self.options["qt"].qtandroidextras = self.settings.os == "Android" # TODO: in Qt 6 it's part of Core
  219. self.options["qt"].with_freetype = self.settings.os == "Android"
  220. self.options["qt"].with_libjpeg = False
  221. self.options["qt"].with_mysql = False
  222. self.options["qt"].with_odbc = False
  223. self.options["qt"].with_openal = False
  224. self.options["qt"].with_pq = False
  225. self.options["qt"].openssl = not is_apple_os(self)
  226. if self.settings.os == "iOS" or self.settings.os == "Android":
  227. self.options["qt"].opengl = "es2"
  228. # transitive deps
  229. # doesn't link to overridden bzip2 & zlib, the tool isn't needed anyway
  230. self.options["pcre2"].build_pcre2grep = False
  231. # executable not needed
  232. self.options["sqlite3"].build_executable = False
  233. # prevents pulling openssl in and isn't needed anyway
  234. self.options["opusfile"].http = False
  235. # programs not needed
  236. self.options["zstd"].build_programs = False
  237. def requirements(self):
  238. # client
  239. if self.options.with_ffmpeg:
  240. self.requires("ffmpeg/[>=4.4]")
  241. # On Android SDL version must be the same as the version of Java wrapper for SDL in VCMI source code
  242. # Wrapper can be found in the following directory: android/vcmi-app/src/main/java/org/libsdl/app
  243. # TODO: try enabling version range once there's no conflict
  244. # sdl_image & sdl_ttf depend on earlier version
  245. # ERROR: Version conflict: Conflict between sdl/2.28.5 and sdl/2.28.3 in the graph.
  246. # Conflict originates from sdl_mixer/2.8.0
  247. # upcoming SDL version 3.0+ is not supported at the moment due to API breakage
  248. # SDL versions between 2.22-2.26.1 have broken sound
  249. # self.requires("sdl/[^2.26.1 || >=2.0.20 <=2.22.0]")
  250. # versions before 2.30.7 don't build for Android with NDK 27: https://github.com/libsdl-org/SDL/issues/9792
  251. self.requires("sdl/2.30.9", override=True)
  252. # launcher
  253. if self.settings.os == "Android":
  254. self.requires("qt/[~5.15.14]") # earlier versions have serious bugs
  255. else:
  256. self.requires("qt/[~5.15.2]")
  257. def _pathForCmake(self, path):
  258. # CMake doesn't like \ in strings
  259. return path.replace(os.path.sep, os.path.altsep) if os.path.altsep else path
  260. def _generateRuntimeLibsFile(self):
  261. # create file with list of libs to copy to the package for distribution
  262. runtimeLibsFile = self._pathForCmake(os.path.join(self.build_folder, "_runtime_libs.txt"))
  263. runtimeLibExtension = {
  264. "Android": "so",
  265. "iOS": "dylib",
  266. "Macos": "dylib",
  267. "Windows": "dll",
  268. }.get(str(self.settings.os))
  269. runtimeLibs = []
  270. for _, dep in self.dependencies.host.items():
  271. # Qt libs are copied using *deployqt
  272. if dep.ref.name == "qt":
  273. continue
  274. runtimeLibDir = ''
  275. if self.settings.os == "Windows":
  276. if len(dep.cpp_info.bindirs) > 0:
  277. runtimeLibDir = dep.cpp_info.bindir
  278. elif len(dep.cpp_info.libdirs) > 0:
  279. runtimeLibDir = dep.cpp_info.libdir
  280. if len(runtimeLibDir) > 0:
  281. runtimeLibs += glob(os.path.join(runtimeLibDir, f"*.{runtimeLibExtension}"))
  282. save(self, runtimeLibsFile, "\n".join(runtimeLibs))
  283. return runtimeLibsFile
  284. def generate(self):
  285. tc = CMakeToolchain(self)
  286. tc.variables["USING_CONAN"] = True
  287. tc.variables["CONAN_RUNTIME_LIBS_FILE"] = self._generateRuntimeLibsFile()
  288. if self.settings.os == "Android":
  289. tc.variables["CMAKE_ANDROID_API"] = str(self.settings.os.api_level)
  290. elif self.settings.os == "Windows":
  291. tc.variables["CONAN_RUNENV_SCRIPT"] = self._pathForCmake(os.path.join(self.build_folder, "conanrun.bat"))
  292. tc.generate()