premake/util/sdl_check_compile.lua
author Sam Lantinga <slouken@libsdl.org>
Sun, 02 Feb 2014 00:53:27 -0800
changeset 8149 681eb46b8ac4
parent 7925 f090a47eb7f7
child 9619 b94b6d0bff0f
permissions -rwxr-xr-x
Fixed bug 2374 - Update copyright for 2014...

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