-
Notifications
You must be signed in to change notification settings - Fork 39
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Expose export-compile-commands in premake5 project.
Minor fix in Sys::getOSArchitecture.
- Loading branch information
Showing
7 changed files
with
279 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
## Generate compile_commands.json for premake projects | ||
|
||
This module implements [JSON Compilation Database Format | ||
Specification](http://clang.llvm.org/docs/JSONCompilationDatabase.html) for | ||
premake projects. | ||
|
||
Install this module somewhere premake can find it, for example: | ||
|
||
``` | ||
git clone https://github.com/tarruda/premake-export-compile-commands export-compile-commands | ||
``` | ||
|
||
Then put this at the top of your system script(eg: ~/.premake/premake-system.lua): | ||
|
||
```lua | ||
require "export-compile-commands" | ||
``` | ||
|
||
Note that while possible, it is not recommended to put the `require` line in | ||
project-specific premake configuration because the "export-compile-commands" | ||
module will need to be installed everywhere your project is built. | ||
|
||
After the above steps, the "export-compile-commands" action will be available | ||
for your projects: | ||
|
||
``` | ||
premake5 export-compile-commands | ||
``` | ||
|
||
The `export-compile-commands` action will generate one json file per | ||
config/platform combination in each workspace, all under the `compile_commands` | ||
subdirectory. For example, say you have defined `debug` and `release` | ||
configurations with `x32` and `x64` platforms, the output will be something | ||
like: | ||
|
||
``` | ||
Generated WORKSPACE_BUILD_DIR/compile_commands/debug_x32.json... | ||
Generated WORKSPACE_BUILD_DIR/compile_commands/debug_x64.json... | ||
Generated WORKSPACE_BUILD_DIR/compile_commands/release_x32.json... | ||
Generated WORKSPACE_BUILD_DIR/compile_commands/release_x64.json... | ||
``` | ||
|
||
where each file contain the compilation commands for the corresponding | ||
config/platform combo. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
return { | ||
'_preload.lua', | ||
'export-compile-commands.lua', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
return function(cfg) | ||
return _ACTION == 'export-compile-commands' | ||
end |
210 changes: 210 additions & 0 deletions
210
premake/export-compile-commands/export-compile-commands.lua
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
local p = premake | ||
|
||
p.modules.export_compile_commands = {} | ||
local m = p.modules.export_compile_commands | ||
|
||
local workspace = p.workspace | ||
local project = p.project | ||
|
||
local function esc(s) | ||
s = s:gsub('\\', '\\\\') | ||
s = s:gsub('"', '\\"') | ||
return s | ||
end | ||
|
||
local function esc_table(t) | ||
local res = {} | ||
for k, v in pairs(t) do | ||
table.insert(res, esc(v)) | ||
end | ||
return res | ||
end | ||
|
||
local function quote(s) | ||
return '"' .. esc(s) .. '"' | ||
end | ||
|
||
function m.getToolset(cfg) | ||
return p.tools[cfg.toolset or 'gcc'] | ||
end | ||
|
||
function m.getCommonFlags(prj, cfg) | ||
-- some tools that consumes compile_commands.json have problems with relative include paths | ||
relative = project.getrelative | ||
project.getrelative = function(prj, dir) return dir end | ||
|
||
local toolset = m.getToolset(cfg) | ||
local flags = toolset.getcppflags(cfg) | ||
flags = table.join(flags, toolset.getdefines(cfg.defines)) | ||
flags = table.join(flags, toolset.getundefines(cfg.undefines)) | ||
flags = table.join(flags, toolset.getincludedirs(cfg, cfg.includedirs, cfg.externalincludedirs)) | ||
flags = table.join(flags, toolset.getforceincludes(cfg)) | ||
if project.iscpp(prj) then | ||
flags = table.join(flags, toolset.getcxxflags(cfg)) | ||
elseif project.isc(prj) then | ||
flags = table.join(flags, toolset.getcflags(cfg)) | ||
end | ||
flags = table.join(flags, cfg.buildoptions) | ||
project.getrelative = relative | ||
return flags | ||
end | ||
|
||
function m.getObjectPath(prj, cfg, node) | ||
return path.join(cfg.objdir, path.appendExtension(node.objname, '.o')) | ||
end | ||
|
||
function m.getDependenciesPath(prj, cfg, node) | ||
return path.join(cfg.objdir, path.appendExtension(node.objname, '.d')) | ||
end | ||
|
||
function m.getFileFlags(prj, cfg, node) | ||
return table.join(m.getCommonFlags(prj, cfg), { | ||
'-o', quote(m.getObjectPath(prj, cfg, node)), | ||
'-MF', quote(m.getDependenciesPath(prj, cfg, node)), | ||
'-c', quote(node.abspath) | ||
}) | ||
end | ||
|
||
local function computesystemincludepaths(tool, iscfile) | ||
local cmd = tool .. " -E -v -fsyntax-only " .. (iscfile and '-x c' or '-x c++') .. ' "' .. _MAIN_SCRIPT .. '"' -- Use script as dummy "c" file | ||
local s,e = os.outputof(cmd, "both") | ||
if not s or not e then return {} end | ||
local s = string.match(s, "#include <...> search starts here:\n(.*)End of search list.\n") | ||
local includepaths = {} | ||
for w in string.gmatch(s, " *([^\n]+) *") do | ||
table.insert(includepaths, path.normalize(w)) | ||
end | ||
return includepaths | ||
end | ||
|
||
local systemincludepathscache = {} | ||
|
||
local function getsystemincludepaths(tool, iscfile) | ||
if not systemincludepathscache[tool] then systemincludepathscache[tool] = {} end | ||
local toolcache = systemincludepathscache[tool] | ||
if not toolcache[iscfile] then toolcache[iscfile] = computesystemincludepaths(tool, iscfile) end | ||
return toolcache[iscfile] | ||
end | ||
|
||
local function getsystemflags(tool, iscfile) | ||
return table.implode(getsystemincludepaths(tool, iscfile), ' -isystem \\"', '\\"', '') | ||
end | ||
|
||
local function getlanguageflags(cfg) | ||
local toolset = m.getToolset(cfg) | ||
local compileas = toolset.shared and toolset.shared.compileas | ||
if compileas then | ||
return compileas[cfg.language] or "" | ||
end | ||
end | ||
|
||
function m.generateCompileCommand(prj, cfg, node) | ||
local toolset = m.getToolset(cfg) | ||
local tool = path.iscfile(node.abspath) and 'cc' or 'cxx' | ||
cfg.gccprefix = cfg.gccprefix or "" -- hack to have gcc.gettoolname return non-nil | ||
local tool = toolset.gettoolname(cfg, tool) or tool | ||
local system_flag = getsystemflags(tool, path.iscfile(node.abspath)) | ||
local language_flag = getlanguageflags(cfg) .. " " | ||
return { | ||
directory = prj.location, | ||
file = node.abspath, | ||
command = (tool .. system_flag .. " " .. language_flag .. table.concat(esc_table(m.getFileFlags(prj, cfg, node)), ' ')) | ||
} | ||
end | ||
|
||
function m.includeFile(prj, node, depth) | ||
return path.iscppfile(node.abspath) or path.iscfile(node.abspath) or path.iscppheader(node.abspath) | ||
end | ||
|
||
function m.getProjectCommands(prj, cfg) | ||
local tr = project.getsourcetree(prj) | ||
local cmds = {} | ||
p.tree.traverse(tr, { | ||
onleaf = function(node, depth) | ||
if m.includeFile(prj, node, depth) then | ||
table.insert(cmds, m.generateCompileCommand(prj, cfg, node)) | ||
end | ||
end | ||
}) | ||
return cmds | ||
end | ||
|
||
local function get_architecture() | ||
if jit then | ||
return jit.arch | ||
end | ||
|
||
local handle = io.popen("uname -m 2>/dev/null") | ||
if handle then | ||
local arch = handle:read("*l") | ||
handle:close() | ||
if arch then return arch end | ||
end | ||
|
||
local arch = os.getenv("PROCESSOR_ARCHITECTURE") | ||
if arch then | ||
if arch == "AMD64" or arch == "IA64" then | ||
return "x86_64" | ||
end | ||
return string.lower(arch) | ||
end | ||
|
||
return "x86_64" | ||
end | ||
|
||
function m.onWorkspace(wks) | ||
local cfgCmds = {} | ||
local prjLocs = {} | ||
for prj in workspace.eachproject(wks) do | ||
for cfg in project.eachconfig(prj) do | ||
local cfgKey = string.format('%s', cfg.shortname) | ||
if not cfgCmds[cfgKey] then | ||
cfgCmds[cfgKey] = {} | ||
end | ||
cfgCmds[cfgKey] = table.join(cfgCmds[cfgKey], m.getProjectCommands(prj, cfg)) | ||
if not prjLocs[cfgKey] then | ||
prjLocs[cfgKey] = {} | ||
end | ||
prjLocs[cfgKey].location = prj.location | ||
end | ||
end | ||
for cfgKey,cmds in pairs(cfgCmds) do | ||
local outfile = string.format('%s/compile_commands.json', cfgKey) | ||
p.generate(wks, outfile, function(wks) | ||
p.push('[') | ||
for i = 1, #cmds do | ||
local item = cmds[i] | ||
p.push('{') | ||
p.x('"directory": "%s",', item.directory) | ||
p.x('"file": "%s",', item.file) | ||
p.w('"command": "%s"', item.command) | ||
if i ~= #cmds then | ||
p.pop('},') | ||
else | ||
p.pop('}') | ||
end | ||
end | ||
p.pop(']') | ||
end) | ||
|
||
if cfgKey == "release_" .. get_architecture() then | ||
local source = path.join(prjLocs[cfgKey].location, outfile) | ||
local destination = path.join(os.getcwd(), "compile_commands.json") | ||
os.copyfile(source, destination) | ||
end | ||
end | ||
end | ||
|
||
newaction { | ||
trigger = 'export-compile-commands', | ||
description = 'Export compiler commands in JSON Compilation Database Format', | ||
onWorkspace = m.onWorkspace, | ||
toolset = "clang", | ||
valid_kinds = { "ConsoleApp", "WindowedApp", "Makefile", "SharedLib", "StaticLib", "Utility" }, | ||
valid_languages = { "C", "C++" }, | ||
valid_tools = { | ||
cc = { "gcc", "clang" } | ||
} | ||
} | ||
|
||
return m |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters