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