• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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