premake/util/sdl_check_compile.lua
changeset 7925 f090a47eb7f7
child 8149 681eb46b8ac4
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/premake/util/sdl_check_compile.lua	Sun Nov 10 00:38:37 2013 -0500
     1.3 @@ -0,0 +1,307 @@
     1.4 +-- Copyright (C) 1997-2011 Sam Lantinga <slouken@libsdl.org>
     1.5 +--
     1.6 +-- This software is provided 'as-is', without any express or implied
     1.7 +-- warranty.  In no event will the authors be held liable for any damages
     1.8 +-- arising from the use of this software.
     1.9 +--
    1.10 +-- Permission is granted to anyone to use this software for any purpose,
    1.11 +-- including commercial applications, and to alter it and redistribute it
    1.12 +-- freely.
    1.13 +--
    1.14 +-- Meta-build system using premake created and maintained by
    1.15 +-- Benjamin Henning <b.henning@digipen.edu>
    1.16 +
    1.17 +--[[
    1.18 +sdl_check_compile.lua
    1.19 +
    1.20 +	This file provides various utility functions which allow the meta-build
    1.21 +	system to perform more complex dependency checking than premake initially
    1.22 +	allows. This is done using the (currently) GCC toolchain to build generated
    1.23 +	C files which try to import certain headers, link to certain functions, link
    1.24 +	to certain libraries, or a combination of the above. It supports providing a
    1.25 +	custom source to try and build, link, and/or run per the implementation's
    1.26 +	choice, so the possibilities are nearly endless with that this system is
    1.27 +	capable of, though it could always do with more flexibility.
    1.28 +]]
    1.29 +
    1.30 +
    1.31 +local cxx = "gcc"
    1.32 +local cxx_flags = ""
    1.33 +local cxx_io_flags = "-o premakecheck.o -c premakecheck.c 2> /dev/null"
    1.34 +local cxx_includes = { }
    1.35 +
    1.36 +local link = "gcc"
    1.37 +local link_flags = ""
    1.38 +local link_io_flags = "-o premakecheck.out premakecheck.o"
    1.39 +local link_end = " 2> /dev/null"
    1.40 +
    1.41 +local run = "./premakecheck.out"
    1.42 +local run_flags = ""
    1.43 +local run_io_flags = " > ./premakecheck.stdout"
    1.44 +
    1.45 +local checked_printf = false
    1.46 +local has_printf = false
    1.47 +
    1.48 +-- Set the application used to compile the generated files.
    1.49 +function set_cxx(compiler)
    1.50 +	cxx = compiler
    1.51 +end
    1.52 +
    1.53 +-- Set custom flags for the compiler.
    1.54 +function set_cxx_flags(flags)
    1.55 +	cxx_flags = flags
    1.56 +end
    1.57 +
    1.58 +-- Include a search directory for libraries.
    1.59 +local function include_library_dir(dir)
    1.60 +	link_flags = link_flags .. "-L" .. dir .. " "
    1.61 +end
    1.62 +
    1.63 +-- Include a library to be linked durnig the link step.
    1.64 +local function link_library(lib)
    1.65 +	link_flags = link_flags .. "-l" .. lib .. " "
    1.66 +end
    1.67 +
    1.68 +-- Reset the link flags.
    1.69 +local function reset_link_flags()
    1.70 +	link_flags = ""
    1.71 +end
    1.72 +
    1.73 +-- Creates the build command line to be executed.
    1.74 +local function build_compile_line()
    1.75 +	return cxx .. " " .. cxx_flags .. " " .. cxx_io_flags
    1.76 +end
    1.77 +
    1.78 +-- Creates the link command line to be executed.
    1.79 +local function build_link_line()
    1.80 +	return link .. " " .. link_io_flags .. " " .. link_flags .. link_end
    1.81 +end
    1.82 +
    1.83 +-- Create the run line to be executed.
    1.84 +local function build_run_line()
    1.85 +	return run .. " " .. run_flags .. " " .. run_io_flags
    1.86 +end
    1.87 +
    1.88 +-- Builds a list of preprocessor include directives for all the include files
    1.89 +-- successfully found so far by these functions, so as to perform automatic
    1.90 +-- feature checking for the clientside code.
    1.91 +local function build_includes()
    1.92 +	local includes = ""
    1.93 +	for _,v in ipairs(cxx_includes) do
    1.94 +		includes = includes .. '#include "' .. v .. '"\n'
    1.95 +	end
    1.96 +	return includes
    1.97 +end
    1.98 +
    1.99 +-- Cleanup the generated build environment.
   1.100 +local function cleanup_build()
   1.101 +	os.remove("./premakecheck.c")
   1.102 +	os.remove("./premakecheck.o")
   1.103 +	os.remove("./premakecheck.out")
   1.104 +	os.remove("./premakecheck.stdout")
   1.105 +end
   1.106 +
   1.107 +-- Check if a source builds, links, and or/runs, where running depends on
   1.108 +-- linking and linking depends on building. The return from this function is
   1.109 +-- a triple, where the first is a boolean value indicating if it successfully
   1.110 +-- was built, the second is a boolean value indicating if it successfully
   1.111 +-- linked, and the third represents nil if it was not run or run correctly, or
   1.112 +-- the output from the program executed (may be empty for no output).
   1.113 +local function check_build_source(source, link, run)
   1.114 +	local file = fileopen("./premakecheck.c", "wt")
   1.115 +	file:write(source)
   1.116 +	file:close()
   1.117 +	local result = os.execute(build_compile_line())
   1.118 +	if not link then
   1.119 +		cleanup_build()
   1.120 +		if result == 0 then
   1.121 +			return true, false, nil -- compile, no link, no run
   1.122 +		end
   1.123 +		return false, false, nil -- no compile, no link, no run
   1.124 +	end
   1.125 +	-- try linking, too
   1.126 +	if result ~= 0 then
   1.127 +		-- can't link if it doesn't compile
   1.128 +		cleanup_build()
   1.129 +		return false, false, nil -- no compile, no link, no run
   1.130 +	end
   1.131 +	result = os.execute(build_link_line())
   1.132 +	if not run or result ~= 0 then -- have to link to run
   1.133 +		cleanup_build()
   1.134 +		return true, result == 0, nil -- compile, maybe link, no run
   1.135 +	end
   1.136 +	result = os.execute(build_run_line())
   1.137 +	local output = readfile("./premakecheck.stdout", "rt")
   1.138 +	cleanup_build()
   1.139 +	return true, true, output -- compile, link, ran
   1.140 +end
   1.141 +
   1.142 +-- Given C source code, determine whether the source code will compile in the
   1.143 +-- present environment. Returns true if the source was successfully compiled, or
   1.144 +-- false if otherwise.
   1.145 +function check_cxx_source_compiles(source)
   1.146 +	local r1, _, __ = check_build_source(source, false, false)
   1.147 +	return r1
   1.148 +end
   1.149 +
   1.150 +-- Given C source code, determine whether the source code can be built into a
   1.151 +-- working executable. That is, it will check if the code both compiles and
   1.152 +-- links. Returns true if the code was successfully built (compiled and linked),
   1.153 +-- or false if otherwise.
   1.154 +function check_cxx_source_builds(source)
   1.155 +	local r1, r2, _ = check_build_source(source, true, false)
   1.156 +	return r1 and r2
   1.157 +end
   1.158 +
   1.159 +-- Given C source code, attempt to compile, link, and execute the source code.
   1.160 +-- This function will return two values. The first is a boolean indicating
   1.161 +-- whether the source code was successfully run (meaning it was compiled, built,
   1.162 +-- and ran successfully), and the second value returned is the actual output
   1.163 +-- from running the application, or nil if it did not run correctly or was not
   1.164 +-- built. The output may be an empty string if the code does not print anything
   1.165 +-- to stdout.
   1.166 +function check_cxx_source_runs(source)
   1.167 +	local r1, r2, r3 = check_build_source(source, true, true)
   1.168 +	return r1 and r2 and (r3 ~= nil), r3
   1.169 +end
   1.170 +
   1.171 +-- Given a header file, check whether the header file is visible to the compiler
   1.172 +-- in the given environment. Returns a boolean indicating thus. If a header file
   1.173 +-- is found in either of these functions, it will be added to a list of headers
   1.174 +-- that can be used in subsequent dependency checks.
   1.175 +function check_include_file(inc)
   1.176 +	return check_include_files(inc)
   1.177 +end
   1.178 +
   1.179 +-- Given a variable list of header files, check whether all of the includes are
   1.180 +-- visible in the given environment. Every file must be included in order for
   1.181 +-- this function to return true.
   1.182 +function check_include_files(...)
   1.183 +	local source = ""
   1.184 +	for _, v in ipairs{...} do
   1.185 +		source = source .. '#include "' .. v .. '"\n'
   1.186 +	end
   1.187 +	local result = check_cxx_source_compiles(source)
   1.188 +	if result then
   1.189 +		for _, v in ipairs{...} do
   1.190 +			table.insert(cxx_includes, v)
   1.191 +		end
   1.192 +	end
   1.193 +	return result
   1.194 +end
   1.195 +
   1.196 +-- Given a directory, determine whether the directory contains any header files.
   1.197 +-- Unfortunately it does assume the extension is .h, but this can be altered in
   1.198 +-- future versions of this software. The function returns true if the directory
   1.199 +-- (or any of its subdirectories) contain .h files, or false if otherwise (such
   1.200 +-- as if the directory does not exist).
   1.201 +function check_include_directory(incDir)
   1.202 +	incDir = incDir:gsub("\\", "/"):gsub("//", "/")
   1.203 +	if incDir:sub(#incDir, #incDir) ~= "/" then
   1.204 +		incDir = incDir .. "/"
   1.205 +	end
   1.206 +	return #os.matchfiles(incDir .. "**.h") > 0
   1.207 +end
   1.208 +
   1.209 +-- Given a variable list of directories, iteratively check if each one contains
   1.210 +-- header files, per the functionality of check_include_directory. This function
   1.211 +-- returns true if and only if every listed directory or its subdirectories
   1.212 +-- contain .h files.
   1.213 +function check_include_directories(...)
   1.214 +	for _, v in ipairs{...} do
   1.215 +		if not check_include_directory(v) then
   1.216 +			return false
   1.217 +		end
   1.218 +	end
   1.219 +	return true
   1.220 +end
   1.221 +
   1.222 +-- Given a function name, attempt to determine whether the function can be found
   1.223 +-- within all of the known include files. Known include files are derived from
   1.224 +-- the check_include_file(s) functions.
   1.225 +function check_function_exists(func)
   1.226 +	local source = build_includes()
   1.227 +	source = source .. 'int main(int argc, char **argv) {\n'
   1.228 +	source = source .. '\tvoid *check = (void *) ' .. func .. ';\n'
   1.229 +	source = source .. '\treturn 0;\n'
   1.230 +	return check_cxx_source_builds(source .. '}')
   1.231 +end
   1.232 +
   1.233 +-- Given a library, a function that must exist within the library, and an
   1.234 +-- include file prototyping the function, this function determines whether those
   1.235 +-- three variables are able to build a working executable. That is, if a
   1.236 +-- function can be properly linked to using a given library, then the library
   1.237 +-- can be assumed to exist. Returns true if and only if the function was
   1.238 +-- correctly linked to.
   1.239 +function check_library_exists(lib, func, inc)
   1.240 +	local source = build_includes()
   1.241 +	if inc ~= nil then
   1.242 +		source = source .. '#include "' .. inc .. '"\n'
   1.243 +	end
   1.244 +	source = source .. 'int main(int argc, char **argv) {\n'
   1.245 +	source = source .. '\tvoid *check = (void *) ' .. func .. ';\n'
   1.246 +	source = source .. '\treturn 0;\n'
   1.247 +	if lib ~= nil then
   1.248 +		link_library(lib)
   1.249 +	end
   1.250 +	local result = check_cxx_source_builds(source .. '}')
   1.251 +	reset_link_flags()
   1.252 +	return result
   1.253 +end
   1.254 +
   1.255 +-- This is a merge variable list version of the check_library_exists function.
   1.256 +-- The thing to note with this function is that it will return true for the
   1.257 +-- first library found to correctly link to the function. This function is used
   1.258 +-- to determine whether the function is found in a list of libraries, not if it
   1.259 +-- is found in every one of the libraries.
   1.260 +function check_library_exists_multiple(func, inc, ...)
   1.261 +	for _,v in ipairs{...} do
   1.262 +		if check_library_exists(v, func, inc) then
   1.263 +			return true
   1.264 +		end
   1.265 +	end
   1.266 +	return false
   1.267 +end
   1.268 +
   1.269 +-- This is a wrapper for the check_library_exists function that will also
   1.270 +-- attempt to locate the library in question, in case it's not in a path the
   1.271 +-- compiler is already aware of. This function has the same return consequences
   1.272 +-- as check_library_exists.
   1.273 +function check_library_exists_lookup(lib, func, inc)
   1.274 +	local dir = os.findlib(lib)
   1.275 +	if dir == nil then
   1.276 +		return false
   1.277 +	end
   1.278 +	include_library_dir(dir)
   1.279 +	return check_library_exists(lib, func, inc)
   1.280 +end
   1.281 +
   1.282 +-- Given a valid C type name, this function generates a program that will print
   1.283 +-- the size of the type using the sizeof operator to the console, then parse the
   1.284 +-- size to indicate the byte size of the type on this platform. The resulting
   1.285 +-- executable is dependent on stdio and the printf function, which it safely
   1.286 +-- checks for behind the scenes. If these dependencies are not found for
   1.287 +-- whatever reason, this function returns 0, otherwise it returns a proper
   1.288 +-- numerical value representing the size of the specified type.
   1.289 +function check_type_size(typename)
   1.290 +	if not checked_printf then
   1.291 +		checked_printf = true
   1.292 +		has_printf = check_include_file("stdio.h") and check_function_exists("printf")
   1.293 +		if not has_printf then
   1.294 +			print("Warning: cannot check the size of a type without stdio and printf.")
   1.295 +		end
   1.296 +	end
   1.297 +	if not has_printf then
   1.298 +		return 0
   1.299 +	end
   1.300 +	local source = '#include "stdio.h"\n'
   1.301 +	source = source .. 'int main(int argc, char **argv) {\n'
   1.302 +	source = source .. '\tprintf("%d", sizeof(' .. typename .. '));\n'
   1.303 +	source = source .. '\treturn 0;\n'
   1.304 +	local success, result = check_cxx_source_runs(source .. '}');
   1.305 +	if not success then
   1.306 +		print("Warning: could not get the size of type: " .. typename)
   1.307 +		return 0
   1.308 +	end
   1.309 +	return tonumber(result)
   1.310 +end
   1.311 \ No newline at end of file