sdl_check_compile.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. -- Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
  2. --
  3. -- This software is provided 'as-is', without any express or implied
  4. -- warranty. In no event will the authors be held liable for any damages
  5. -- arising from the use of this software.
  6. --
  7. -- Permission is granted to anyone to use this software for any purpose,
  8. -- including commercial applications, and to alter it and redistribute it
  9. -- freely.
  10. --
  11. -- Meta-build system using premake created and maintained by
  12. -- Benjamin Henning <b.henning@digipen.edu>
  13. --[[
  14. sdl_check_compile.lua
  15. This file provides various utility functions which allow the meta-build
  16. system to perform more complex dependency checking than premake initially
  17. allows. This is done using the (currently) GCC toolchain to build generated
  18. C files which try to import certain headers, link to certain functions, link
  19. to certain libraries, or a combination of the above. It supports providing a
  20. custom source to try and build, link, and/or run per the implementation's
  21. choice, so the possibilities are nearly endless with that this system is
  22. capable of, though it could always do with more flexibility.
  23. ]]
  24. local cxx = "gcc"
  25. local cxx_flags = ""
  26. local cxx_io_flags = "-o premakecheck.o -c premakecheck.c 2> /dev/null"
  27. local cxx_includes = { }
  28. local link = "gcc"
  29. local link_flags = ""
  30. local link_io_flags = "-o premakecheck.out premakecheck.o"
  31. local link_end = " 2> /dev/null"
  32. local run = "./premakecheck.out"
  33. local run_flags = ""
  34. local run_io_flags = " > ./premakecheck.stdout"
  35. local checked_printf = false
  36. local has_printf = false
  37. -- Set the application used to compile the generated files.
  38. function set_cxx(compiler)
  39. cxx = compiler
  40. end
  41. -- Set custom flags for the compiler.
  42. function set_cxx_flags(flags)
  43. cxx_flags = flags
  44. end
  45. -- Include a search directory for libraries.
  46. local function include_library_dir(dir)
  47. link_flags = link_flags .. "-L" .. dir .. " "
  48. end
  49. -- Include a library to be linked durnig the link step.
  50. local function link_library(lib)
  51. link_flags = link_flags .. "-l" .. lib .. " "
  52. end
  53. -- Reset the link flags.
  54. local function reset_link_flags()
  55. link_flags = ""
  56. end
  57. -- Creates the build command line to be executed.
  58. local function build_compile_line()
  59. return cxx .. " " .. cxx_flags .. " " .. cxx_io_flags
  60. end
  61. -- Creates the link command line to be executed.
  62. local function build_link_line()
  63. return link .. " " .. link_io_flags .. " " .. link_flags .. link_end
  64. end
  65. -- Create the run line to be executed.
  66. local function build_run_line()
  67. return run .. " " .. run_flags .. " " .. run_io_flags
  68. end
  69. -- Builds a list of preprocessor include directives for all the include files
  70. -- successfully found so far by these functions, so as to perform automatic
  71. -- feature checking for the clientside code.
  72. local function build_includes()
  73. local includes = ""
  74. for _,v in ipairs(cxx_includes) do
  75. includes = includes .. '#include "' .. v .. '"\n'
  76. end
  77. return includes
  78. end
  79. -- Cleanup the generated build environment.
  80. local function cleanup_build()
  81. os.remove("./premakecheck.c")
  82. os.remove("./premakecheck.o")
  83. os.remove("./premakecheck.out")
  84. os.remove("./premakecheck.stdout")
  85. end
  86. local function os_execute(cmd)
  87. if _ENV then
  88. -- Lua 5.2 or greater
  89. local cmdSuccess, textStatus, returnCode = os.execute(cmd)
  90. return returnCode
  91. else
  92. -- Lua 5.1 or lesser
  93. local returnCode = os.execute(cmd)
  94. return returnCode
  95. end
  96. end
  97. -- Check if a source builds, links, and or/runs, where running depends on
  98. -- linking and linking depends on building. The return from this function is
  99. -- a triple, where the first is a boolean value indicating if it successfully
  100. -- was built, the second is a boolean value indicating if it successfully
  101. -- linked, and the third represents nil if it was not run or run correctly, or
  102. -- the output from the program executed (may be empty for no output).
  103. local function check_build_source(source, link, run)
  104. local file = fileopen("./premakecheck.c", "w")
  105. file:write(source)
  106. file:close()
  107. local result = os_execute(build_compile_line())
  108. if not link then
  109. cleanup_build()
  110. if result == 0 then
  111. return true, false, nil -- compile, no link, no run
  112. end
  113. return false, false, nil -- no compile, no link, no run
  114. end
  115. -- try linking, too
  116. if result ~= 0 then
  117. -- can't link if it doesn't compile
  118. cleanup_build()
  119. return false, false, nil -- no compile, no link, no run
  120. end
  121. result = os_execute(build_link_line())
  122. if not run or result ~= 0 then -- have to link to run
  123. cleanup_build()
  124. return true, result == 0, nil -- compile, maybe link, no run
  125. end
  126. result = os_execute(build_run_line())
  127. local output = readfile("./premakecheck.stdout", "r")
  128. cleanup_build()
  129. return true, true, output -- compile, link, ran
  130. end
  131. -- Given C source code, determine whether the source code will compile in the
  132. -- present environment. Returns true if the source was successfully compiled, or
  133. -- false if otherwise.
  134. function check_cxx_source_compiles(source)
  135. local r1, _, __ = check_build_source(source, false, false)
  136. return r1
  137. end
  138. -- Given C source code, determine whether the source code can be built into a
  139. -- working executable. That is, it will check if the code both compiles and
  140. -- links. Returns true if the code was successfully built (compiled and linked),
  141. -- or false if otherwise.
  142. function check_cxx_source_builds(source)
  143. local r1, r2, _ = check_build_source(source, true, false)
  144. return r1 and r2
  145. end
  146. -- Given C source code, attempt to compile, link, and execute the source code.
  147. -- This function will return two values. The first is a boolean indicating
  148. -- whether the source code was successfully run (meaning it was compiled, built,
  149. -- and ran successfully), and the second value returned is the actual output
  150. -- from running the application, or nil if it did not run correctly or was not
  151. -- built. The output may be an empty string if the code does not print anything
  152. -- to stdout.
  153. function check_cxx_source_runs(source)
  154. local r1, r2, r3 = check_build_source(source, true, true)
  155. return r1 and r2 and (r3 ~= nil), r3
  156. end
  157. -- Given a header file, check whether the header file is visible to the compiler
  158. -- in the given environment. Returns a boolean indicating thus. If a header file
  159. -- is found in either of these functions, it will be added to a list of headers
  160. -- that can be used in subsequent dependency checks.
  161. function check_include_file(inc)
  162. return check_include_files(inc)
  163. end
  164. -- Given a variable list of header files, check whether all of the includes are
  165. -- visible in the given environment. Every file must be included in order for
  166. -- this function to return true.
  167. function check_include_files(...)
  168. local source = ""
  169. for _, v in ipairs{...} do
  170. source = source .. '#include "' .. v .. '"\n'
  171. end
  172. local result = check_cxx_source_compiles(source)
  173. if result then
  174. for _, v in ipairs{...} do
  175. table.insert(cxx_includes, v)
  176. end
  177. end
  178. return result
  179. end
  180. -- Given a directory, determine whether the directory contains any header files.
  181. -- Unfortunately it does assume the extension is .h, but this can be altered in
  182. -- future versions of this software. The function returns true if the directory
  183. -- (or any of its subdirectories) contain .h files, or false if otherwise (such
  184. -- as if the directory does not exist).
  185. function check_include_directory(incDir)
  186. incDir = incDir:gsub("\\", "/"):gsub("//", "/")
  187. if incDir:sub(#incDir, #incDir) ~= "/" then
  188. incDir = incDir .. "/"
  189. end
  190. return #os.matchfiles(incDir .. "**.h") > 0
  191. end
  192. -- Given a variable list of directories, iteratively check if each one contains
  193. -- header files, per the functionality of check_include_directory. This function
  194. -- returns true if and only if every listed directory or its subdirectories
  195. -- contain .h files.
  196. function check_include_directories(...)
  197. for _, v in ipairs{...} do
  198. if not check_include_directory(v) then
  199. return false
  200. end
  201. end
  202. return true
  203. end
  204. -- Given a function name, attempt to determine whether the function can be found
  205. -- within all of the known include files. Known include files are derived from
  206. -- the check_include_file(s) functions.
  207. function check_function_exists(func)
  208. local source = build_includes()
  209. source = source .. 'int main(int argc, char **argv) {\n'
  210. source = source .. '\tvoid *check = (void *) ' .. func .. ';\n'
  211. source = source .. '\treturn 0;\n'
  212. return check_cxx_source_builds(source .. '}')
  213. end
  214. -- Given a library, a function that must exist within the library, and an
  215. -- include file prototyping the function, this function determines whether those
  216. -- three variables are able to build a working executable. That is, if a
  217. -- function can be properly linked to using a given library, then the library
  218. -- can be assumed to exist. Returns true if and only if the function was
  219. -- correctly linked to.
  220. function check_library_exists(lib, func, inc)
  221. local source = build_includes()
  222. if inc ~= nil then
  223. source = source .. '#include "' .. inc .. '"\n'
  224. end
  225. source = source .. 'int main(int argc, char **argv) {\n'
  226. source = source .. '\tvoid *check = (void *) ' .. func .. ';\n'
  227. source = source .. '\treturn 0;\n'
  228. if lib ~= nil then
  229. link_library(lib)
  230. end
  231. local result = check_cxx_source_builds(source .. '}')
  232. reset_link_flags()
  233. return result
  234. end
  235. -- This is a merge variable list version of the check_library_exists function.
  236. -- The thing to note with this function is that it will return true for the
  237. -- first library found to correctly link to the function. This function is used
  238. -- to determine whether the function is found in a list of libraries, not if it
  239. -- is found in every one of the libraries.
  240. function check_library_exists_multiple(func, inc, ...)
  241. for _,v in ipairs{...} do
  242. if check_library_exists(v, func, inc) then
  243. return true
  244. end
  245. end
  246. return false
  247. end
  248. -- This is a wrapper for the check_library_exists function that will also
  249. -- attempt to locate the library in question, in case it's not in a path the
  250. -- compiler is already aware of. This function has the same return consequences
  251. -- as check_library_exists.
  252. function check_library_exists_lookup(lib, func, inc)
  253. local dir = os.findlib(lib)
  254. if dir == nil then
  255. return false
  256. end
  257. include_library_dir(dir)
  258. return check_library_exists(lib, func, inc)
  259. end
  260. -- Given a valid C type name, this function generates a program that will print
  261. -- the size of the type using the sizeof operator to the console, then parse the
  262. -- size to indicate the byte size of the type on this platform. The resulting
  263. -- executable is dependent on stdio and the printf function, which it safely
  264. -- checks for behind the scenes. If these dependencies are not found for
  265. -- whatever reason, this function returns 0, otherwise it returns a proper
  266. -- numerical value representing the size of the specified type.
  267. function check_type_size(typename)
  268. if not checked_printf then
  269. checked_printf = true
  270. has_printf = check_include_file("stdio.h") and check_function_exists("printf")
  271. if not has_printf then
  272. print("Warning: cannot check the size of a type without stdio and printf.")
  273. end
  274. end
  275. if not has_printf then
  276. return 0
  277. end
  278. local source = '#include "stdio.h"\n'
  279. source = source .. 'int main(int argc, char **argv) {\n'
  280. source = source .. '\tprintf("%d", sizeof(' .. typename .. '));\n'
  281. source = source .. '\treturn 0;\n'
  282. local success, result = check_cxx_source_runs(source .. '}');
  283. if not success then
  284. print("Warning: could not get the size of type: " .. typename)
  285. return 0
  286. end
  287. return tonumber(result)
  288. end