Files
buildcache/lua-examples/gcc_wrapper.lua
T

191 lines
6.3 KiB
Lua

-- match(.*(gcc|g\+\+|clang|clang\+\+).*)
-------------------------------------------------------------------------------
-- Copyright (c) 2018 Marcus Geelnard
--
-- This software is provided 'as-is', without any express or implied warranty.
-- In no event will the authors be held liable for any damages arising from the
-- use of this software.
--
-- Permission is granted to anyone to use this software for any purpose,
-- including commercial applications, and to alter it and redistribute it
-- freely, subject to the following restrictions:
--
-- 1. The origin of this software must not be misrepresented; you must not
-- claim that you wrote the original software. If you use this software in
-- a product, an acknowledgment in the product documentation would be
-- appreciated but is not required.
--
-- 2. Altered source versions must be plainly marked as such, and must not be
-- misrepresented as being the original software.
--
-- 3. This notice may not be removed or altered from any source distribution.
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- This is a re-implementation of the C++ class gcc_wrapper_t.
--
-- Note: The Lua and C++ implementations are not interchangeable (cache entries
-- produced by one of them will not produce a cache hit for the other). The
-- main purpose of this implementation is to serve as an example.
-------------------------------------------------------------------------------
require_std("io")
require_std("os")
require_std("string")
require_std("table")
require_std("bcache")
-------------------------------------------------------------------------------
-- Internal helper functions.
-------------------------------------------------------------------------------
local function make_preprocessor_cmd (args, preprocessed_file)
local preprocess_args = {}
-- Drop arguments that we do not want/need.
local drop_next_arg = false
for i, arg in ipairs(args) do
local drop_this_arg = drop_next_arg
drop_next_arg = false
if arg == "-c" then
drop_this_arg = true
elseif arg == "-o" then
drop_this_arg = true
drop_next_arg = true
end
if not drop_this_arg then
table.insert(preprocess_args, arg)
end
end
-- Append the required arguments for producing preprocessed output.
table.insert(preprocess_args, "-E")
table.insert(preprocess_args, "-P")
table.insert(preprocess_args, "-o")
table.insert(preprocess_args, preprocessed_file)
return preprocess_args
end
local function is_source_file (path)
local ext = bcache.get_extension(path):lower()
return (ext == ".cpp") or (ext == ".cc") or (ext == ".cxx") or (ext == ".c")
end
-------------------------------------------------------------------------------
-- Wrapper interface implementation.
-------------------------------------------------------------------------------
function get_capabilities ()
-- We can use hard links with GCC since it will never overwrite already
-- existing files.
return { "hard_links" }
end
function get_build_files ()
local files = {}
local found_object_file = false
for i = 2, #ARGS do
local next_idx = i + 1
if (ARGS[i] == "-o") and (next_idx <= #ARGS) then
if found_object_file then
error("Only a single target object file can be specified.")
end
files["object"] = ARGS[next_idx]
found_object_file = true
elseif (ARGS[i] == "-ftest-coverage") then
error("Code coverage data is currently not supported.")
end
end
if not found_object_file then
error("Unable to get the target object file.")
end
return files
end
function get_program_id ()
-- TODO(m): Add things like executable file size too.
-- Get the version string for the compiler.
local result = bcache.run({ARGS[1], "--version"})
if result.return_code ~= 0 then
error("Unable to get the compiler version information string.")
end
return result.std_out
end
function get_relevant_arguments ()
local filtered_args = {}
-- The first argument is the compiler binary without the path.
table.insert(filtered_args, bcache.get_file_part(ARGS[1]))
-- Note: We always skip the first arg since we have handled it already.
local skip_next_arg = true
for i, arg in ipairs(ARGS) do
if not skip_next_arg then
-- Does this argument specify a file (we don't want to hash those).
local is_arg_plus_file_name = (arg == "-I") or (arg == "-MF") or
(arg == "-MT") or (arg == "-MQ") or
(arg == "-o")
-- Generally unwanted argument (things that will not change how we go
-- from preprocessed code to binary object files)?
local first_two_chars = arg:sub(1, 2)
local is_unwanted_arg = (first_two_chars == "-I") or
(first_two_chars == "-D") or
(first_two_chars == "-M") or
is_source_file(arg)
if is_arg_plus_file_name then
skip_next_arg = true
elseif not is_unwanted_arg then
table.insert(filtered_args, arg)
end
else
skip_next_arg = false
end
end
return filtered_args
end
function preprocess_source ()
-- Check if this is a compilation command that we support.
local is_object_compilation = false
local has_object_output = false
for i, arg in ipairs(ARGS) do
if arg == "-c" then
is_object_compilation = true
elseif arg == "-o" then
has_object_output = true
elseif arg:sub(1, 1) == "@" then
error("Response files are currently not supported.")
end
end
if (not is_object_compilation) or (not has_object_output) then
error("Unsupported complation command.")
end
-- Run the preprocessor step.
local preprocessed_file = os.tmpname()
local preprocessor_args = make_preprocessor_cmd(ARGS, preprocessed_file)
local result = bcache.run(preprocessor_args)
if result.return_code ~= 0 then
os.remove(preprocessed_file)
error("Preprocessing command was unsuccessful.")
end
-- Read and return the preprocessed file.
local f = assert(io.open(preprocessed_file, "rb"))
local preprocessed_source = f:read("*all")
f:close()
os.remove(preprocessed_file)
return preprocessed_source
end