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